From: Stephen Boyd <sboyd@kernel.org>
To: Vinod Koul <vkoul@kernel.org>
Cc: linux-arm-msm@vger.kernel.org,
Bjorn Andersson <bjorn.andersson@linaro.org>,
Deepak Katragadda <dkatraga@codeaurora.org>,
Andy Gross <agross@kernel.org>,
David Brown <david.brown@linaro.org>,
Michael Turquette <mturquette@baylibre.com>,
linux-clk@vger.kernel.org, Taniya Das <tdas@codeaurora.org>,
Vinod Koul <vkoul@kernel.org>
Subject: Re: [PATCH 1/2] clk: qcom: clk-alpha-pll: Add support for Trion PLLs
Date: Fri, 07 Jun 2019 10:55:42 -0700 [thread overview]
Message-ID: <20190607175542.D9D56208C0@mail.kernel.org> (raw)
In-Reply-To: <20190607101234.30449-1-vkoul@kernel.org>
Quoting Vinod Koul (2019-06-07 03:12:33)
> From: Deepak Katragadda <dkatraga@codeaurora.org>
>
> Add programming sequence support for managing the Trion
> PLLs.
>
> Signed-off-by: Deepak Katragadda <dkatraga@codeaurora.org>
> Signed-off-by: Taniya Das <tdas@codeaurora.org>
> [vkoul: port to upstream and tidy-up]
This tag isn't very useful. Maybe you can list out what you actually did
instead of just "tidying"?
> diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
> index 0ced4a5a9a17..bf36a929458b 100644
> --- a/drivers/clk/qcom/clk-alpha-pll.c
> +++ b/drivers/clk/qcom/clk-alpha-pll.c
> @@ -120,6 +140,15 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
> #define FABIA_PLL_OUT_MASK 0x7
> #define FABIA_PLL_RATE_MARGIN 500
>
> +#define TRION_PLL_CAL_VAL 0x44
> +#define TRION_PLL_STANDBY 0x0
> +#define TRION_PLL_RUN 0x1
> +#define TRION_PLL_OUT_MASK 0x7
> +#define TRION_PCAL_DONE BIT(26)
> +#define TRION_PLL_RATE_MARGIN 500
These last two aren't used. Do we need them?
> +
> +#define XO_RATE 19200000
Please remove this define. It isn't used (thankfully!).
> +
> #define pll_alpha_width(p) \
> ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \
> ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH)
> @@ -392,6 +421,15 @@ alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a,
> u64 remainder;
> u64 quotient;
>
> + /*
> + * The PLLs parent rate is zero probably since the parent hasn't
> + * registered yet. Return early with the requested rate.
> + */
> + if (!prate) {
This hasn't been a problem before. Why is it a problem now?
> + pr_warn("PLLs parent rate hasn't been initialized.\n");
> + return rate;
> + }
> +
> quotient = rate;
> remainder = do_div(quotient, prate);
> *l = quotient;
> @@ -730,6 +768,136 @@ static long alpha_pll_huayra_round_rate(struct clk_hw *hw, unsigned long rate,
> return alpha_huayra_pll_round_rate(rate, *prate, &l, &a);
> }
>
> +static int trion_pll_is_enabled(struct clk_alpha_pll *pll,
> + struct regmap *regmap)
> +{
> + u32 mode_regval, opmode_regval;
> + int ret;
> +
> + ret = regmap_read(regmap, PLL_MODE(pll), &mode_regval);
> + ret |= regmap_read(regmap, PLL_OPMODE(pll), &opmode_regval);
> + if (ret)
> + return 0;
> +
> + return ((opmode_regval & TRION_PLL_RUN) && (mode_regval & PLL_OUTCTRL));
> +}
> +
> +static int clk_trion_pll_enable(struct clk_hw *hw)
> +{
> + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> + struct regmap *regmap = pll->clkr.regmap;
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(regmap, PLL_MODE(pll), &val);
> + if (ret)
> + return ret;
> +
> + /* If in FSM mode, just vote for it */
> + if (val & PLL_VOTE_FSM_ENA) {
> + ret = clk_enable_regmap(hw);
> + if (ret)
> + return ret;
> + return wait_for_pll_enable_active(pll);
> + }
> +
> + /* Set operation mode to RUN */
> + regmap_write(regmap, PLL_OPMODE(pll), TRION_PLL_RUN);
> +
> + ret = wait_for_pll_enable_lock(pll);
> + if (ret)
> + return ret;
> +
> + /* Enable the PLL outputs */
> + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll),
> + TRION_PLL_OUT_MASK, TRION_PLL_OUT_MASK);
> + if (ret)
> + return ret;
> +
> + /* Enable the global PLL outputs */
> + ret = regmap_update_bits(regmap, PLL_MODE(pll),
> + PLL_OUTCTRL, PLL_OUTCTRL);
> + if (ret)
> + return ret;
This if (ret) can be removed.
> +
> + /* Ensure that the write above goes through before returning. */
> + mb();
As far as I recall mb() does nothing to ensure the write above goes
through, just that writes after this one are ordered with the write
above.
> + return ret;
> +}
> +
> +static void clk_trion_pll_disable(struct clk_hw *hw)
> +{
> + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> + struct regmap *regmap = pll->clkr.regmap;
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(regmap, PLL_MODE(pll), &val);
> + if (ret)
> + return;
> +
> + /* If in FSM mode, just unvote it */
> + if (val & PLL_VOTE_FSM_ENA) {
> + clk_disable_regmap(hw);
> + return;
> + }
> +
> + /* Disable the global PLL output */
> + ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0);
> + if (ret)
> + return;
> +
> + /* Disable the PLL outputs */
> + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll),
> + TRION_PLL_OUT_MASK, 0);
> + if (ret)
> + return;
> +
> + /* Place the PLL mode in STANDBY */
> + regmap_write(regmap, PLL_OPMODE(pll), TRION_PLL_STANDBY);
> + regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N);
> +}
> +
> +static unsigned long
> +clk_trion_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> + struct regmap *regmap = pll->clkr.regmap;
> + u32 l, frac;
> + u64 prate = parent_rate;
> +
> + regmap_read(regmap, PLL_L_VAL(pll), &l);
> + regmap_read(regmap, PLL_ALPHA_VAL(pll), &frac);
> +
> + return alpha_pll_calc_rate(prate, l, frac, ALPHA_REG_16BIT_WIDTH);
> +}
> +
> +static int clk_trion_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> +
> + return trion_pll_is_enabled(pll, pll->clkr.regmap);
> +}
Can you move this function right below trion_pll_is_enabled()?
> +
> +static long clk_trion_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> + unsigned long min_freq, max_freq;
> + u32 l;
> + u64 a;
> +
> + rate = alpha_pll_round_rate(rate, *prate,
> + &l, &a, ALPHA_REG_16BIT_WIDTH);
> + if (!pll->vco_table || alpha_pll_find_vco(pll, rate))
> + return rate;
> +
> + min_freq = pll->vco_table[0].min_freq;
> + max_freq = pll->vco_table[pll->num_vco - 1].max_freq;
> +
> + return clamp(rate, min_freq, max_freq);
> +}
> +
> const struct clk_ops clk_alpha_pll_ops = {
> .enable = clk_alpha_pll_enable,
> .disable = clk_alpha_pll_disable,
> @@ -902,6 +1079,10 @@ static int alpha_pll_fabia_enable(struct clk_hw *hw)
> ret = regmap_read(regmap, PLL_OPMODE(pll), &opmode_val);
> if (ret)
> return ret;
> + ret = regmap_update_bits(regmap, PLL_MODE(pll),
> + PLL_BYPASSNL, PLL_BYPASSNL);
> + if (ret)
> + return ret;
What is this?
>
> /* Skip If PLL is already running */
> if ((opmode_val & FABIA_OPMODE_RUN) && (val & PLL_OUTCTRL))
> @@ -1058,6 +1239,91 @@ static unsigned long clk_alpha_pll_postdiv_fabia_recalc_rate(struct clk_hw *hw,
> return (parent_rate / div);
> }
>
> +static unsigned long
> +clk_trion_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
> + struct regmap *regmap = pll->clkr.regmap;
> + u32 i, div = 1, val;
> +
> + if (!pll->post_div_table) {
> + pr_err("Missing the post_div_table for the PLL\n");
Ahhh!
> + return -EINVAL;
> + }
> +
> + regmap_read(regmap, PLL_USER_CTL(pll), &val);
> +
> + val >>= pll->post_div_shift;
> + val &= PLL_POST_DIV_MASK(pll);
> +
> + for (i = 0; i < pll->num_post_div; i++) {
> + if (pll->post_div_table[i].val == val) {
> + div = pll->post_div_table[i].div;
> + break;
> + }
> + }
> +
> + return (parent_rate / div);
> +}
> +
> +static long
> +clk_trion_pll_postdiv_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
> +
> + if (!pll->post_div_table) {
> + pr_err("Missing the post_div_table for the PLL\n");
> + return -EINVAL;
Does this ever happen? I'd rather see this removed and the code blow up
if the driver author didn't test this.
> + }
> +
> + return divider_round_rate(hw, rate, prate, pll->post_div_table,
> + pll->width, CLK_DIVIDER_ROUND_CLOSEST);
> +};
> +
> +static int
> +clk_trion_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
> + struct regmap *regmap = pll->clkr.regmap;
> + int i, val = 0, div, ret;
> +
> + /*
> + * If the PLL is in FSM mode, then treat the set_rate callback
> + * as a no-operation.
> + */
> + ret = regmap_read(regmap, PLL_MODE(pll), &val);
> + if (ret)
> + return ret;
> +
> + if (val & PLL_VOTE_FSM_ENA)
> + return 0;
> +
> + if (!pll->post_div_table) {
> + pr_err("Missing the post_div_table for the PLL\n");
Again!
> + return -EINVAL;
> + }
> +
> + div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
Is the cast necessary?
> + for (i = 0; i < pll->num_post_div; i++) {
> + if (pll->post_div_table[i].div == div) {
> + val = pll->post_div_table[i].val;
> + break;
> + }
> + }
> + return regmap_update_bits(regmap, PLL_USER_CTL(pll),
> + PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT,
> + val << PLL_POST_DIV_SHIFT);
> +}
> +
> +const struct clk_ops clk_trion_pll_postdiv_ops = {
> + .recalc_rate = clk_trion_pll_postdiv_recalc_rate,
> + .round_rate = clk_trion_pll_postdiv_round_rate,
> + .set_rate = clk_trion_pll_postdiv_set_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_trion_pll_postdiv_ops);
> +
> static long clk_alpha_pll_postdiv_fabia_round_rate(struct clk_hw *hw,
> unsigned long rate, unsigned long *prate)
> {
next prev parent reply other threads:[~2019-06-07 17:55 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-07 10:12 [PATCH 1/2] clk: qcom: clk-alpha-pll: Add support for Trion PLLs Vinod Koul
2019-06-07 10:12 ` [PATCH 2/2] clk: qcom: gcc: Add global clock controller driver for SM8150 Vinod Koul
2019-06-07 17:43 ` Stephen Boyd
2019-06-07 17:43 ` Stephen Boyd
2019-06-08 9:15 ` Vinod Koul
2019-06-10 15:08 ` Stephen Boyd
2019-06-12 5:07 ` Vinod Koul
2019-07-09 1:56 ` Rob Herring
2019-06-07 17:55 ` Stephen Boyd [this message]
2019-06-08 9:14 ` [PATCH 1/2] clk: qcom: clk-alpha-pll: Add support for Trion PLLs Vinod Koul
2019-06-10 15:06 ` Stephen Boyd
2019-06-12 5:10 ` Vinod Koul
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190607175542.D9D56208C0@mail.kernel.org \
--to=sboyd@kernel.org \
--cc=agross@kernel.org \
--cc=bjorn.andersson@linaro.org \
--cc=david.brown@linaro.org \
--cc=dkatraga@codeaurora.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-clk@vger.kernel.org \
--cc=mturquette@baylibre.com \
--cc=tdas@codeaurora.org \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.