diff mbox series

[2/5] clk: qcom: clk-alpha-pll: Add support for Regera PLL ops

Message ID 20240229-camcc-support-sm8150-v1-2-8c28c6c87990@quicinc.com (mailing list archive)
State Superseded, archived
Headers show
Series clk: qcom: sm8150: Add camera clock controller support for SM8150 | expand

Commit Message

Satya Priya Kakitapalli (Temp) Feb. 29, 2024, 5:38 a.m. UTC
From: Taniya Das <quic_tdas@quicinc.com>

Regera PLL ops are required to control the Regera PLL from clock
controller drivers, thus add support for the same.

Signed-off-by: Taniya Das <quic_tdas@quicinc.com>
Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
---
 drivers/clk/qcom/clk-alpha-pll.c | 152 ++++++++++++++++++++++++++++++++++++++-
 drivers/clk/qcom/clk-alpha-pll.h |   5 ++
 2 files changed, 156 insertions(+), 1 deletion(-)

Comments

Konrad Dybcio March 1, 2024, 11:56 p.m. UTC | #1
On 29.02.2024 06:38, Satya Priya Kakitapalli wrote:
> From: Taniya Das <quic_tdas@quicinc.com>
> 
> Regera PLL ops are required to control the Regera PLL from clock
> controller drivers, thus add support for the same.
> 
> Signed-off-by: Taniya Das <quic_tdas@quicinc.com>
> Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
> ---

[...]


> +static int clk_regera_pll_enable(struct clk_hw *hw)

This function is 1:1 clk_zonda_pll_enable() logic-wise, except for
the `if (val & ZONDA_STAY_IN_CFA)` part. Would it be an issue on
Regera?

> +static void clk_regera_pll_disable(struct clk_hw *hw)

This again is clk_zonda_pll_disable(), except the very last value written
to PLL_OPMODE is different. Could you commonize them?


> +
> +static void zonda_pll_adjust_l_val(unsigned long rate, unsigned long prate,
> +									u32 *l)

(Ugly wrapping, please touch it up)

..should it apply to zonda as the name suggests? Also, any explanations?

> +	u64 remainder, quotient;
> +
> +	quotient = rate;
> +	remainder = do_div(quotient, prate);
> +	*l = quotient;
> +
> +	if ((remainder * 2) / prate)
> +		*l = *l + 1;
> +}
> +
> +static int clk_regera_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long prate)
> +{
> +	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> +	unsigned long rrate;
> +	u32 l, alpha_width = pll_alpha_width(pll);
> +	u64 a;
> +	int ret;
> +
> +	rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
> +
> +	ret = alpha_pll_check_rate_margin(hw, rrate, rate);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (a && (a & BIT(15)))

What is BIT(15)?

Also, the left part of the condition is totally bogus, if a is 0 then
a & BIT(15) will surely be zero as well.

Konrad
Satya Priya Kakitapalli (Temp) March 8, 2024, 8:26 a.m. UTC | #2
On 3/2/2024 5:26 AM, Konrad Dybcio wrote:
> On 29.02.2024 06:38, Satya Priya Kakitapalli wrote:
>> From: Taniya Das <quic_tdas@quicinc.com>
>>
>> Regera PLL ops are required to control the Regera PLL from clock
>> controller drivers, thus add support for the same.
>>
>> Signed-off-by: Taniya Das <quic_tdas@quicinc.com>
>> Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
>> ---
> [...]
>
>
>> +static int clk_regera_pll_enable(struct clk_hw *hw)
> This function is 1:1 clk_zonda_pll_enable() logic-wise, except for
> the `if (val & ZONDA_STAY_IN_CFA)` part. Would it be an issue on
> Regera?


Yes, that is only applicable for Zonda PLL, hence we cannot re-use the 
same code for Regera.


>> +static void clk_regera_pll_disable(struct clk_hw *hw)
> This again is clk_zonda_pll_disable(), except the very last value written
> to PLL_OPMODE is different. Could you commonize them?
>

This difference is there between Zonda and regera PLLs as per the HW 
recommendation, hence we cannot re-use this.


>> +
>> +static void zonda_pll_adjust_l_val(unsigned long rate, unsigned long prate,
>> +									u32 *l)
> (Ugly wrapping, please touch it up)
>
> ..should it apply to zonda as the name suggests? Also, any explanations?


Yes, it is applicable for Zonda PLL as well, will update the Zonda pll 
set rate in next post. The L value needs to be adjusted to handle the 16 
bit signed alpha.


>> +	u64 remainder, quotient;
>> +
>> +	quotient = rate;
>> +	remainder = do_div(quotient, prate);
>> +	*l = quotient;
>> +
>> +	if ((remainder * 2) / prate)
>> +		*l = *l + 1;
>> +}
>> +
>> +static int clk_regera_pll_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				  unsigned long prate)
>> +{
>> +	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
>> +	unsigned long rrate;
>> +	u32 l, alpha_width = pll_alpha_width(pll);
>> +	u64 a;
>> +	int ret;
>> +
>> +	rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
>> +
>> +	ret = alpha_pll_check_rate_margin(hw, rrate, rate);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (a && (a & BIT(15)))
> What is BIT(15)?
>
> Also, the left part of the condition is totally bogus, if a is 0 then
> a & BIT(15) will surely be zero as well.


Sure, will correct this.


> Konrad
>
>
Konrad Dybcio March 13, 2024, 6:43 p.m. UTC | #3
On 3/8/24 09:26, Satya Priya Kakitapalli (Temp) wrote:
> 
> On 3/2/2024 5:26 AM, Konrad Dybcio wrote:
>> On 29.02.2024 06:38, Satya Priya Kakitapalli wrote:
>>> From: Taniya Das <quic_tdas@quicinc.com>
>>>
>>> Regera PLL ops are required to control the Regera PLL from clock
>>> controller drivers, thus add support for the same.
>>>
>>> Signed-off-by: Taniya Das <quic_tdas@quicinc.com>
>>> Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
>>> ---
>> [...]
>>
>>
>>> +static int clk_regera_pll_enable(struct clk_hw *hw)
>> This function is 1:1 clk_zonda_pll_enable() logic-wise, except for
>> the `if (val & ZONDA_STAY_IN_CFA)` part. Would it be an issue on
>> Regera?
> 
> 
> Yes, that is only applicable for Zonda PLL, hence we cannot re-use the same code for Regera.
> 
> 
>>> +static void clk_regera_pll_disable(struct clk_hw *hw)
>> This again is clk_zonda_pll_disable(), except the very last value written
>> to PLL_OPMODE is different. Could you commonize them?
>>
> 
> This difference is there between Zonda and regera PLLs as per the HW recommendation, hence we cannot re-use this.

Yes you can, just make the function accept an argument and consume it,
where things differ, so that we won't duplicate the same 80 or so lines
for no reason

Konrad
diff mbox series

Patch

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 8dc3b8774b8e..f0bf282212c9 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -1,7 +1,7 @@ 
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021, 2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021, 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/kernel.h>
@@ -2587,3 +2587,153 @@  const struct clk_ops clk_alpha_pll_stromer_plus_ops = {
 	.set_rate = clk_alpha_pll_stromer_plus_set_rate,
 };
 EXPORT_SYMBOL_GPL(clk_alpha_pll_stromer_plus_ops);
+
+void clk_regera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
+			     const struct alpha_pll_config *config)
+{
+	clk_alpha_pll_write_config(regmap, PLL_L_VAL(pll), config->l);
+	clk_alpha_pll_write_config(regmap, PLL_ALPHA_VAL(pll), config->alpha);
+	clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val);
+	clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL_U(pll), config->config_ctl_hi_val);
+	clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL_U1(pll), config->config_ctl_hi1_val);
+	clk_alpha_pll_write_config(regmap, PLL_USER_CTL(pll), config->user_ctl_val);
+	clk_alpha_pll_write_config(regmap, PLL_USER_CTL_U(pll), config->user_ctl_hi_val);
+	clk_alpha_pll_write_config(regmap, PLL_USER_CTL_U1(pll), config->user_ctl_hi1_val);
+	clk_alpha_pll_write_config(regmap, PLL_TEST_CTL(pll), config->test_ctl_val);
+	clk_alpha_pll_write_config(regmap, PLL_TEST_CTL_U(pll), config->test_ctl_hi_val);
+	clk_alpha_pll_write_config(regmap, PLL_TEST_CTL_U1(pll), config->test_ctl_hi1_val);
+
+	/* Set operation mode to STANDBY */
+	regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY);
+}
+EXPORT_SYMBOL_GPL(clk_regera_pll_configure);
+
+static int clk_regera_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;
+
+	regmap_read(regmap, PLL_MODE(pll), &val);
+
+	/* 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);
+	}
+
+	/* Get the PLL out of bypass mode */
+	regmap_update_bits(regmap, PLL_MODE(pll), PLL_BYPASSNL, PLL_BYPASSNL);
+
+	/*
+	 * H/W requires a 1us delay between disabling the bypass and
+	 * de-asserting the reset.
+	 */
+	udelay(1);
+
+	regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N);
+
+	/* Set operation mode to RUN */
+	regmap_write(regmap, PLL_OPMODE(pll), PLL_RUN);
+
+	ret = wait_for_pll_enable_lock(pll);
+	if (ret)
+		return ret;
+
+	/* Enable the PLL outputs */
+	regmap_update_bits(regmap, PLL_USER_CTL(pll), ZONDA_PLL_OUT_MASK, ZONDA_PLL_OUT_MASK);
+
+	/* Enable the global PLL outputs */
+	regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, PLL_OUTCTRL);
+
+	return 0;
+}
+
+static void clk_regera_pll_disable(struct clk_hw *hw)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	struct regmap *regmap = pll->clkr.regmap;
+	u32 val;
+
+	regmap_read(regmap, PLL_MODE(pll), &val);
+
+	/* If in FSM mode, just unvote it */
+	if (val & PLL_VOTE_FSM_ENA) {
+		clk_disable_regmap(hw);
+		return;
+	}
+
+	/* Disable the global PLL output */
+	regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0);
+
+	/* Disable the PLL outputs */
+	regmap_update_bits(regmap, PLL_USER_CTL(pll), ZONDA_PLL_OUT_MASK, 0);
+
+	/* Put the PLL in bypass and reset */
+	regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N | PLL_BYPASSNL, 0);
+
+	/* Place the PLL mode in STANDBY state */
+	regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY);
+}
+
+static void zonda_pll_adjust_l_val(unsigned long rate, unsigned long prate,
+									u32 *l)
+{
+	u64 remainder, quotient;
+
+	quotient = rate;
+	remainder = do_div(quotient, prate);
+	*l = quotient;
+
+	if ((remainder * 2) / prate)
+		*l = *l + 1;
+}
+
+static int clk_regera_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long prate)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	unsigned long rrate;
+	u32 l, alpha_width = pll_alpha_width(pll);
+	u64 a;
+	int ret;
+
+	rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
+
+	ret = alpha_pll_check_rate_margin(hw, rrate, rate);
+	if (ret < 0)
+		return ret;
+
+	if (a && (a & BIT(15)))
+		zonda_pll_adjust_l_val(rate, prate, &l);
+
+	regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
+	regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
+
+	/* Wait before polling for the frequency latch */
+	udelay(5);
+
+	if (clk_hw_is_enabled(hw)) {
+		ret = wait_for_pll_enable_lock(pll);
+		if (ret)
+			return ret;
+	}
+
+	/* Wait for PLL output to stabilize */
+	udelay(100);
+
+	return 0;
+}
+
+const struct clk_ops clk_alpha_pll_regera_ops = {
+	.enable = clk_regera_pll_enable,
+	.disable = clk_regera_pll_disable,
+	.is_enabled = clk_alpha_pll_is_enabled,
+	.recalc_rate = clk_trion_pll_recalc_rate,
+	.round_rate = clk_alpha_pll_round_rate,
+	.set_rate = clk_regera_pll_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_regera_ops);
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index fb6d50263bb9..5bb0a07da53d 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -21,6 +21,7 @@  enum {
 	CLK_ALPHA_PLL_TYPE_LUCID = CLK_ALPHA_PLL_TYPE_TRION,
 	CLK_ALPHA_PLL_TYPE_AGERA,
 	CLK_ALPHA_PLL_TYPE_ZONDA,
+	CLK_ALPHA_PLL_TYPE_REGERA = CLK_ALPHA_PLL_TYPE_ZONDA,
 	CLK_ALPHA_PLL_TYPE_ZONDA_OLE,
 	CLK_ALPHA_PLL_TYPE_LUCID_EVO,
 	CLK_ALPHA_PLL_TYPE_LUCID_OLE,
@@ -189,6 +190,8 @@  extern const struct clk_ops clk_alpha_pll_postdiv_lucid_evo_ops;
 extern const struct clk_ops clk_alpha_pll_rivian_evo_ops;
 #define clk_alpha_pll_postdiv_rivian_evo_ops clk_alpha_pll_postdiv_fabia_ops
 
+extern const struct clk_ops clk_alpha_pll_regera_ops;
+
 void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 			     const struct alpha_pll_config *config);
 void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
@@ -210,5 +213,7 @@  void clk_rivian_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regm
 				  const struct alpha_pll_config *config);
 void clk_stromer_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 			       const struct alpha_pll_config *config);
+void clk_regera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
+			     const struct alpha_pll_config *config);
 
 #endif