diff options
Diffstat (limited to 'drivers/clk/qcom/clk-alpha-pll.c')
-rw-r--r-- | drivers/clk/qcom/clk-alpha-pll.c | 277 |
1 files changed, 223 insertions, 54 deletions
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 7c2936da9b14..9b2dfa08acb2 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -52,6 +52,7 @@ #define PLL_CONFIG_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL_U1]) #define PLL_TEST_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL]) #define PLL_TEST_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U]) +#define PLL_TEST_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U1]) #define PLL_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS]) #define PLL_OPMODE(p) ((p)->offset + (p)->regs[PLL_OFF_OPMODE]) #define PLL_FRAC(p) ((p)->offset + (p)->regs[PLL_OFF_FRAC]) @@ -116,6 +117,22 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_ALPHA_VAL] = 0x40, [PLL_OFF_CAL_VAL] = 0x44, }, + [CLK_ALPHA_PLL_TYPE_LUCID] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_CAL_L_VAL] = 0x08, + [PLL_OFF_USER_CTL] = 0x0c, + [PLL_OFF_USER_CTL_U] = 0x10, + [PLL_OFF_USER_CTL_U1] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL_U] = 0x1c, + [PLL_OFF_CONFIG_CTL_U1] = 0x20, + [PLL_OFF_TEST_CTL] = 0x24, + [PLL_OFF_TEST_CTL_U] = 0x28, + [PLL_OFF_TEST_CTL_U1] = 0x2c, + [PLL_OFF_STATUS] = 0x30, + [PLL_OFF_OPMODE] = 0x38, + [PLL_OFF_ALPHA_VAL] = 0x40, + }, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); @@ -134,15 +151,14 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); #define PLL_HUAYRA_N_MASK 0xff #define PLL_HUAYRA_ALPHA_WIDTH 16 -#define FABIA_OPMODE_STANDBY 0x0 -#define FABIA_OPMODE_RUN 0x1 - -#define FABIA_PLL_OUT_MASK 0x7 -#define FABIA_PLL_RATE_MARGIN 500 +#define PLL_STANDBY 0x0 +#define PLL_RUN 0x1 +#define PLL_OUT_MASK 0x7 +#define PLL_RATE_MARGIN 500 -#define TRION_PLL_STANDBY 0x0 -#define TRION_PLL_RUN 0x1 -#define TRION_PLL_OUT_MASK 0x7 +/* LUCID PLL specific settings and offsets */ +#define LUCID_PLL_CAL_VAL 0x44 +#define LUCID_PCAL_DONE BIT(26) #define pll_alpha_width(p) \ ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ @@ -544,7 +560,8 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, 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"); + pr_err("%s: alpha pll not in a valid vco range\n", + clk_hw_get_name(hw)); return -EINVAL; } @@ -722,7 +739,7 @@ static int alpha_pll_huayra_set_rate(struct clk_hw *hw, unsigned long rate, */ if (clk_alpha_pll_is_enabled(hw)) { if (cur_alpha != a) { - pr_err("clock needs to be gated %s\n", + pr_err("%s: clock needs to be gated\n", clk_hw_get_name(hw)); return -EBUSY; } @@ -765,7 +782,7 @@ static int trion_pll_is_enabled(struct clk_alpha_pll *pll, if (ret) return 0; - return ((opmode_regval & TRION_PLL_RUN) && (mode_regval & PLL_OUTCTRL)); + return ((opmode_regval & PLL_RUN) && (mode_regval & PLL_OUTCTRL)); } static int clk_trion_pll_is_enabled(struct clk_hw *hw) @@ -795,7 +812,7 @@ static int clk_trion_pll_enable(struct clk_hw *hw) } /* Set operation mode to RUN */ - regmap_write(regmap, PLL_OPMODE(pll), TRION_PLL_RUN); + regmap_write(regmap, PLL_OPMODE(pll), PLL_RUN); ret = wait_for_pll_enable_lock(pll); if (ret) @@ -803,7 +820,7 @@ static int clk_trion_pll_enable(struct clk_hw *hw) /* Enable the PLL outputs */ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), - TRION_PLL_OUT_MASK, TRION_PLL_OUT_MASK); + PLL_OUT_MASK, PLL_OUT_MASK); if (ret) return ret; @@ -836,12 +853,12 @@ static void clk_trion_pll_disable(struct clk_hw *hw) /* Disable the PLL outputs */ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), - TRION_PLL_OUT_MASK, 0); + PLL_OUT_MASK, 0); if (ret) return; /* Place the PLL mode in STANDBY */ - regmap_write(regmap, PLL_OPMODE(pll), TRION_PLL_STANDBY); + regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); } @@ -849,33 +866,12 @@ static unsigned long clk_trion_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); - struct regmap *regmap = pll->clkr.regmap; - u32 l, frac; - u64 prate = parent_rate; - - regmap_read(regmap, PLL_L_VAL(pll), &l); - regmap_read(regmap, PLL_ALPHA_VAL(pll), &frac); - - return alpha_pll_calc_rate(prate, l, frac, ALPHA_REG_16BIT_WIDTH); -} - -static long clk_trion_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); - unsigned long min_freq, max_freq; - u32 l; - u64 a; - - rate = alpha_pll_round_rate(rate, *prate, - &l, &a, ALPHA_REG_16BIT_WIDTH); - if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) - return rate; + u32 l, frac, alpha_width = pll_alpha_width(pll); - min_freq = pll->vco_table[0].min_freq; - max_freq = pll->vco_table[pll->num_vco - 1].max_freq; + regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l); + regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &frac); - return clamp(rate, min_freq, max_freq); + return alpha_pll_calc_rate(parent_rate, l, frac, alpha_width); } const struct clk_ops clk_alpha_pll_fixed_ops = { @@ -921,7 +917,7 @@ const struct clk_ops clk_trion_fixed_pll_ops = { .disable = clk_trion_pll_disable, .is_enabled = clk_trion_pll_is_enabled, .recalc_rate = clk_trion_pll_recalc_rate, - .round_rate = clk_trion_pll_round_rate, + .round_rate = clk_alpha_pll_round_rate, }; EXPORT_SYMBOL_GPL(clk_trion_fixed_pll_ops); @@ -1088,14 +1084,14 @@ static int alpha_pll_fabia_enable(struct clk_hw *hw) return ret; /* Skip If PLL is already running */ - if ((opmode_val & FABIA_OPMODE_RUN) && (val & PLL_OUTCTRL)) + if ((opmode_val & PLL_RUN) && (val & PLL_OUTCTRL)) return 0; ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); if (ret) return ret; - ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY); + ret = regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); if (ret) return ret; @@ -1104,7 +1100,7 @@ static int alpha_pll_fabia_enable(struct clk_hw *hw) if (ret) return ret; - ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_RUN); + ret = regmap_write(regmap, PLL_OPMODE(pll), PLL_RUN); if (ret) return ret; @@ -1113,7 +1109,7 @@ static int alpha_pll_fabia_enable(struct clk_hw *hw) return ret; ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), - FABIA_PLL_OUT_MASK, FABIA_PLL_OUT_MASK); + PLL_OUT_MASK, PLL_OUT_MASK); if (ret) return ret; @@ -1143,13 +1139,12 @@ static void alpha_pll_fabia_disable(struct clk_hw *hw) return; /* Disable main outputs */ - ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), FABIA_PLL_OUT_MASK, - 0); + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), PLL_OUT_MASK, 0); if (ret) return; /* Place the PLL in STANDBY */ - regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY); + regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); } static unsigned long alpha_pll_fabia_recalc_rate(struct clk_hw *hw, @@ -1170,7 +1165,7 @@ static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); u32 l, alpha_width = pll_alpha_width(pll); u64 a; - unsigned long rrate; + unsigned long rrate, max = rate + PLL_RATE_MARGIN; rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); @@ -1178,8 +1173,9 @@ static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate, * 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 + FABIA_PLL_RATE_MARGIN) || rrate < rate) { - pr_err("Call set rate on the PLL with rounded rates!\n"); + 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; } @@ -1196,6 +1192,7 @@ static int alpha_pll_fabia_prepare(struct clk_hw *hw) struct clk_hw *parent_hw; unsigned long cal_freq, rrate; u32 cal_l, val, alpha_width = pll_alpha_width(pll); + const char *name = clk_hw_get_name(hw); u64 a; int ret; @@ -1210,7 +1207,7 @@ static int alpha_pll_fabia_prepare(struct clk_hw *hw) vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw)); if (!vco) { - pr_err("alpha pll: not in a valid vco range\n"); + pr_err("%s: alpha pll not in a valid vco range\n", name); return -EINVAL; } @@ -1227,7 +1224,7 @@ static int alpha_pll_fabia_prepare(struct clk_hw *hw) * Due to a limited number of bits for fractional rate programming, the * rounded up rate could be marginally higher than the requested rate. */ - if (rrate > (cal_freq + FABIA_PLL_RATE_MARGIN) || rrate < cal_freq) + if (rrate > (cal_freq + PLL_RATE_MARGIN) || rrate < cal_freq) return -EINVAL; /* Setup PLL for calibration frequency */ @@ -1236,7 +1233,7 @@ static int alpha_pll_fabia_prepare(struct clk_hw *hw) /* Bringup the PLL at calibration frequency */ ret = clk_alpha_pll_enable(hw); if (ret) { - pr_err("alpha pll calibration failed\n"); + pr_err("%s: alpha pll calibration failed\n", name); return ret; } @@ -1394,3 +1391,175 @@ const struct clk_ops clk_alpha_pll_postdiv_fabia_ops = { .set_rate = clk_alpha_pll_postdiv_fabia_set_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_fabia_ops); + +/** + * clk_lucid_pll_configure - configure the lucid pll + * + * @pll: clk alpha pll + * @regmap: register map + * @config: configuration to apply for pll + */ +void clk_lucid_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); + + regmap_write(regmap, PLL_CAL_L_VAL(pll), LUCID_PLL_CAL_VAL); + + if (config->alpha) + regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha); + + 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->config_ctl_hi1_val) + regmap_write(regmap, PLL_CONFIG_CTL_U1(pll), + config->config_ctl_hi1_val); + + if (config->user_ctl_val) + regmap_write(regmap, PLL_USER_CTL(pll), + config->user_ctl_val); + + if (config->user_ctl_hi_val) + regmap_write(regmap, PLL_USER_CTL_U(pll), + config->user_ctl_hi_val); + + if (config->user_ctl_hi1_val) + regmap_write(regmap, PLL_USER_CTL_U1(pll), + config->user_ctl_hi1_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); + + if (config->test_ctl_hi1_val) + regmap_write(regmap, PLL_TEST_CTL_U1(pll), + config->test_ctl_hi1_val); + + regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, + PLL_UPDATE_BYPASS); + + /* Disable PLL output */ + regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); + + /* Set operation mode to OFF */ + regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); + + /* Place the PLL in STANDBY mode */ + regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); +} +EXPORT_SYMBOL_GPL(clk_lucid_pll_configure); + +/* + * The Lucid PLL requires a power-on self-calibration which happens when the + * PLL comes out of reset. Calibrate in case it is not completed. + */ +static int alpha_pll_lucid_prepare(struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 regval; + int ret; + + /* Return early if calibration is not needed. */ + regmap_read(pll->clkr.regmap, PLL_STATUS(pll), ®val); + if (regval & LUCID_PCAL_DONE) + return 0; + + /* On/off to calibrate */ + ret = clk_trion_pll_enable(hw); + if (!ret) + clk_trion_pll_disable(hw); + + return ret; +} + +static int alpha_pll_lucid_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 regval, l, alpha_width = pll_alpha_width(pll); + u64 a; + int ret; + + rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); + + /* + * Due to a 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("Call set rate on the PLL with rounded rates!\n"); + return -EINVAL; + } + + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); + + /* Latch the PLL input */ + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), + PLL_UPDATE, PLL_UPDATE); + if (ret) + return ret; + + /* Wait for 2 reference cycles before checking the ACK bit. */ + udelay(1); + regmap_read(pll->clkr.regmap, PLL_MODE(pll), ®val); + if (!(regval & ALPHA_PLL_ACK_LATCH)) { + pr_err("Lucid PLL latch failed. Output may be unstable!\n"); + return -EINVAL; + } + + /* Return the latch input to 0 */ + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), + PLL_UPDATE, 0); + if (ret) + return ret; + + 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_lucid_ops = { + .prepare = alpha_pll_lucid_prepare, + .enable = clk_trion_pll_enable, + .disable = clk_trion_pll_disable, + .is_enabled = clk_trion_pll_is_enabled, + .recalc_rate = clk_trion_pll_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, + .set_rate = alpha_pll_lucid_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_lucid_ops); + +const struct clk_ops clk_alpha_pll_fixed_lucid_ops = { + .enable = clk_trion_pll_enable, + .disable = clk_trion_pll_disable, + .is_enabled = clk_trion_pll_is_enabled, + .recalc_rate = clk_trion_pll_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_lucid_ops); + +const struct clk_ops clk_alpha_pll_postdiv_lucid_ops = { + .recalc_rate = clk_alpha_pll_postdiv_fabia_recalc_rate, + .round_rate = clk_alpha_pll_postdiv_fabia_round_rate, + .set_rate = clk_alpha_pll_postdiv_fabia_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_lucid_ops); |