Loading drivers/clk/qcom/clk-alpha-pll.c +183 −0 Original line number Diff line number Diff line Loading @@ -2429,3 +2429,186 @@ const struct clk_ops clk_alpha_pll_agera_ops = { .bus_vote = clk_debug_bus_vote, }; EXPORT_SYMBOL(clk_alpha_pll_agera_ops); static int clk_alpha_pll_slew_update(struct clk_alpha_pll *pll) { int ret = 0; u32 val; regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, PLL_UPDATE); regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); ret = wait_for_pll_update(pll); if (ret) return ret; /* * HPG mandates a wait of at least 570ns before polling the LOCK * detect bit. Have a delay of 1us just to be safe. */ mb(); udelay(1); ret = wait_for_pll_enable_lock(pll); return ret; } static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); unsigned long freq_hz; const struct pll_vco *curr_vco, *vco; u32 l, alpha_width = pll_alpha_width(pll); u64 a; freq_hz = alpha_pll_round_rate(rate, parent_rate, &l, &a, alpha_width); if (freq_hz != rate) { pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n"); return -EINVAL; } curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); if (!curr_vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; } vco = alpha_pll_find_vco(pll, freq_hz); if (!vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; } /* * Dynamic pll update will not support switching frequencies across * vco ranges. In those cases fall back to normal alpha set rate. */ if (curr_vco->val != vco->val) return clk_alpha_pll_set_rate(hw, rate, parent_rate); a = a << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); /* Ensure that the write above goes through before proceeding. */ mb(); if (clk_hw_is_enabled(hw)) clk_alpha_pll_slew_update(pll); return 0; } /* * Slewing plls should be bought up at frequency which is in the middle of the * desired VCO range. So after bringing up the pll at calibration freq, set it * back to desired frequency(that was set by previous clk_set_rate). */ static int clk_alpha_pll_calibrate(struct clk_hw *hw) { unsigned long calibration_freq, freq_hz; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); struct clk_hw *parent; const struct pll_vco *vco; u64 a; u32 l, alpha_width = pll_alpha_width(pll); int rc; parent = clk_hw_get_parent(hw); if (!parent) { pr_err("alpha pll: no valid parent found\n"); return -EINVAL; } vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); if (!vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; } /* * As during slewing plls vco_sel won't be allowed to change, vco table * should have only one entry table, i.e. index = 0, find the * calibration frequency. */ calibration_freq = (pll->vco_table[0].min_freq + pll->vco_table[0].max_freq)/2; freq_hz = alpha_pll_round_rate(calibration_freq, clk_hw_get_rate(parent), &l, &a, alpha_width); if (freq_hz != calibration_freq) { pr_err("alpha_pll: call clk_set_rate with rounded rates!\n"); return -EINVAL; } /* Setup PLL for calibration frequency */ a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_VCO_MASK << PLL_VCO_SHIFT, vco->val << PLL_VCO_SHIFT); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); /* Bringup the pll at calibration frequency */ rc = clk_alpha_pll_enable(hw); if (rc) { pr_err("alpha pll calibration failed\n"); return rc; } /* * PLL is already running at calibration frequency. * So slew pll to the previously set frequency. */ freq_hz = alpha_pll_round_rate(clk_hw_get_rate(hw), clk_hw_get_rate(parent), &l, &a, alpha_width); pr_debug("pll %s: setting back to required rate %lu, freq_hz %ld\n", hw->init->name, clk_hw_get_rate(hw), freq_hz); /* Setup the PLL for the new frequency */ a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); return clk_alpha_pll_slew_update(pll); } static int clk_alpha_pll_slew_enable(struct clk_hw *hw) { int rc; rc = clk_alpha_pll_calibrate(hw); if (rc) return rc; rc = clk_alpha_pll_enable(hw); return rc; } const struct clk_ops clk_alpha_pll_slew_ops = { .enable = clk_alpha_pll_slew_enable, .disable = clk_alpha_pll_disable, .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_alpha_pll_slew_set_rate, .list_registers = clk_alpha_pll_list_registers, }; drivers/clk/qcom/clk-alpha-pll.h +1 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,7 @@ extern const struct clk_ops clk_alpha_pll_zonda_ops; extern const struct clk_ops clk_alpha_pll_postdiv_zonda_ops; extern const struct clk_ops clk_alpha_pll_agera_ops; extern const struct clk_ops clk_alpha_pll_slew_ops; void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); Loading Loading
drivers/clk/qcom/clk-alpha-pll.c +183 −0 Original line number Diff line number Diff line Loading @@ -2429,3 +2429,186 @@ const struct clk_ops clk_alpha_pll_agera_ops = { .bus_vote = clk_debug_bus_vote, }; EXPORT_SYMBOL(clk_alpha_pll_agera_ops); static int clk_alpha_pll_slew_update(struct clk_alpha_pll *pll) { int ret = 0; u32 val; regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, PLL_UPDATE); regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); ret = wait_for_pll_update(pll); if (ret) return ret; /* * HPG mandates a wait of at least 570ns before polling the LOCK * detect bit. Have a delay of 1us just to be safe. */ mb(); udelay(1); ret = wait_for_pll_enable_lock(pll); return ret; } static int clk_alpha_pll_slew_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); unsigned long freq_hz; const struct pll_vco *curr_vco, *vco; u32 l, alpha_width = pll_alpha_width(pll); u64 a; freq_hz = alpha_pll_round_rate(rate, parent_rate, &l, &a, alpha_width); if (freq_hz != rate) { pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n"); return -EINVAL; } curr_vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); if (!curr_vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; } vco = alpha_pll_find_vco(pll, freq_hz); if (!vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; } /* * Dynamic pll update will not support switching frequencies across * vco ranges. In those cases fall back to normal alpha set rate. */ if (curr_vco->val != vco->val) return clk_alpha_pll_set_rate(hw, rate, parent_rate); a = a << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); /* Ensure that the write above goes through before proceeding. */ mb(); if (clk_hw_is_enabled(hw)) clk_alpha_pll_slew_update(pll); return 0; } /* * Slewing plls should be bought up at frequency which is in the middle of the * desired VCO range. So after bringing up the pll at calibration freq, set it * back to desired frequency(that was set by previous clk_set_rate). */ static int clk_alpha_pll_calibrate(struct clk_hw *hw) { unsigned long calibration_freq, freq_hz; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); struct clk_hw *parent; const struct pll_vco *vco; u64 a; u32 l, alpha_width = pll_alpha_width(pll); int rc; parent = clk_hw_get_parent(hw); if (!parent) { pr_err("alpha pll: no valid parent found\n"); return -EINVAL; } vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); if (!vco) { pr_err("alpha pll: not in a valid vco range\n"); return -EINVAL; } /* * As during slewing plls vco_sel won't be allowed to change, vco table * should have only one entry table, i.e. index = 0, find the * calibration frequency. */ calibration_freq = (pll->vco_table[0].min_freq + pll->vco_table[0].max_freq)/2; freq_hz = alpha_pll_round_rate(calibration_freq, clk_hw_get_rate(parent), &l, &a, alpha_width); if (freq_hz != calibration_freq) { pr_err("alpha_pll: call clk_set_rate with rounded rates!\n"); return -EINVAL; } /* Setup PLL for calibration frequency */ a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_VCO_MASK << PLL_VCO_SHIFT, vco->val << PLL_VCO_SHIFT); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); /* Bringup the pll at calibration frequency */ rc = clk_alpha_pll_enable(hw); if (rc) { pr_err("alpha pll calibration failed\n"); return rc; } /* * PLL is already running at calibration frequency. * So slew pll to the previously set frequency. */ freq_hz = alpha_pll_round_rate(clk_hw_get_rate(hw), clk_hw_get_rate(parent), &l, &a, alpha_width); pr_debug("pll %s: setting back to required rate %lu, freq_hz %ld\n", hw->init->name, clk_hw_get_rate(hw), freq_hz); /* Setup the PLL for the new frequency */ a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); return clk_alpha_pll_slew_update(pll); } static int clk_alpha_pll_slew_enable(struct clk_hw *hw) { int rc; rc = clk_alpha_pll_calibrate(hw); if (rc) return rc; rc = clk_alpha_pll_enable(hw); return rc; } const struct clk_ops clk_alpha_pll_slew_ops = { .enable = clk_alpha_pll_slew_enable, .disable = clk_alpha_pll_disable, .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_alpha_pll_slew_set_rate, .list_registers = clk_alpha_pll_list_registers, };
drivers/clk/qcom/clk-alpha-pll.h +1 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,7 @@ extern const struct clk_ops clk_alpha_pll_zonda_ops; extern const struct clk_ops clk_alpha_pll_postdiv_zonda_ops; extern const struct clk_ops clk_alpha_pll_agera_ops; extern const struct clk_ops clk_alpha_pll_slew_ops; void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); Loading