Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clk: qcom: clk-alpha-pll: Add support for dynamic update for slewing PLLs

The alpha PLLs which slew to a new frequency at runtime would require
the PLL to calibrate at the mid point of the VCO. Add the new PLL ops
which can support the slewing of the PLL to a new frequency.

Reviewed-by: Imran Shaik <quic_imrashai@quicinc.com>
Signed-off-by: Taniya Das <quic_tdas@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250702-qcs615-mm-v10-clock-controllers-v11-1-9c216e1615ab@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>

authored by

Taniya Das and committed by
Bjorn Andersson
48d2c6de f6a4a55a

+175 -18
+174 -18
drivers/clk/qcom/clk-alpha-pll.c
··· 790 790 return __clk_alpha_pll_update_latch(pll); 791 791 } 792 792 793 + static void clk_alpha_pll_update_configs(struct clk_alpha_pll *pll, const struct pll_vco *vco, 794 + u32 l, u64 alpha, u32 alpha_width, bool alpha_en) 795 + { 796 + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); 797 + 798 + if (alpha_width > ALPHA_BITWIDTH) 799 + alpha <<= alpha_width - ALPHA_BITWIDTH; 800 + 801 + if (alpha_width > 32) 802 + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), upper_32_bits(alpha)); 803 + 804 + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), lower_32_bits(alpha)); 805 + 806 + if (vco) { 807 + regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), 808 + PLL_VCO_MASK << PLL_VCO_SHIFT, 809 + vco->val << PLL_VCO_SHIFT); 810 + } 811 + 812 + if (alpha_en) 813 + regmap_set_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN); 814 + } 815 + 793 816 static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, 794 817 unsigned long prate, 795 818 int (*is_enabled)(struct clk_hw *)) ··· 830 807 return -EINVAL; 831 808 } 832 809 833 - regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); 834 - 835 - if (alpha_width > ALPHA_BITWIDTH) 836 - a <<= alpha_width - ALPHA_BITWIDTH; 837 - 838 - if (alpha_width > 32) 839 - regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); 840 - 841 - regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); 842 - 843 - if (vco) { 844 - regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), 845 - PLL_VCO_MASK << PLL_VCO_SHIFT, 846 - vco->val << PLL_VCO_SHIFT); 847 - } 848 - 849 - regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), 850 - PLL_ALPHA_EN, PLL_ALPHA_EN); 810 + clk_alpha_pll_update_configs(pll, vco, l, a, alpha_width, true); 851 811 852 812 return clk_alpha_pll_update_latch(pll, is_enabled); 853 813 } ··· 3023 3017 } 3024 3018 } 3025 3019 EXPORT_SYMBOL_GPL(qcom_clk_alpha_pll_configure); 3020 + 3021 + static int clk_alpha_pll_slew_update(struct clk_alpha_pll *pll) 3022 + { 3023 + u32 val; 3024 + int ret; 3025 + 3026 + regmap_set_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE); 3027 + regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); 3028 + 3029 + ret = wait_for_pll_update(pll); 3030 + if (ret) 3031 + return ret; 3032 + /* 3033 + * Hardware programming mandates a wait of at least 570ns before polling the LOCK 3034 + * detect bit. Have a delay of 1us just to be safe. 3035 + */ 3036 + udelay(1); 3037 + 3038 + return wait_for_pll_enable_lock(pll); 3039 + } 3040 + 3041 + static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate, 3042 + unsigned long parent_rate) 3043 + { 3044 + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); 3045 + const struct pll_vco *curr_vco, *vco; 3046 + unsigned long freq_hz; 3047 + u64 a; 3048 + u32 l; 3049 + 3050 + freq_hz = alpha_pll_round_rate(rate, parent_rate, &l, &a, ALPHA_REG_BITWIDTH); 3051 + if (freq_hz != rate) { 3052 + pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n"); 3053 + return -EINVAL; 3054 + } 3055 + 3056 + curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); 3057 + if (!curr_vco) { 3058 + pr_err("alpha pll: not in a valid vco range\n"); 3059 + return -EINVAL; 3060 + } 3061 + 3062 + vco = alpha_pll_find_vco(pll, freq_hz); 3063 + if (!vco) { 3064 + pr_err("alpha pll: not in a valid vco range\n"); 3065 + return -EINVAL; 3066 + } 3067 + 3068 + /* 3069 + * Dynamic pll update will not support switching frequencies across 3070 + * vco ranges. In those cases fall back to normal alpha set rate. 3071 + */ 3072 + if (curr_vco->val != vco->val) 3073 + return clk_alpha_pll_set_rate(hw, rate, parent_rate); 3074 + 3075 + clk_alpha_pll_update_configs(pll, NULL, l, a, ALPHA_REG_BITWIDTH, false); 3076 + 3077 + /* Ensure that the write above goes before slewing the PLL */ 3078 + mb(); 3079 + 3080 + if (clk_hw_is_enabled(hw)) 3081 + return clk_alpha_pll_slew_update(pll); 3082 + 3083 + return 0; 3084 + } 3085 + 3086 + /* 3087 + * Slewing plls should be bought up at frequency which is in the middle of the 3088 + * desired VCO range. So after bringing up the pll at calibration freq, set it 3089 + * back to desired frequency(that was set by previous clk_set_rate). 3090 + */ 3091 + static int clk_alpha_pll_calibrate(struct clk_hw *hw) 3092 + { 3093 + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); 3094 + struct clk_hw *parent; 3095 + const struct pll_vco *vco; 3096 + unsigned long calibration_freq, freq_hz; 3097 + u64 a; 3098 + u32 l; 3099 + int rc; 3100 + 3101 + parent = clk_hw_get_parent(hw); 3102 + if (!parent) { 3103 + pr_err("alpha pll: no valid parent found\n"); 3104 + return -EINVAL; 3105 + } 3106 + 3107 + vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); 3108 + if (!vco) { 3109 + pr_err("alpha pll: not in a valid vco range\n"); 3110 + return -EINVAL; 3111 + } 3112 + 3113 + /* 3114 + * As during slewing plls vco_sel won't be allowed to change, vco table 3115 + * should have only one entry table, i.e. index = 0, find the 3116 + * calibration frequency. 3117 + */ 3118 + calibration_freq = (pll->vco_table[0].min_freq + pll->vco_table[0].max_freq) / 2; 3119 + 3120 + freq_hz = alpha_pll_round_rate(calibration_freq, clk_hw_get_rate(parent), 3121 + &l, &a, ALPHA_REG_BITWIDTH); 3122 + if (freq_hz != calibration_freq) { 3123 + pr_err("alpha_pll: call clk_set_rate with rounded rates!\n"); 3124 + return -EINVAL; 3125 + } 3126 + 3127 + clk_alpha_pll_update_configs(pll, vco, l, a, ALPHA_REG_BITWIDTH, false); 3128 + 3129 + /* Bringup the pll at calibration frequency */ 3130 + rc = clk_alpha_pll_enable(hw); 3131 + if (rc) { 3132 + pr_err("alpha pll calibration failed\n"); 3133 + return rc; 3134 + } 3135 + 3136 + /* 3137 + * PLL is already running at calibration frequency. 3138 + * So slew pll to the previously set frequency. 3139 + */ 3140 + freq_hz = alpha_pll_round_rate(clk_hw_get_rate(hw), 3141 + clk_hw_get_rate(parent), &l, &a, ALPHA_REG_BITWIDTH); 3142 + 3143 + pr_debug("pll %s: setting back to required rate %lu, freq_hz %ld\n", 3144 + clk_hw_get_name(hw), clk_hw_get_rate(hw), freq_hz); 3145 + 3146 + clk_alpha_pll_update_configs(pll, NULL, l, a, ALPHA_REG_BITWIDTH, true); 3147 + 3148 + return clk_alpha_pll_slew_update(pll); 3149 + } 3150 + 3151 + static int clk_alpha_pll_slew_enable(struct clk_hw *hw) 3152 + { 3153 + int rc; 3154 + 3155 + rc = clk_alpha_pll_calibrate(hw); 3156 + if (rc) 3157 + return rc; 3158 + 3159 + return clk_alpha_pll_enable(hw); 3160 + } 3161 + 3162 + const struct clk_ops clk_alpha_pll_slew_ops = { 3163 + .enable = clk_alpha_pll_slew_enable, 3164 + .disable = clk_alpha_pll_disable, 3165 + .recalc_rate = clk_alpha_pll_recalc_rate, 3166 + .round_rate = clk_alpha_pll_round_rate, 3167 + .set_rate = clk_alpha_pll_slew_set_rate, 3168 + }; 3169 + EXPORT_SYMBOL(clk_alpha_pll_slew_ops);
+1
drivers/clk/qcom/clk-alpha-pll.h
··· 206 206 #define clk_alpha_pll_postdiv_rivian_evo_ops clk_alpha_pll_postdiv_fabia_ops 207 207 208 208 extern const struct clk_ops clk_alpha_pll_regera_ops; 209 + extern const struct clk_ops clk_alpha_pll_slew_ops; 209 210 210 211 void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, 211 212 const struct alpha_pll_config *config);