Loading drivers/clk/qcom/clk-alpha-pll.c +373 −32 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/delay.h> #include <linux/sched/clock.h> #include "clk-alpha-pll.h" #include "clk-debug.h" Loading @@ -23,6 +24,7 @@ # define PLL_LOCK_COUNT_MASK 0x3f # define PLL_BIAS_COUNT_SHIFT 14 # define PLL_BIAS_COUNT_MASK 0x3f #define PLL_LATCH_INTERFACE BIT(11) # define PLL_VOTE_FSM_ENA BIT(20) # define PLL_FSM_ENA BIT(20) # define PLL_VOTE_FSM_RESET BIT(21) Loading Loading @@ -291,12 +293,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, u32 val; int count; int ret; u64 time; const char *name = clk_hw_get_name(&pll->clkr.hw); ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; time = sched_clock(); for (count = 100; count > 0; count--) { ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) Loading @@ -308,6 +313,9 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, udelay(1); } time = sched_clock() - time; pr_err("PLL lock bit detection total wait time: %lld ns\n", time); WARN(1, "%s failed to %s!\n", name, action); return -ETIMEDOUT; Loading Loading @@ -337,14 +345,21 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, #define wait_for_pll_update_ack_clear(pll) \ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear") #define wait_for_pll_latch_ack(pll) \ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "latch ack") void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config) { u32 val, mask; if (config->l) regmap_write(regmap, PLL_L_VAL(pll), config->l); if (config->alpha) regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha); regmap_write(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); if (config->config_ctl_val) regmap_write(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); if (pll_has_64bit_config(pll)) regmap_write(regmap, PLL_CONFIG_CTL_U(pll), Loading @@ -353,25 +368,60 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, if (pll_alpha_width(pll) > 32) regmap_write(regmap, PLL_ALPHA_VAL_U(pll), config->alpha_hi); if (config->main_output_mask || config->aux_output_mask || config->aux2_output_mask || config->early_output_mask || config->pre_div_val || config->vco_val || config->alpha_en_mask) { val = config->main_output_mask; val |= config->aux_output_mask; val |= config->aux2_output_mask; val |= config->early_output_mask; val |= config->pre_div_val; val |= config->post_div_val; val |= config->vco_val; val |= config->alpha_en_mask; val |= config->alpha_mode_mask; mask = config->main_output_mask; mask |= config->aux_output_mask; mask |= config->aux2_output_mask; mask |= config->early_output_mask; mask |= config->pre_div_mask; mask |= config->post_div_mask; mask |= config->vco_mask; mask |= config->alpha_en_mask; regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val); } if (config->post_div_mask) { mask = config->post_div_mask; val = config->post_div_val; regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val); } /* Do not bypass the latch interface */ if (pll->flags & SUPPORTS_SLEW) regmap_update_bits(regmap, PLL_USER_CTL_U(pll), PLL_LATCH_INTERFACE, (u32)~PLL_LATCH_INTERFACE); if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) { regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, PLL_UPDATE_BYPASS); } if (config->test_ctl_mask) { mask = config->test_ctl_mask; val = config->test_ctl_val; regmap_update_bits(regmap, PLL_TEST_CTL(pll), mask, val); } if (config->test_ctl_hi_mask) { mask = config->test_ctl_hi_mask; val = config->test_ctl_hi_val; regmap_update_bits(regmap, PLL_TEST_CTL_U(pll), mask, val); } if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, PLL_UPDATE_BYPASS); if (pll->flags & SUPPORTS_FSM_MODE) qcom_pll_set_fsm_mode(regmap, PLL_MODE(pll), 6, 0); Loading Loading @@ -610,6 +660,48 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return alpha_pll_calc_rate(prate, l, a, alpha_width); } static int clk_alpha_pll_dynamic_update(struct clk_alpha_pll *pll) { int ret; /* Latch the input to the PLL */ regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, PLL_UPDATE); /* Wait for 2 reference cycle before checking ACK bit */ udelay(1); ret = wait_for_pll_latch_ack(pll); if (ret) return ret; /* Return latch input to 0 */ regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, (u32)~PLL_UPDATE); ret = wait_for_pll_enable_lock(pll); if (ret) return ret; return 0; } static const struct pll_vco_data *find_vco_data(const struct pll_vco_data *data, unsigned long rate, size_t size) { int i; if (!data) return NULL; for (i = 0; i < size; i++) { if (rate == data[i].freq) return &data[i]; } return &data[i - 1]; } static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll) { Loading Loading @@ -644,6 +736,9 @@ static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll) return ret; } if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) ret = wait_for_pll_enable_lock(pll); else ret = wait_for_pll_update_ack_clear(pll); if (ret) return ret; Loading @@ -669,17 +764,36 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, int (*is_enabled)(struct clk_hw *)) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); struct clk_alpha_pll_postdiv *pll_postdiv = to_clk_alpha_pll_postdiv(hw); const struct pll_vco *vco; const struct pll_vco_data *data; u32 l, alpha_width = pll_alpha_width(pll); u64 a; unsigned long rrate; rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); if (rrate != rate) { pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n"); return -EINVAL; } rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); vco = alpha_pll_find_vco(pll, rate); if (pll->vco_table && !vco) { pr_err("alpha pll not in a valid vco range\n"); return -EINVAL; } /* * For PLLs that do not support dynamic programming (dynamic_update * is not set), ensure PLL is off before changing rate. For * optimization reasons, assume no downstream clock is actively * using it. */ if (is_enabled(&pll->clkr.hw) && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) hw->init->ops->disable(hw); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); if (alpha_width > ALPHA_BITWIDTH) Loading @@ -696,9 +810,31 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, vco->val << PLL_VCO_SHIFT); } data = find_vco_data(pll->vco_data, rate, pll->num_vco_data); if (data) { if (data->freq == rate) regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_POST_DIV_MASK(pll_postdiv) << PLL_POST_DIV_SHIFT, data->post_div_val << PLL_POST_DIV_SHIFT); else regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_POST_DIV_MASK(pll_postdiv) << PLL_POST_DIV_SHIFT, 0x0 << PLL_VCO_SHIFT); } regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); if (is_enabled(&pll->clkr.hw) && (pll->flags & SUPPORTS_DYNAMIC_UPDATE)) clk_alpha_pll_dynamic_update(pll); if (is_enabled(&pll->clkr.hw) && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) hw->init->ops->enable(hw); return clk_alpha_pll_update_latch(pll, is_enabled); } Loading @@ -724,6 +860,9 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, u64 a; unsigned long min_freq, max_freq; if (rate < pll->min_supported_freq) return pll->min_supported_freq; rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width); if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) return rate; Loading Loading @@ -1603,12 +1742,16 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) int size, i, val; static struct clk_register_data data[] = { {"PLL_MODE", 0x0}, {"PLL_L_VAL", 0x4}, {"PLL_ALPHA_VAL", 0x8}, {"PLL_ALPHA_VAL_U", 0xC}, {"PLL_USER_CTL", 0x10}, {"PLL_CONFIG_CTL", 0x18}, {"PLL_MODE", PLL_OFF_MODE}, {"PLL_L_VAL", PLL_OFF_L_VAL}, {"PLL_ALPHA_VAL", PLL_OFF_ALPHA_VAL}, {"PLL_ALPHA_VAL_U", PLL_OFF_ALPHA_VAL_U}, {"PLL_TEST_CTL", PLL_OFF_TEST_CTL}, {"PLL_TEST_CTL_U", PLL_OFF_TEST_CTL_U}, {"PLL_USER_CTL", PLL_OFF_USER_CTL}, {"PLL_USER_CTL_U", PLL_OFF_USER_CTL_U}, {"PLL_CONFIG_CTL", PLL_OFF_CONFIG_CTL}, {"PLL_STATUS", PLL_OFF_STATUS}, }; static struct clk_register_data data1[] = { Loading @@ -1618,12 +1761,13 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->offset + data[i].offset, &val); regmap_read(pll->clkr.regmap, pll->offset + pll->regs[data[i].offset], &val); seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); } regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val); regmap_read(pll->clkr.regmap, pll->offset + pll->regs[data[0].offset], &val); if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + Loading Loading @@ -4110,3 +4254,200 @@ const struct clk_ops clk_agera_pll_ops = { #endif }; EXPORT_SYMBOL(clk_agera_pll_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)) { if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) clk_alpha_pll_dynamic_update(pll); else 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); if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) return clk_alpha_pll_dynamic_update(pll); else 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 = { .prepare = clk_prepare_regmap, .unprepare = clk_unprepare_regmap, .pre_rate_change = clk_pre_change_regmap, .post_rate_change = clk_post_change_regmap, .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, .init = clk_alpha_pll_init, .debug_init = clk_common_debug_init, #ifdef CONFIG_COMMON_CLK_QCOM_DEBUG .list_rate_vdd_level = clk_list_rate_vdd_level, #endif }; drivers/clk/qcom/clk-alpha-pll.h +20 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,11 @@ enum { extern const u8 clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_MAX][PLL_OFF_MAX_REGS]; struct pll_vco_data { unsigned long freq; u8 post_div_val; }; struct pll_vco { unsigned long min_freq; unsigned long max_freq; Loading @@ -65,6 +70,7 @@ struct pll_vco { * @offset: base address of registers * @vco_table: array of VCO settings * @regs: alpha pll register map (see @clk_alpha_pll_regs) * @vco_data: array of VCO data settings like post div * @clkr: regmap clock handle */ struct clk_alpha_pll { Loading @@ -73,13 +79,24 @@ struct clk_alpha_pll { struct alpha_pll_config *config; const struct pll_vco *vco_table; size_t num_vco; const struct pll_vco_data *vco_data; size_t num_vco_data; #define SUPPORTS_OFFLINE_REQ BIT(0) #define SUPPORTS_FSM_MODE BIT(2) /* * Some PLLs support dynamically updating their rate without disabling * the PLL first. Set this flag to enable this support. */ #define SUPPORTS_DYNAMIC_UPDATE BIT(3) #define SUPPORTS_FSM_LEGACY_MODE BIT(4) #define SUPPORTS_SLEW BIT(4) u8 flags; struct clk_regmap clkr; unsigned long min_supported_freq; }; /** Loading Loading @@ -127,6 +144,8 @@ struct alpha_pll_config { u32 pre_div_mask; u32 post_div_val; u32 post_div_mask; u32 test_ctl_mask; u32 test_ctl_hi_mask; u32 vco_val; u32 vco_mask; const u32 *custom_reg_offset; Loading Loading @@ -155,6 +174,7 @@ extern const struct clk_ops clk_alpha_pll_zonda_5lpe_ops; extern const struct clk_ops clk_alpha_pll_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_fixed_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_postdiv_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_slew_ops; extern const struct clk_ops clk_trion_fixed_pll_ops; extern const struct clk_ops clk_trion_pll_postdiv_ops; Loading Loading
drivers/clk/qcom/clk-alpha-pll.c +373 −32 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/delay.h> #include <linux/sched/clock.h> #include "clk-alpha-pll.h" #include "clk-debug.h" Loading @@ -23,6 +24,7 @@ # define PLL_LOCK_COUNT_MASK 0x3f # define PLL_BIAS_COUNT_SHIFT 14 # define PLL_BIAS_COUNT_MASK 0x3f #define PLL_LATCH_INTERFACE BIT(11) # define PLL_VOTE_FSM_ENA BIT(20) # define PLL_FSM_ENA BIT(20) # define PLL_VOTE_FSM_RESET BIT(21) Loading Loading @@ -291,12 +293,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, u32 val; int count; int ret; u64 time; const char *name = clk_hw_get_name(&pll->clkr.hw); ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; time = sched_clock(); for (count = 100; count > 0; count--) { ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) Loading @@ -308,6 +313,9 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, udelay(1); } time = sched_clock() - time; pr_err("PLL lock bit detection total wait time: %lld ns\n", time); WARN(1, "%s failed to %s!\n", name, action); return -ETIMEDOUT; Loading Loading @@ -337,14 +345,21 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, #define wait_for_pll_update_ack_clear(pll) \ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear") #define wait_for_pll_latch_ack(pll) \ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "latch ack") void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config) { u32 val, mask; if (config->l) regmap_write(regmap, PLL_L_VAL(pll), config->l); if (config->alpha) regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha); regmap_write(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); if (config->config_ctl_val) regmap_write(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); if (pll_has_64bit_config(pll)) regmap_write(regmap, PLL_CONFIG_CTL_U(pll), Loading @@ -353,25 +368,60 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, if (pll_alpha_width(pll) > 32) regmap_write(regmap, PLL_ALPHA_VAL_U(pll), config->alpha_hi); if (config->main_output_mask || config->aux_output_mask || config->aux2_output_mask || config->early_output_mask || config->pre_div_val || config->vco_val || config->alpha_en_mask) { val = config->main_output_mask; val |= config->aux_output_mask; val |= config->aux2_output_mask; val |= config->early_output_mask; val |= config->pre_div_val; val |= config->post_div_val; val |= config->vco_val; val |= config->alpha_en_mask; val |= config->alpha_mode_mask; mask = config->main_output_mask; mask |= config->aux_output_mask; mask |= config->aux2_output_mask; mask |= config->early_output_mask; mask |= config->pre_div_mask; mask |= config->post_div_mask; mask |= config->vco_mask; mask |= config->alpha_en_mask; regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val); } if (config->post_div_mask) { mask = config->post_div_mask; val = config->post_div_val; regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val); } /* Do not bypass the latch interface */ if (pll->flags & SUPPORTS_SLEW) regmap_update_bits(regmap, PLL_USER_CTL_U(pll), PLL_LATCH_INTERFACE, (u32)~PLL_LATCH_INTERFACE); if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) { regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, PLL_UPDATE_BYPASS); } if (config->test_ctl_mask) { mask = config->test_ctl_mask; val = config->test_ctl_val; regmap_update_bits(regmap, PLL_TEST_CTL(pll), mask, val); } if (config->test_ctl_hi_mask) { mask = config->test_ctl_hi_mask; val = config->test_ctl_hi_val; regmap_update_bits(regmap, PLL_TEST_CTL_U(pll), mask, val); } if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, PLL_UPDATE_BYPASS); if (pll->flags & SUPPORTS_FSM_MODE) qcom_pll_set_fsm_mode(regmap, PLL_MODE(pll), 6, 0); Loading Loading @@ -610,6 +660,48 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return alpha_pll_calc_rate(prate, l, a, alpha_width); } static int clk_alpha_pll_dynamic_update(struct clk_alpha_pll *pll) { int ret; /* Latch the input to the PLL */ regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, PLL_UPDATE); /* Wait for 2 reference cycle before checking ACK bit */ udelay(1); ret = wait_for_pll_latch_ack(pll); if (ret) return ret; /* Return latch input to 0 */ regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, (u32)~PLL_UPDATE); ret = wait_for_pll_enable_lock(pll); if (ret) return ret; return 0; } static const struct pll_vco_data *find_vco_data(const struct pll_vco_data *data, unsigned long rate, size_t size) { int i; if (!data) return NULL; for (i = 0; i < size; i++) { if (rate == data[i].freq) return &data[i]; } return &data[i - 1]; } static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll) { Loading Loading @@ -644,6 +736,9 @@ static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll) return ret; } if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) ret = wait_for_pll_enable_lock(pll); else ret = wait_for_pll_update_ack_clear(pll); if (ret) return ret; Loading @@ -669,17 +764,36 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, int (*is_enabled)(struct clk_hw *)) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); struct clk_alpha_pll_postdiv *pll_postdiv = to_clk_alpha_pll_postdiv(hw); const struct pll_vco *vco; const struct pll_vco_data *data; u32 l, alpha_width = pll_alpha_width(pll); u64 a; unsigned long rrate; rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); if (rrate != rate) { pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n"); return -EINVAL; } rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); vco = alpha_pll_find_vco(pll, rate); if (pll->vco_table && !vco) { pr_err("alpha pll not in a valid vco range\n"); return -EINVAL; } /* * For PLLs that do not support dynamic programming (dynamic_update * is not set), ensure PLL is off before changing rate. For * optimization reasons, assume no downstream clock is actively * using it. */ if (is_enabled(&pll->clkr.hw) && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) hw->init->ops->disable(hw); regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); if (alpha_width > ALPHA_BITWIDTH) Loading @@ -696,9 +810,31 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, vco->val << PLL_VCO_SHIFT); } data = find_vco_data(pll->vco_data, rate, pll->num_vco_data); if (data) { if (data->freq == rate) regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_POST_DIV_MASK(pll_postdiv) << PLL_POST_DIV_SHIFT, data->post_div_val << PLL_POST_DIV_SHIFT); else regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_POST_DIV_MASK(pll_postdiv) << PLL_POST_DIV_SHIFT, 0x0 << PLL_VCO_SHIFT); } regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); if (is_enabled(&pll->clkr.hw) && (pll->flags & SUPPORTS_DYNAMIC_UPDATE)) clk_alpha_pll_dynamic_update(pll); if (is_enabled(&pll->clkr.hw) && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) hw->init->ops->enable(hw); return clk_alpha_pll_update_latch(pll, is_enabled); } Loading @@ -724,6 +860,9 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, u64 a; unsigned long min_freq, max_freq; if (rate < pll->min_supported_freq) return pll->min_supported_freq; rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width); if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) return rate; Loading Loading @@ -1603,12 +1742,16 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) int size, i, val; static struct clk_register_data data[] = { {"PLL_MODE", 0x0}, {"PLL_L_VAL", 0x4}, {"PLL_ALPHA_VAL", 0x8}, {"PLL_ALPHA_VAL_U", 0xC}, {"PLL_USER_CTL", 0x10}, {"PLL_CONFIG_CTL", 0x18}, {"PLL_MODE", PLL_OFF_MODE}, {"PLL_L_VAL", PLL_OFF_L_VAL}, {"PLL_ALPHA_VAL", PLL_OFF_ALPHA_VAL}, {"PLL_ALPHA_VAL_U", PLL_OFF_ALPHA_VAL_U}, {"PLL_TEST_CTL", PLL_OFF_TEST_CTL}, {"PLL_TEST_CTL_U", PLL_OFF_TEST_CTL_U}, {"PLL_USER_CTL", PLL_OFF_USER_CTL}, {"PLL_USER_CTL_U", PLL_OFF_USER_CTL_U}, {"PLL_CONFIG_CTL", PLL_OFF_CONFIG_CTL}, {"PLL_STATUS", PLL_OFF_STATUS}, }; static struct clk_register_data data1[] = { Loading @@ -1618,12 +1761,13 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->offset + data[i].offset, &val); regmap_read(pll->clkr.regmap, pll->offset + pll->regs[data[i].offset], &val); seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); } regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val); regmap_read(pll->clkr.regmap, pll->offset + pll->regs[data[0].offset], &val); if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + Loading Loading @@ -4110,3 +4254,200 @@ const struct clk_ops clk_agera_pll_ops = { #endif }; EXPORT_SYMBOL(clk_agera_pll_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)) { if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) clk_alpha_pll_dynamic_update(pll); else 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); if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) return clk_alpha_pll_dynamic_update(pll); else 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 = { .prepare = clk_prepare_regmap, .unprepare = clk_unprepare_regmap, .pre_rate_change = clk_pre_change_regmap, .post_rate_change = clk_post_change_regmap, .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, .init = clk_alpha_pll_init, .debug_init = clk_common_debug_init, #ifdef CONFIG_COMMON_CLK_QCOM_DEBUG .list_rate_vdd_level = clk_list_rate_vdd_level, #endif };
drivers/clk/qcom/clk-alpha-pll.h +20 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,11 @@ enum { extern const u8 clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_MAX][PLL_OFF_MAX_REGS]; struct pll_vco_data { unsigned long freq; u8 post_div_val; }; struct pll_vco { unsigned long min_freq; unsigned long max_freq; Loading @@ -65,6 +70,7 @@ struct pll_vco { * @offset: base address of registers * @vco_table: array of VCO settings * @regs: alpha pll register map (see @clk_alpha_pll_regs) * @vco_data: array of VCO data settings like post div * @clkr: regmap clock handle */ struct clk_alpha_pll { Loading @@ -73,13 +79,24 @@ struct clk_alpha_pll { struct alpha_pll_config *config; const struct pll_vco *vco_table; size_t num_vco; const struct pll_vco_data *vco_data; size_t num_vco_data; #define SUPPORTS_OFFLINE_REQ BIT(0) #define SUPPORTS_FSM_MODE BIT(2) /* * Some PLLs support dynamically updating their rate without disabling * the PLL first. Set this flag to enable this support. */ #define SUPPORTS_DYNAMIC_UPDATE BIT(3) #define SUPPORTS_FSM_LEGACY_MODE BIT(4) #define SUPPORTS_SLEW BIT(4) u8 flags; struct clk_regmap clkr; unsigned long min_supported_freq; }; /** Loading Loading @@ -127,6 +144,8 @@ struct alpha_pll_config { u32 pre_div_mask; u32 post_div_val; u32 post_div_mask; u32 test_ctl_mask; u32 test_ctl_hi_mask; u32 vco_val; u32 vco_mask; const u32 *custom_reg_offset; Loading Loading @@ -155,6 +174,7 @@ extern const struct clk_ops clk_alpha_pll_zonda_5lpe_ops; extern const struct clk_ops clk_alpha_pll_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_fixed_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_postdiv_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_slew_ops; extern const struct clk_ops clk_trion_fixed_pll_ops; extern const struct clk_ops clk_trion_pll_postdiv_ops; Loading