mbox series

[v2,0/3] Add Camera clock controller driver for SC7180

Message ID 1602609110-11504-1-git-send-email-tdas@codeaurora.org
Headers show
Series Add Camera clock controller driver for SC7180 | expand

Message

Taniya Das Oct. 13, 2020, 5:11 p.m. UTC
[v2]
 * Update PLL set rate function : clk_alpha_pll_agera_set_rate
 * Remove mb()

[v1]
 * Add support for Agera PLL which is used in the camera clock controller.

 * Add driver support for camera clock controller for SC7180 and also
   update device tree bindings for the various clocks supported in the
   clock controller.

Taniya Das (3):
  clk: qcom: clk-alpha-pll: Add support for controlling Agera PLLs
  dt-bindings: clock: Add YAML schemas for the QCOM Camera clock
    bindings.
  clk: qcom: camcc: Add camera clock controller driver for SC7180

 .../bindings/clock/qcom,sc7180-camcc.yaml          |   73 +
 drivers/clk/qcom/Kconfig                           |    9 +
 drivers/clk/qcom/Makefile                          |    1 +
 drivers/clk/qcom/camcc-sc7180.c                    | 1737 ++++++++++++++++++++
 drivers/clk/qcom/clk-alpha-pll.c                   |   80 +
 drivers/clk/qcom/clk-alpha-pll.h                   |    4 +
 include/dt-bindings/clock/qcom,camcc-sc7180.h      |  121 ++
 7 files changed, 2025 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,sc7180-camcc.yaml
 create mode 100644 drivers/clk/qcom/camcc-sc7180.c
 create mode 100644 include/dt-bindings/clock/qcom,camcc-sc7180.h

--
Qualcomm INDIA, on behalf of Qualcomm Innovation Center, Inc.is a member
of the Code Aurora Forum, hosted by the  Linux Foundation.

Comments

Stephen Boyd Oct. 14, 2020, 1:59 a.m. UTC | #1
Quoting Taniya Das (2020-10-13 10:11:50)
> diff --git a/drivers/clk/qcom/camcc-sc7180.c b/drivers/clk/qcom/camcc-sc7180.c
> new file mode 100644
> index 0000000..e954d21
> --- /dev/null
> +++ b/drivers/clk/qcom/camcc-sc7180.c
> @@ -0,0 +1,1737 @@
[...]
> +
> +enum {
> +       P_BI_TCXO,
> +       P_CAM_CC_PLL0_OUT_EVEN,
> +       P_CAM_CC_PLL1_OUT_EVEN,
> +       P_CAM_CC_PLL2_OUT_AUX,
> +       P_CAM_CC_PLL2_OUT_EARLY,
> +       P_CAM_CC_PLL3_OUT_MAIN,
> +       P_CORE_BI_PLL_TEST_SE,
> +};
> +
> +static struct pll_vco agera_vco[] = {

Can this be const?

> +       { 600000000, 3300000000, 0 },
> +};
> +
> +static struct pll_vco fabia_vco[] = {

Can this be const?

> +       { 249600000, 2000000000, 0 },
> +};
> +
[...]
> +
> +static int cam_cc_sc7180_probe(struct platform_device *pdev)
> +{
> +       struct regmap *regmap;
> +       int ret;
> +
> +       pm_runtime_enable(&pdev->dev);
> +       ret = pm_clk_create(&pdev->dev);
> +       if (ret)
> +               return ret;
> +
> +       ret = pm_clk_add(&pdev->dev, "xo");
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Failed to acquire XO clock\n");
> +               goto disable_pm_runtime;
> +       }
> +
> +       ret = pm_clk_add(&pdev->dev, "iface");
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Failed to acquire iface clock\n");
> +               goto disable_pm_runtime;
> +       }
> +
> +       ret = pm_clk_runtime_resume(&pdev->dev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "pm runtime resume failed\n");
> +               goto destroy_pm_clk;
> +       }
> +
> +       regmap = qcom_cc_map(pdev, &cam_cc_sc7180_desc);
> +       if (IS_ERR(regmap)) {
> +               ret = PTR_ERR(regmap);
> +               goto destroy_pm_clk;
> +       }
> +
> +       clk_fabia_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config);
> +       clk_fabia_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config);
> +       clk_agera_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config);
> +       clk_fabia_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config);
> +
> +       ret = qcom_cc_really_probe(pdev, &cam_cc_sc7180_desc, regmap);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Failed to register CAM CC clocks\n");
> +               goto suspend_pm_runtime;

ret is non-zero here

> +       }
> +
> +suspend_pm_runtime:
> +       ret = pm_clk_runtime_suspend(&pdev->dev);

But then it is overwritten here.

> +       if (ret)
> +               dev_err(&pdev->dev, "pm runtime suspend failed\n");
> +
> +       return 0;

And we return 0 when there was a failure to probe the clks?

> +
> +destroy_pm_clk:
> +       pm_clk_destroy(&pdev->dev);
> +
> +disable_pm_runtime:
> +       pm_runtime_disable(&pdev->dev);
> +
> +       return ret;
> +}
Stephen Boyd Oct. 14, 2020, 2:07 a.m. UTC | #2
Quoting Taniya Das (2020-10-13 10:11:48)
> diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
> index 26139ef..17e1fc0 100644
> --- a/drivers/clk/qcom/clk-alpha-pll.c
> +++ b/drivers/clk/qcom/clk-alpha-pll.c
> @@ -1561,3 +1571,73 @@ const struct clk_ops clk_alpha_pll_postdiv_lucid_ops = {
>         .set_rate = clk_alpha_pll_postdiv_fabia_set_rate,
>  };
>  EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_lucid_ops);
> +
> +void clk_agera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
> +                       const struct alpha_pll_config *config)
> +{
> +       if (config->l)
> +               regmap_write(regmap, PLL_L_VAL(pll), config->l);

Maybe make a helper function for this too. That way we can't mix up the
if condition with the value in the write.

	clk_alpha_pll_write_config(regmap, PLL_L_VAL(pll), config->l);

static void clk_alpha_pll_write_config(struct regmap *regmap,
				       unsigned int reg,
				       unsigned int val) {
	if (val)
		regmap_write(regmap, reg, val);
}

and how are we so lucky that zero isn't a value that we may need to
write?

> +
> +       if (config->alpha)
> +               regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha);
> +
> +       if (config->user_ctl_val)
> +               regmap_write(regmap, PLL_USER_CTL(pll), config->user_ctl_val);
> +
> +       if (config->config_ctl_val)
> +               regmap_write(regmap, PLL_CONFIG_CTL(pll),
> +                                               config->config_ctl_val);
> +
> +       if (config->config_ctl_hi_val)
> +               regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
> +                                               config->config_ctl_hi_val);
> +
> +       if (config->test_ctl_val)
> +               regmap_write(regmap, PLL_TEST_CTL(pll),
> +                                               config->test_ctl_val);
> +
> +       if (config->test_ctl_hi_val)
> +               regmap_write(regmap,  PLL_TEST_CTL_U(pll),
> +                                               config->test_ctl_hi_val);
> +}
> +EXPORT_SYMBOL_GPL(clk_agera_pll_configure);
> +
> +static int clk_alpha_pll_agera_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                                       unsigned long prate)
> +{
> +       struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
> +       u32 l, alpha_width = pll_alpha_width(pll);
> +       unsigned long rrate, max = rate + PLL_RATE_MARGIN;
> +       u64 a;
> +
> +       rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
> +
> +       /*
> +        * Due to limited number of bits for fractional rate programming, the
> +        * rounded up rate could be marginally higher than the requested rate.
> +        */
> +       if (rrate > (rate + PLL_RATE_MARGIN) || rrate < rate) {
> +               pr_err("%s: Rounded rate %lu not within range [%lu, %lu)\n",
> +                      clk_hw_get_name(hw), rrate, rate, max);
> +               return -EINVAL;
> +       }

Can this be extracted into a helper function?

> +
> +       /* change L_VAL without having to go through the power on sequence */
> +       regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
> +       regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
> +
> +       if (clk_hw_is_enabled(hw))
> +               return wait_for_pll_enable_lock(pll);
> +
> +       return 0;
> +}
> +
Taniya Das Oct. 16, 2020, 6:38 p.m. UTC | #3
Thanks Stephen for the review comments.

On 10/14/2020 7:37 AM, Stephen Boyd wrote:
> Quoting Taniya Das (2020-10-13 10:11:48)
>> diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
>> index 26139ef..17e1fc0 100644
>> --- a/drivers/clk/qcom/clk-alpha-pll.c
>> +++ b/drivers/clk/qcom/clk-alpha-pll.c
>> @@ -1561,3 +1571,73 @@ const struct clk_ops clk_alpha_pll_postdiv_lucid_ops = {
>>          .set_rate = clk_alpha_pll_postdiv_fabia_set_rate,
>>   };
>>   EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_lucid_ops);
>> +
>> +void clk_agera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
>> +                       const struct alpha_pll_config *config)
>> +{
>> +       if (config->l)
>> +               regmap_write(regmap, PLL_L_VAL(pll), config->l);
> 
> Maybe make a helper function for this too. That way we can't mix up the
> if condition with the value in the write.
> 

Sure, I will add a helper function.

> 	clk_alpha_pll_write_config(regmap, PLL_L_VAL(pll), config->l);
> 
> static void clk_alpha_pll_write_config(struct regmap *regmap,
> 				       unsigned int reg,
> 				       unsigned int val) {
> 	if (val)
> 		regmap_write(regmap, reg, val);
> }
> 
> and how are we so lucky that zero isn't a value that we may need to
> write?
> 
>> +
>> +       if (config->alpha)
>> +               regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha);
>> +
>> +       if (config->user_ctl_val)
>> +               regmap_write(regmap, PLL_USER_CTL(pll), config->user_ctl_val);
>> +
>> +       if (config->config_ctl_val)
>> +               regmap_write(regmap, PLL_CONFIG_CTL(pll),
>> +                                               config->config_ctl_val);
>> +
>> +       if (config->config_ctl_hi_val)
>> +               regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
>> +                                               config->config_ctl_hi_val);
>> +
>> +       if (config->test_ctl_val)
>> +               regmap_write(regmap, PLL_TEST_CTL(pll),
>> +                                               config->test_ctl_val);
>> +
>> +       if (config->test_ctl_hi_val)
>> +               regmap_write(regmap,  PLL_TEST_CTL_U(pll),
>> +                                               config->test_ctl_hi_val);
>> +}
>> +EXPORT_SYMBOL_GPL(clk_agera_pll_configure);
>> +
>> +static int clk_alpha_pll_agera_set_rate(struct clk_hw *hw, unsigned long rate,
>> +                                                       unsigned long prate)
>> +{
>> +       struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
>> +       u32 l, alpha_width = pll_alpha_width(pll);
>> +       unsigned long rrate, max = rate + PLL_RATE_MARGIN;
>> +       u64 a;
>> +
>> +       rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
>> +
>> +       /*
>> +        * Due to limited number of bits for fractional rate programming, the
>> +        * rounded up rate could be marginally higher than the requested rate.
>> +        */
>> +       if (rrate > (rate + PLL_RATE_MARGIN) || rrate < rate) {
>> +               pr_err("%s: Rounded rate %lu not within range [%lu, %lu)\n",
>> +                      clk_hw_get_name(hw), rrate, rate, max);
>> +               return -EINVAL;
>> +       }
> 
> Can this be extracted into a helper function?
> 

Yes, I will add this too.

>> +
>> +       /* change L_VAL without having to go through the power on sequence */
>> +       regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
>> +       regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
>> +
>> +       if (clk_hw_is_enabled(hw))
>> +               return wait_for_pll_enable_lock(pll);
>> +
>> +       return 0;
>> +}
>> +
Taniya Das Oct. 16, 2020, 6:40 p.m. UTC | #4
Thanks for your review Stephen.

On 10/14/2020 7:29 AM, Stephen Boyd wrote:
> Quoting Taniya Das (2020-10-13 10:11:50)
>> diff --git a/drivers/clk/qcom/camcc-sc7180.c b/drivers/clk/qcom/camcc-sc7180.c
>> new file mode 100644
>> index 0000000..e954d21
>> --- /dev/null
>> +++ b/drivers/clk/qcom/camcc-sc7180.c
>> @@ -0,0 +1,1737 @@
> [...]
>> +
>> +enum {
>> +       P_BI_TCXO,
>> +       P_CAM_CC_PLL0_OUT_EVEN,
>> +       P_CAM_CC_PLL1_OUT_EVEN,
>> +       P_CAM_CC_PLL2_OUT_AUX,
>> +       P_CAM_CC_PLL2_OUT_EARLY,
>> +       P_CAM_CC_PLL3_OUT_MAIN,
>> +       P_CORE_BI_PLL_TEST_SE,
>> +};
>> +
>> +static struct pll_vco agera_vco[] = {
> 
> Can this be const?
> 
>> +       { 600000000, 3300000000, 0 },
>> +};
>> +
>> +static struct pll_vco fabia_vco[] = {
> 
> Can this be const?
> 
>> +       { 249600000, 2000000000, 0 },
>> +};
>> +
> [...]

Will take care of the above in the next patch.

>> +
>> +static int cam_cc_sc7180_probe(struct platform_device *pdev)
>> +{
>> +       struct regmap *regmap;
>> +       int ret;
>> +
>> +       pm_runtime_enable(&pdev->dev);
>> +       ret = pm_clk_create(&pdev->dev);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = pm_clk_add(&pdev->dev, "xo");
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "Failed to acquire XO clock\n");
>> +               goto disable_pm_runtime;
>> +       }
>> +
>> +       ret = pm_clk_add(&pdev->dev, "iface");
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "Failed to acquire iface clock\n");
>> +               goto disable_pm_runtime;
>> +       }
>> +
>> +       ret = pm_clk_runtime_resume(&pdev->dev);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "pm runtime resume failed\n");
>> +               goto destroy_pm_clk;
>> +       }
>> +
>> +       regmap = qcom_cc_map(pdev, &cam_cc_sc7180_desc);
>> +       if (IS_ERR(regmap)) {
>> +               ret = PTR_ERR(regmap);
>> +               goto destroy_pm_clk;
>> +       }
>> +
>> +       clk_fabia_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config);
>> +       clk_fabia_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config);
>> +       clk_agera_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config);
>> +       clk_fabia_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config);
>> +
>> +       ret = qcom_cc_really_probe(pdev, &cam_cc_sc7180_desc, regmap);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Failed to register CAM CC clocks\n");
>> +               goto suspend_pm_runtime;
> 
> ret is non-zero here
> 
>> +       }
>> +
>> +suspend_pm_runtime:
>> +       ret = pm_clk_runtime_suspend(&pdev->dev);
> 
> But then it is overwritten here.
> 
>> +       if (ret)
>> +               dev_err(&pdev->dev, "pm runtime suspend failed\n");
>> +
>> +       return 0;
> 
> And we return 0 when there was a failure to probe the clks?
> 

I will clean the error path in the next patch.

>> +
>> +destroy_pm_clk:
>> +       pm_clk_destroy(&pdev->dev);
>> +
>> +disable_pm_runtime:
>> +       pm_runtime_disable(&pdev->dev);
>> +
>> +       return ret;
>> +}