From 28d3f06e56baa9c6e2c8e1d9e5fd663138c08b92 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:40 +0530 Subject: clk: qcom: support for alpha pll properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alpha PLL is a generic name used for QCOM PLLs which uses L and Alpha values for configuring the integer and fractional part. QCOM SoCs use different types of Alpha PLLs for which basic software configuration part is common with following differences. 1. All these PLLs have the same basic registers like PLL_MODE, L_VAL, ALPHA_VAL but some of the register offsets are different between PLLs types. 2. The dynamic programming sequence is different in some of the Alpha PLLs 3. Some of the PLLs don’t have 64 bit config control, 64 bit user control, VCO configuration, etc. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 130 ++++++++++++++++++++------------------- drivers/clk/qcom/clk-alpha-pll.h | 26 ++++++++ drivers/clk/qcom/gcc-ipq8074.c | 2 + drivers/clk/qcom/gcc-msm8994.c | 4 ++ drivers/clk/qcom/gcc-msm8996.c | 4 ++ drivers/clk/qcom/mmcc-msm8996.c | 16 +++++ 6 files changed, 120 insertions(+), 62 deletions(-) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 47a1da3739ce..446a9a4eee18 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -20,7 +20,7 @@ #include "clk-alpha-pll.h" #include "common.h" -#define PLL_MODE 0x00 +#define PLL_MODE(p) ((p)->offset + 0x0) # define PLL_OUTCTRL BIT(0) # define PLL_BYPASSNL BIT(1) # define PLL_RESET_N BIT(2) @@ -36,24 +36,39 @@ # define PLL_ACTIVE_FLAG BIT(30) # define PLL_LOCK_DET BIT(31) -#define PLL_L_VAL 0x04 -#define PLL_ALPHA_VAL 0x08 -#define PLL_ALPHA_VAL_U 0x0c +#define PLL_L_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_L_VAL]) +#define PLL_ALPHA_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_ALPHA_VAL]) +#define PLL_ALPHA_VAL_U(p) ((p)->offset + (p)->regs[PLL_OFF_ALPHA_VAL_U]) -#define PLL_USER_CTL 0x10 +#define PLL_USER_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL]) # define PLL_POST_DIV_SHIFT 8 # define PLL_POST_DIV_MASK 0xf # define PLL_ALPHA_EN BIT(24) # define PLL_VCO_SHIFT 20 # define PLL_VCO_MASK 0x3 -#define PLL_USER_CTL_U 0x14 - -#define PLL_CONFIG_CTL 0x18 -#define PLL_CONFIG_CTL_U 0x20 -#define PLL_TEST_CTL 0x1c -#define PLL_TEST_CTL_U 0x20 -#define PLL_STATUS 0x24 +#define PLL_USER_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL_U]) + +#define PLL_CONFIG_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL]) +#define PLL_CONFIG_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL_U]) +#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_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS]) + +const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { + [CLK_ALPHA_PLL_TYPE_DEFAULT] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL_U] = 0x0c, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_USER_CTL_U] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_TEST_CTL] = 0x1c, + [PLL_OFF_TEST_CTL_U] = 0x20, + [PLL_OFF_STATUS] = 0x24, + }, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); /* * Even though 40 bits are present, use only 32 for ease of calculation. @@ -71,18 +86,17 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, const char *action) { - u32 val, off; + u32 val; int count; int ret; const char *name = clk_hw_get_name(&pll->clkr.hw); - off = pll->offset; - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; for (count = 100; count > 0; count--) { - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; if (inverse && !(val & mask)) @@ -113,12 +127,11 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config) { u32 val, mask; - u32 off = pll->offset; - regmap_write(regmap, off + PLL_L_VAL, config->l); - regmap_write(regmap, off + PLL_ALPHA_VAL, config->alpha); - regmap_write(regmap, off + PLL_CONFIG_CTL, config->config_ctl_val); - regmap_write(regmap, off + PLL_CONFIG_CTL_U, config->config_ctl_hi_val); + regmap_write(regmap, PLL_L_VAL(pll), config->l); + regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha); + regmap_write(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); + regmap_write(regmap, PLL_CONFIG_CTL_U(pll), config->config_ctl_hi_val); val = config->main_output_mask; val |= config->aux_output_mask; @@ -136,20 +149,19 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, mask |= config->post_div_mask; mask |= config->vco_mask; - regmap_update_bits(regmap, off + PLL_USER_CTL, mask, val); + regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val); if (pll->flags & SUPPORTS_FSM_MODE) - qcom_pll_set_fsm_mode(regmap, off + PLL_MODE, 6, 0); + qcom_pll_set_fsm_mode(regmap, PLL_MODE(pll), 6, 0); } static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw) { int ret; - u32 val, off; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 val; - off = pll->offset; - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; @@ -158,7 +170,7 @@ static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw) if (pll->flags & SUPPORTS_OFFLINE_REQ) val &= ~PLL_OFFLINE_REQ; - ret = regmap_write(pll->clkr.regmap, off + PLL_MODE, val); + ret = regmap_write(pll->clkr.regmap, PLL_MODE(pll), val); if (ret) return ret; @@ -171,16 +183,15 @@ static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw) static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw) { int ret; - u32 val, off; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 val; - off = pll->offset; - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return; if (pll->flags & SUPPORTS_OFFLINE_REQ) { - ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_OFFLINE_REQ, PLL_OFFLINE_REQ); if (ret) return; @@ -191,7 +202,7 @@ static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw) } /* Disable hwfsm */ - ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_FSM_ENA, 0); if (ret) return; @@ -202,11 +213,10 @@ static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw) static int pll_is_enabled(struct clk_hw *hw, u32 mask) { int ret; - u32 val, off; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 val; - off = pll->offset; - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; @@ -227,12 +237,10 @@ static int clk_alpha_pll_enable(struct clk_hw *hw) { int ret; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); - u32 val, mask, off; - - off = pll->offset; + u32 val, mask; mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL; - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; @@ -248,7 +256,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw) if ((val & mask) == mask) return 0; - ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_BYPASSNL, PLL_BYPASSNL); if (ret) return ret; @@ -260,7 +268,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw) mb(); udelay(5); - ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); if (ret) return ret; @@ -269,7 +277,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw) if (ret) return ret; - ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_OUTCTRL, PLL_OUTCTRL); /* Ensure that the write above goes through before returning. */ @@ -281,11 +289,9 @@ static void clk_alpha_pll_disable(struct clk_hw *hw) { int ret; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); - u32 val, mask, off; - - off = pll->offset; + u32 val, mask; - ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return; @@ -296,14 +302,14 @@ static void clk_alpha_pll_disable(struct clk_hw *hw) } mask = PLL_OUTCTRL; - regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, mask, 0); + regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), mask, 0); /* Delay of 2 output clock ticks required until output is disabled */ mb(); udelay(1); mask = PLL_RESET_N | PLL_BYPASSNL; - regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, mask, 0); + regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), mask, 0); } static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a) @@ -356,17 +362,16 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) u32 l, low, high, ctl; u64 a = 0, prate = parent_rate; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); - u32 off = pll->offset; - regmap_read(pll->clkr.regmap, off + PLL_L_VAL, &l); + regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l); - regmap_read(pll->clkr.regmap, off + PLL_USER_CTL, &ctl); + regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); if (ctl & PLL_ALPHA_EN) { - regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL, &low); + regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &low); if (pll->flags & SUPPORTS_16BIT_ALPHA) { a = low & ALPHA_16BIT_MASK; } else { - regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, + regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), &high); a = (u64)high << 32 | low; a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH; @@ -381,7 +386,7 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); const struct pll_vco *vco; - u32 l, off = pll->offset; + u32 l; u64 a; rate = alpha_pll_round_rate(rate, prate, &l, &a); @@ -391,22 +396,23 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; } - regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l); + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); if (pll->flags & SUPPORTS_16BIT_ALPHA) { - regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL, + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a & ALPHA_16BIT_MASK); } else { a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); - regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32); + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), + a >> 32); } - regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, + 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, off + PLL_USER_CTL, PLL_ALPHA_EN, - PLL_ALPHA_EN); + regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), + PLL_ALPHA_EN, PLL_ALPHA_EN); return 0; } @@ -455,7 +461,7 @@ clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); u32 ctl; - regmap_read(pll->clkr.regmap, pll->offset + PLL_USER_CTL, &ctl); + regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); ctl >>= PLL_POST_DIV_SHIFT; ctl &= PLL_POST_DIV_MASK; @@ -491,7 +497,7 @@ static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, /* 16 -> 0xf, 8 -> 0x7, 4 -> 0x3, 2 -> 0x1, 1 -> 0x0 */ div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1; - return regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_USER_CTL, + return regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT, div << PLL_POST_DIV_SHIFT); } diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index d6e1ee2c7348..ef7c75effbfe 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -17,6 +17,28 @@ #include #include "clk-regmap.h" +/* Alpha PLL types */ +enum { + CLK_ALPHA_PLL_TYPE_DEFAULT, + CLK_ALPHA_PLL_TYPE_MAX, +}; + +enum { + PLL_OFF_L_VAL, + PLL_OFF_ALPHA_VAL, + PLL_OFF_ALPHA_VAL_U, + PLL_OFF_USER_CTL, + PLL_OFF_USER_CTL_U, + PLL_OFF_CONFIG_CTL, + PLL_OFF_CONFIG_CTL_U, + PLL_OFF_TEST_CTL, + PLL_OFF_TEST_CTL_U, + PLL_OFF_STATUS, + PLL_OFF_MAX_REGS +}; + +extern const u8 clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_MAX][PLL_OFF_MAX_REGS]; + struct pll_vco { unsigned long min_freq; unsigned long max_freq; @@ -27,10 +49,12 @@ struct pll_vco { * struct clk_alpha_pll - phase locked loop (PLL) * @offset: base address of registers * @vco_table: array of VCO settings + * @regs: alpha pll register map (see @clk_alpha_pll_regs) * @clkr: regmap clock handle */ struct clk_alpha_pll { u32 offset; + const u8 *regs; const struct pll_vco *vco_table; size_t num_vco; @@ -45,12 +69,14 @@ struct clk_alpha_pll { /** * struct clk_alpha_pll_postdiv - phase locked loop (PLL) post-divider * @offset: base address of registers + * @regs: alpha pll register map (see @clk_alpha_pll_regs) * @width: width of post-divider * @clkr: regmap clock handle */ struct clk_alpha_pll_postdiv { u32 offset; u8 width; + const u8 *regs; struct clk_regmap clkr; }; diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index 0f735d37690f..ed2d00f55378 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -52,6 +52,7 @@ static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { static struct clk_alpha_pll gpll0_main = { .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr = { .enable_reg = 0x0b000, .enable_mask = BIT(0), @@ -82,6 +83,7 @@ static struct clk_fixed_factor gpll0_out_main_div2 = { static struct clk_alpha_pll_postdiv gpll0 = { .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data){ .name = "gpll0", .parent_names = (const char *[]){ diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c index 7983288d9141..1e38efc37180 100644 --- a/drivers/clk/qcom/gcc-msm8994.c +++ b/drivers/clk/qcom/gcc-msm8994.c @@ -73,6 +73,7 @@ static struct clk_fixed_factor xo = { static struct clk_alpha_pll gpll0_early = { .offset = 0x00000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr = { .enable_reg = 0x1480, .enable_mask = BIT(0), @@ -88,6 +89,7 @@ static struct clk_alpha_pll gpll0_early = { static struct clk_alpha_pll_postdiv gpll0 = { .offset = 0x00000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data) { .name = "gpll0", @@ -99,6 +101,7 @@ static struct clk_alpha_pll_postdiv gpll0 = { static struct clk_alpha_pll gpll4_early = { .offset = 0x1dc0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr = { .enable_reg = 0x1480, .enable_mask = BIT(4), @@ -114,6 +117,7 @@ static struct clk_alpha_pll gpll4_early = { static struct clk_alpha_pll_postdiv gpll4 = { .offset = 0x1dc0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data) { .name = "gpll4", diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 7ddec886fcd3..5d7451209206 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -227,6 +227,7 @@ static struct clk_fixed_factor xo = { static struct clk_alpha_pll gpll0_early = { .offset = 0x00000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr = { .enable_reg = 0x52000, .enable_mask = BIT(0), @@ -252,6 +253,7 @@ static struct clk_fixed_factor gpll0_early_div = { static struct clk_alpha_pll_postdiv gpll0 = { .offset = 0x00000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data){ .name = "gpll0", .parent_names = (const char *[]){ "gpll0_early" }, @@ -262,6 +264,7 @@ static struct clk_alpha_pll_postdiv gpll0 = { static struct clk_alpha_pll gpll4_early = { .offset = 0x77000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr = { .enable_reg = 0x52000, .enable_mask = BIT(4), @@ -276,6 +279,7 @@ static struct clk_alpha_pll gpll4_early = { static struct clk_alpha_pll_postdiv gpll4 = { .offset = 0x77000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data){ .name = "gpll4", .parent_names = (const char *[]){ "gpll4_early" }, diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index 352394d8fd8c..66a2fa4ec93c 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -267,6 +267,7 @@ static struct pll_vco mmpll_t_vco[] = { static struct clk_alpha_pll mmpll0_early = { .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_p_vco, .num_vco = ARRAY_SIZE(mmpll_p_vco), .clkr = { @@ -283,6 +284,7 @@ static struct clk_alpha_pll mmpll0_early = { static struct clk_alpha_pll_postdiv mmpll0 = { .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll0", @@ -295,6 +297,7 @@ static struct clk_alpha_pll_postdiv mmpll0 = { static struct clk_alpha_pll mmpll1_early = { .offset = 0x30, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_p_vco, .num_vco = ARRAY_SIZE(mmpll_p_vco), .clkr = { @@ -311,6 +314,7 @@ static struct clk_alpha_pll mmpll1_early = { static struct clk_alpha_pll_postdiv mmpll1 = { .offset = 0x30, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll1", @@ -323,6 +327,7 @@ static struct clk_alpha_pll_postdiv mmpll1 = { static struct clk_alpha_pll mmpll2_early = { .offset = 0x4100, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_gfx_vco, .num_vco = ARRAY_SIZE(mmpll_gfx_vco), .clkr.hw.init = &(struct clk_init_data){ @@ -335,6 +340,7 @@ static struct clk_alpha_pll mmpll2_early = { static struct clk_alpha_pll_postdiv mmpll2 = { .offset = 0x4100, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll2", @@ -347,6 +353,7 @@ static struct clk_alpha_pll_postdiv mmpll2 = { static struct clk_alpha_pll mmpll3_early = { .offset = 0x60, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_p_vco, .num_vco = ARRAY_SIZE(mmpll_p_vco), .clkr.hw.init = &(struct clk_init_data){ @@ -359,6 +366,7 @@ static struct clk_alpha_pll mmpll3_early = { static struct clk_alpha_pll_postdiv mmpll3 = { .offset = 0x60, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll3", @@ -371,6 +379,7 @@ static struct clk_alpha_pll_postdiv mmpll3 = { static struct clk_alpha_pll mmpll4_early = { .offset = 0x90, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_t_vco, .num_vco = ARRAY_SIZE(mmpll_t_vco), .clkr.hw.init = &(struct clk_init_data){ @@ -383,6 +392,7 @@ static struct clk_alpha_pll mmpll4_early = { static struct clk_alpha_pll_postdiv mmpll4 = { .offset = 0x90, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 2, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll4", @@ -395,6 +405,7 @@ static struct clk_alpha_pll_postdiv mmpll4 = { static struct clk_alpha_pll mmpll5_early = { .offset = 0xc0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_p_vco, .num_vco = ARRAY_SIZE(mmpll_p_vco), .clkr.hw.init = &(struct clk_init_data){ @@ -407,6 +418,7 @@ static struct clk_alpha_pll mmpll5_early = { static struct clk_alpha_pll_postdiv mmpll5 = { .offset = 0xc0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll5", @@ -419,6 +431,7 @@ static struct clk_alpha_pll_postdiv mmpll5 = { static struct clk_alpha_pll mmpll8_early = { .offset = 0x4130, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_gfx_vco, .num_vco = ARRAY_SIZE(mmpll_gfx_vco), .clkr.hw.init = &(struct clk_init_data){ @@ -431,6 +444,7 @@ static struct clk_alpha_pll mmpll8_early = { static struct clk_alpha_pll_postdiv mmpll8 = { .offset = 0x4130, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll8", @@ -443,6 +457,7 @@ static struct clk_alpha_pll_postdiv mmpll8 = { static struct clk_alpha_pll mmpll9_early = { .offset = 0x4200, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .vco_table = mmpll_t_vco, .num_vco = ARRAY_SIZE(mmpll_t_vco), .clkr.hw.init = &(struct clk_init_data){ @@ -455,6 +470,7 @@ static struct clk_alpha_pll mmpll9_early = { static struct clk_alpha_pll_postdiv mmpll9 = { .offset = 0x4200, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .width = 2, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll9", -- cgit From 1e859d3e03f0ac97d15e1952bddda4b29de1c71c Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:41 +0530 Subject: clk: qcom: fix 16 bit alpha support calculation The alpha value calculation has been written for 40-bit alpha values which doesn't work work properly for 16-bit ones. The alpha value is calculated on the basis of ALPHA_BITWIDTH to make the computation easy for 40 bit alpha. After calculating the 32 bit alpha, it is converted to 40 bit alpha by making lower bits zero. But if actual alpha register width is less than ALPHA_BITWIDTH, then the actual width can be used for calculation. This also means, during the 40 bit alpha pll set rate path, the lower alpha register is not configured Change the code to calculate the rate and register values from 'alpha_width' instead of hard-coding it so that it can work for the different widths that are supported. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 58 +++++++++++++++++++++++----------------- drivers/clk/qcom/clk-alpha-pll.h | 1 - 2 files changed, 34 insertions(+), 25 deletions(-) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 446a9a4eee18..3890f20be7d5 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -74,8 +74,13 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); * Even though 40 bits are present, use only 32 for ease of calculation. */ #define ALPHA_REG_BITWIDTH 40 -#define ALPHA_BITWIDTH 32 -#define ALPHA_16BIT_MASK 0xffff +#define ALPHA_REG_16BIT_WIDTH 16 +#define ALPHA_BITWIDTH 32U +#define ALPHA_SHIFT(w) min(w, ALPHA_BITWIDTH) + +#define pll_alpha_width(p) \ + ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ + ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH) #define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \ struct clk_alpha_pll, clkr) @@ -312,13 +317,15 @@ static void clk_alpha_pll_disable(struct clk_hw *hw) regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), mask, 0); } -static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a) +static unsigned long +alpha_pll_calc_rate(u64 prate, u32 l, u32 a, u32 alpha_width) { - return (prate * l) + ((prate * a) >> ALPHA_BITWIDTH); + return (prate * l) + ((prate * a) >> ALPHA_SHIFT(alpha_width)); } static unsigned long -alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a) +alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a, + u32 alpha_width) { u64 remainder; u64 quotient; @@ -333,14 +340,15 @@ alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a) } /* Upper ALPHA_BITWIDTH bits of Alpha */ - quotient = remainder << ALPHA_BITWIDTH; + quotient = remainder << ALPHA_SHIFT(alpha_width); + remainder = do_div(quotient, prate); if (remainder) quotient++; *a = quotient; - return alpha_pll_calc_rate(prate, *l, *a); + return alpha_pll_calc_rate(prate, *l, *a, alpha_width); } static const struct pll_vco * @@ -362,23 +370,26 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) u32 l, low, high, ctl; u64 a = 0, prate = parent_rate; struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 alpha_width = pll_alpha_width(pll); regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l); regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); if (ctl & PLL_ALPHA_EN) { regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &low); - if (pll->flags & SUPPORTS_16BIT_ALPHA) { - a = low & ALPHA_16BIT_MASK; - } else { + if (alpha_width > 32) { regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), &high); a = (u64)high << 32 | low; - a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH; + } else { + a = low & GENMASK(alpha_width - 1, 0); } + + if (alpha_width > ALPHA_BITWIDTH) + a >>= alpha_width - ALPHA_BITWIDTH; } - return alpha_pll_calc_rate(prate, l, a); + return alpha_pll_calc_rate(prate, l, a, alpha_width); } static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, @@ -386,10 +397,10 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); const struct pll_vco *vco; - u32 l; + u32 l, alpha_width = pll_alpha_width(pll); u64 a; - rate = alpha_pll_round_rate(rate, prate, &l, &a); + rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); vco = alpha_pll_find_vco(pll, rate); if (!vco) { pr_err("alpha pll not in a valid vco range\n"); @@ -398,14 +409,13 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); - if (pll->flags & SUPPORTS_16BIT_ALPHA) { - regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), - a & ALPHA_16BIT_MASK); - } else { - a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); - regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), - a >> 32); - } + if (alpha_width > ALPHA_BITWIDTH) + a <<= alpha_width - ALPHA_BITWIDTH; + + if (alpha_width > 32) + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL_U(pll), a >> 32); + + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_VCO_MASK << PLL_VCO_SHIFT, @@ -421,11 +431,11 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); - u32 l; + u32 l, alpha_width = pll_alpha_width(pll); u64 a; unsigned long min_freq, max_freq; - rate = alpha_pll_round_rate(rate, *prate, &l, &a); + rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width); if (alpha_pll_find_vco(pll, rate)) return rate; diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index ef7c75effbfe..18c0c3eff855 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -59,7 +59,6 @@ struct clk_alpha_pll { const struct pll_vco *vco_table; size_t num_vco; #define SUPPORTS_OFFLINE_REQ BIT(0) -#define SUPPORTS_16BIT_ALPHA BIT(1) #define SUPPORTS_FSM_MODE BIT(2) u8 flags; -- cgit From 26945e0a2341523170aa5b6ff4cc35e9ce1f7cf4 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:43 +0530 Subject: clk: qcom: flag for 64 bit CONFIG_CTL Some of the Alpha PLLs (like Spark and Brammo) don't have a CONFIG_CTL_U register. Add logic to detect when PLLs don't have this second config register and skip programming it during PLL initialization. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 3890f20be7d5..81cfd31f569a 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -82,6 +82,8 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH) +#define pll_has_64bit_config(p) ((PLL_CONFIG_CTL_U(p) - PLL_CONFIG_CTL(p)) == 4) + #define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \ struct clk_alpha_pll, clkr) @@ -136,7 +138,10 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, regmap_write(regmap, PLL_L_VAL(pll), config->l); regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha); regmap_write(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); - regmap_write(regmap, PLL_CONFIG_CTL_U(pll), config->config_ctl_hi_val); + + if (pll_has_64bit_config(pll)) + regmap_write(regmap, PLL_CONFIG_CTL_U(pll), + config->config_ctl_hi_val); val = config->main_output_mask; val |= config->aux_output_mask; -- cgit From c45ae598fc16aca8ca8778a9b85e41449e8e5182 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:44 +0530 Subject: clk: qcom: support for alpha mode configuration The current configuration does not fully configure PLL alpha mode and values so this patch 1. Configures PLL_ALPHA_VAL_U for PLL which supports 40 bit alpha. 2. Adds alpha enable and alpha mode configuration support. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 5 +++++ drivers/clk/qcom/clk-alpha-pll.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 81cfd31f569a..38ed1b83a094 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -143,6 +143,9 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, regmap_write(regmap, PLL_CONFIG_CTL_U(pll), config->config_ctl_hi_val); + if (pll_alpha_width(pll) > 32) + regmap_write(regmap, PLL_ALPHA_VAL_U(pll), config->alpha_hi); + val = config->main_output_mask; val |= config->aux_output_mask; val |= config->aux2_output_mask; @@ -150,6 +153,8 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, 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; diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 18c0c3eff855..c9c1f2f911b4 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -83,12 +83,15 @@ struct clk_alpha_pll_postdiv { struct alpha_pll_config { u32 l; u32 alpha; + u32 alpha_hi; u32 config_ctl_val; u32 config_ctl_hi_val; u32 main_output_mask; u32 aux_output_mask; u32 aux2_output_mask; u32 early_output_mask; + u32 alpha_en_mask; + u32 alpha_mode_mask; u32 pre_div_val; u32 pre_div_mask; u32 post_div_val; -- cgit From 472796defebbc5381d6c462adcd0a9a6fc9494e4 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:45 +0530 Subject: clk: qcom: support for dynamic updating the PLL Some of the Alpha PLLs support dynamic update in which the frequency can be changed dynamically without turning off the PLL. This dynamic update requires the following sequence: 1. Write the desired values to L_VAL and ALPHA_VAL registers 2. Toggle pll_latch_input from low to high 3. Wait for pll_ack_latch to transition from low to high The new L and alpha values have been latched. It may take some time for the PLL to fully settle with these new values. 4. Pull pll_latch_input low Signed-off-by: Rajendra Nayak Signed-off-by: Taniya Das Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 83 ++++++++++++++++++++++++++++++++++++++-- drivers/clk/qcom/clk-alpha-pll.h | 1 + 2 files changed, 80 insertions(+), 4 deletions(-) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 38ed1b83a094..2a4b0c9f5c4d 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -32,7 +32,10 @@ # define PLL_VOTE_FSM_ENA BIT(20) # define PLL_FSM_ENA BIT(20) # define PLL_VOTE_FSM_RESET BIT(21) +# define PLL_UPDATE BIT(22) +# define PLL_UPDATE_BYPASS BIT(23) # define PLL_OFFLINE_ACK BIT(28) +# define ALPHA_PLL_ACK_LATCH BIT(29) # define PLL_ACTIVE_FLAG BIT(30) # define PLL_LOCK_DET BIT(31) @@ -130,6 +133,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, #define wait_for_pll_offline(pll) \ wait_for_pll(pll, PLL_OFFLINE_ACK, 0, "offline") +#define wait_for_pll_update(pll) \ + wait_for_pll(pll, PLL_UPDATE, 1, "update") + +#define wait_for_pll_update_ack_set(pll) \ + wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "update_ack_set") + +#define wait_for_pll_update_ack_clear(pll) \ + wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear") + void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config) { @@ -402,8 +414,57 @@ 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_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long prate) +static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll, + int (*is_enabled)(struct clk_hw *)) +{ + int ret; + u32 mode; + + if (!is_enabled(&pll->clkr.hw) || + !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) + return 0; + + regmap_read(pll->clkr.regmap, PLL_MODE(pll), &mode); + + /* 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); + + /* + * PLL will latch the new L, Alpha and freq control word. + * PLL will respond by raising PLL_ACK_LATCH output when new programming + * has been latched in and PLL is being updated. When + * UPDATE_LOGIC_BYPASS bit is not set, PLL_UPDATE will be cleared + * automatically by hardware when PLL_ACK_LATCH is asserted by PLL. + */ + if (mode & PLL_UPDATE_BYPASS) { + ret = wait_for_pll_update_ack_set(pll); + if (ret) + return ret; + + regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll), PLL_UPDATE, 0); + } else { + ret = wait_for_pll_update(pll); + if (ret) + return ret; + } + + ret = wait_for_pll_update_ack_clear(pll); + if (ret) + return ret; + + /* Wait for PLL output to stabilize */ + udelay(10); + + return 0; +} + +static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate, + int (*is_enabled)(struct clk_hw *)) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); const struct pll_vco *vco; @@ -434,7 +495,21 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_ALPHA_EN, PLL_ALPHA_EN); - return 0; + return clk_alpha_pll_update_latch(pll, is_enabled); +} + +static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + return __clk_alpha_pll_set_rate(hw, rate, prate, + clk_alpha_pll_is_enabled); +} + +static int clk_alpha_pll_hwfsm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + return __clk_alpha_pll_set_rate(hw, rate, prate, + clk_alpha_pll_hwfsm_is_enabled); } static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -471,7 +546,7 @@ const struct clk_ops clk_alpha_pll_hwfsm_ops = { .is_enabled = clk_alpha_pll_hwfsm_is_enabled, .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, - .set_rate = clk_alpha_pll_set_rate, + .set_rate = clk_alpha_pll_hwfsm_set_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index c9c1f2f911b4..82fef8f1c9af 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -60,6 +60,7 @@ struct clk_alpha_pll { size_t num_vco; #define SUPPORTS_OFFLINE_REQ BIT(0) #define SUPPORTS_FSM_MODE BIT(2) +#define SUPPORTS_DYNAMIC_UPDATE BIT(3) u8 flags; struct clk_regmap clkr; -- cgit From 134b55b7e19f832fea8822ce8821b6e8733b8795 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:46 +0530 Subject: clk: qcom: support Huayra type Alpha PLL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Huayra type Alpha PLL has a 16 bit alpha value, and depending on the alpha_mode, the alpha value can be treated as M/N value or as a two’s compliment number. This PLL supports dynamic programming. Since the decoding of alpha val and dynamic programming are completely different from other Alpha PLLs we add separate functions for Huayra PLLs. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 192 ++++++++++++++++++++++++++++++++++++++- drivers/clk/qcom/clk-alpha-pll.h | 2 + 2 files changed, 189 insertions(+), 5 deletions(-) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 2a4b0c9f5c4d..6860f5b1db88 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -47,6 +47,7 @@ # define PLL_POST_DIV_SHIFT 8 # define PLL_POST_DIV_MASK 0xf # define PLL_ALPHA_EN BIT(24) +# define PLL_ALPHA_MODE BIT(25) # define PLL_VCO_SHIFT 20 # define PLL_VCO_MASK 0x3 @@ -70,6 +71,16 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_TEST_CTL_U] = 0x20, [PLL_OFF_STATUS] = 0x24, }, + [CLK_ALPHA_PLL_TYPE_HUAYRA] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_CONFIG_CTL] = 0x14, + [PLL_OFF_CONFIG_CTL_U] = 0x18, + [PLL_OFF_TEST_CTL] = 0x1c, + [PLL_OFF_TEST_CTL_U] = 0x20, + [PLL_OFF_STATUS] = 0x24, + }, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); @@ -81,6 +92,13 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); #define ALPHA_BITWIDTH 32U #define ALPHA_SHIFT(w) min(w, ALPHA_BITWIDTH) +#define PLL_HUAYRA_M_WIDTH 8 +#define PLL_HUAYRA_M_SHIFT 8 +#define PLL_HUAYRA_M_MASK 0xff +#define PLL_HUAYRA_N_SHIFT 0 +#define PLL_HUAYRA_N_MASK 0xff +#define PLL_HUAYRA_ALPHA_WIDTH 16 + #define pll_alpha_width(p) \ ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH) @@ -473,7 +491,7 @@ 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 (!vco) { + if (pll->vco_table && !vco) { pr_err("alpha pll not in a valid vco range\n"); return -EINVAL; } @@ -488,9 +506,11 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); - regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), - PLL_VCO_MASK << PLL_VCO_SHIFT, - vco->val << PLL_VCO_SHIFT); + if (vco) { + 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); @@ -521,7 +541,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long min_freq, max_freq; rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width); - if (alpha_pll_find_vco(pll, rate)) + if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) return rate; min_freq = pll->vco_table[0].min_freq; @@ -530,6 +550,158 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, return clamp(rate, min_freq, max_freq); } +static unsigned long +alpha_huayra_pll_calc_rate(u64 prate, u32 l, u32 a) +{ + /* + * a contains 16 bit alpha_val in two’s compliment number in the range + * of [-0.5, 0.5). + */ + if (a >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1)) + l -= 1; + + return (prate * l) + (prate * a >> PLL_HUAYRA_ALPHA_WIDTH); +} + +static unsigned long +alpha_huayra_pll_round_rate(unsigned long rate, unsigned long prate, + u32 *l, u32 *a) +{ + u64 remainder; + u64 quotient; + + quotient = rate; + remainder = do_div(quotient, prate); + *l = quotient; + + if (!remainder) { + *a = 0; + return rate; + } + + quotient = remainder << PLL_HUAYRA_ALPHA_WIDTH; + remainder = do_div(quotient, prate); + + if (remainder) + quotient++; + + /* + * alpha_val should be in two’s compliment number in the range + * of [-0.5, 0.5) so if quotient >= 0.5 then increment the l value + * since alpha value will be subtracted in this case. + */ + if (quotient >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1)) + *l += 1; + + *a = quotient; + return alpha_huayra_pll_calc_rate(prate, *l, *a); +} + +static unsigned long +alpha_pll_huayra_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + u64 rate = parent_rate, tmp; + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 l, alpha = 0, ctl, alpha_m, alpha_n; + + regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l); + regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); + + if (ctl & PLL_ALPHA_EN) { + regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &alpha); + /* + * Depending upon alpha_mode, it can be treated as M/N value or + * as a two’s compliment number. When alpha_mode=1, + * pll_alpha_val<15:8>=M and pll_apla_val<7:0>=N + * + * Fout=FIN*(L+(M/N)) + * + * M is a signed number (-128 to 127) and N is unsigned + * (0 to 255). M/N has to be within +/-0.5. + * + * When alpha_mode=0, it is a two’s compliment number in the + * range [-0.5, 0.5). + * + * Fout=FIN*(L+(alpha_val)/2^16) + * + * where alpha_val is two’s compliment number. + */ + if (!(ctl & PLL_ALPHA_MODE)) + return alpha_huayra_pll_calc_rate(rate, l, alpha); + + alpha_m = alpha >> PLL_HUAYRA_M_SHIFT & PLL_HUAYRA_M_MASK; + alpha_n = alpha >> PLL_HUAYRA_N_SHIFT & PLL_HUAYRA_N_MASK; + + rate *= l; + tmp = parent_rate; + if (alpha_m >= BIT(PLL_HUAYRA_M_WIDTH - 1)) { + alpha_m = BIT(PLL_HUAYRA_M_WIDTH) - alpha_m; + tmp *= alpha_m; + do_div(tmp, alpha_n); + rate -= tmp; + } else { + tmp *= alpha_m; + do_div(tmp, alpha_n); + rate += tmp; + } + + return rate; + } + + return alpha_huayra_pll_calc_rate(rate, l, alpha); +} + +static int alpha_pll_huayra_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 l, a, ctl, cur_alpha = 0; + + rate = alpha_huayra_pll_round_rate(rate, prate, &l, &a); + + regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); + + if (ctl & PLL_ALPHA_EN) + regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &cur_alpha); + + /* + * Huayra PLL supports PLL dynamic programming. User can change L_VAL, + * without having to go through the power on sequence. + */ + if (clk_alpha_pll_is_enabled(hw)) { + if (cur_alpha != a) { + pr_err("clock needs to be gated %s\n", + clk_hw_get_name(hw)); + return -EBUSY; + } + + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); + /* Ensure that the write above goes to detect L val change. */ + mb(); + return wait_for_pll_enable_lock(pll); + } + + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); + regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a); + + if (a == 0) + regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), + PLL_ALPHA_EN, 0x0); + else + regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), + PLL_ALPHA_EN | PLL_ALPHA_MODE, PLL_ALPHA_EN); + + return 0; +} + +static long alpha_pll_huayra_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u32 l, a; + + return alpha_huayra_pll_round_rate(rate, *prate, &l, &a); +} + const struct clk_ops clk_alpha_pll_ops = { .enable = clk_alpha_pll_enable, .disable = clk_alpha_pll_disable, @@ -540,6 +712,16 @@ const struct clk_ops clk_alpha_pll_ops = { }; EXPORT_SYMBOL_GPL(clk_alpha_pll_ops); +const struct clk_ops clk_alpha_pll_huayra_ops = { + .enable = clk_alpha_pll_enable, + .disable = clk_alpha_pll_disable, + .is_enabled = clk_alpha_pll_is_enabled, + .recalc_rate = alpha_pll_huayra_recalc_rate, + .round_rate = alpha_pll_huayra_round_rate, + .set_rate = alpha_pll_huayra_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_huayra_ops); + const struct clk_ops clk_alpha_pll_hwfsm_ops = { .enable = clk_alpha_pll_hwfsm_enable, .disable = clk_alpha_pll_hwfsm_disable, diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 82fef8f1c9af..667e9069d086 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -20,6 +20,7 @@ /* Alpha PLL types */ enum { CLK_ALPHA_PLL_TYPE_DEFAULT, + CLK_ALPHA_PLL_TYPE_HUAYRA, CLK_ALPHA_PLL_TYPE_MAX, }; @@ -104,6 +105,7 @@ struct alpha_pll_config { extern const struct clk_ops clk_alpha_pll_ops; extern const struct clk_ops clk_alpha_pll_hwfsm_ops; extern const struct clk_ops clk_alpha_pll_postdiv_ops; +extern const struct clk_ops clk_alpha_pll_huayra_ops; void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); -- cgit From c23e8a1f6016bd35599c35052d39bb9e879351ad Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:48 +0530 Subject: clk: qcom: support Brammo type Alpha PLL The Brammo type of Alpha PLL doesn't allow configuration of a VCO, but it does support dynamic update in which the frequency can be changed dynamically without turning off the PLL. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 9 +++++++++ drivers/clk/qcom/clk-alpha-pll.h | 1 + 2 files changed, 10 insertions(+) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 6860f5b1db88..3a38861a7944 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -81,6 +81,15 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_TEST_CTL_U] = 0x20, [PLL_OFF_STATUS] = 0x24, }, + [CLK_ALPHA_PLL_TYPE_BRAMMO] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL_U] = 0x0c, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_TEST_CTL] = 0x1c, + [PLL_OFF_STATUS] = 0x24, + }, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 667e9069d086..bcc6676a8e7a 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -21,6 +21,7 @@ enum { CLK_ALPHA_PLL_TYPE_DEFAULT, CLK_ALPHA_PLL_TYPE_HUAYRA, + CLK_ALPHA_PLL_TYPE_BRAMMO, CLK_ALPHA_PLL_TYPE_MAX, }; -- cgit From 1c3541145cbfd1cf631602bd7494dd03260e8533 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:49 +0530 Subject: clk: qcom: support for 2 bit PLL post divider Current PLL driver only supports 4 bit PLL post divider so modified the PLL divider operations to support 2 bit PLL post divider. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 3a38861a7944..2de66b98e997 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -45,7 +45,7 @@ #define PLL_USER_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL]) # define PLL_POST_DIV_SHIFT 8 -# define PLL_POST_DIV_MASK 0xf +# define PLL_POST_DIV_MASK(p) GENMASK((p)->width, 0) # define PLL_ALPHA_EN BIT(24) # define PLL_ALPHA_MODE BIT(25) # define PLL_VCO_SHIFT 20 @@ -750,7 +750,7 @@ clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); ctl >>= PLL_POST_DIV_SHIFT; - ctl &= PLL_POST_DIV_MASK; + ctl &= PLL_POST_DIV_MASK(pll); return parent_rate >> fls(ctl); } @@ -764,13 +764,26 @@ static const struct clk_div_table clk_alpha_div_table[] = { { } }; +static const struct clk_div_table clk_alpha_2bit_div_table[] = { + { 0x0, 1 }, + { 0x1, 2 }, + { 0x3, 4 }, + { } +}; + static long clk_alpha_pll_postdiv_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + const struct clk_div_table *table; + + if (pll->width == 2) + table = clk_alpha_2bit_div_table; + else + table = clk_alpha_div_table; - return divider_round_rate(hw, rate, prate, clk_alpha_div_table, + return divider_round_rate(hw, rate, prate, table, pll->width, CLK_DIVIDER_POWER_OF_TWO); } @@ -784,7 +797,7 @@ static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1; return regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), - PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT, + PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT, div << PLL_POST_DIV_SHIFT); } -- cgit From 23c68cc9683efbd08457f06fb5eacd7c8616dfae Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 28 Sep 2017 23:20:50 +0530 Subject: clk: qcom: add read-only alpha pll post divider operations Some of the divider settings are preconfigured and should not be changed by the clock framework during frequency change. This patch adds the read-only divider operation for QCOM alpha pll post divider which is equivalent to generic divider operations in 'commit 79c6ab509558 ("clk: divider: add CLK_DIVIDER_READ_ONLY flag")'. Signed-off-by: Abhishek Sahu Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 25 +++++++++++++++++++++++++ drivers/clk/qcom/clk-alpha-pll.h | 1 + 2 files changed, 26 insertions(+) (limited to 'drivers/clk/qcom') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 2de66b98e997..6d04cd96482a 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -787,6 +787,25 @@ clk_alpha_pll_postdiv_round_rate(struct clk_hw *hw, unsigned long rate, pll->width, CLK_DIVIDER_POWER_OF_TWO); } +static long +clk_alpha_pll_postdiv_round_ro_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + u32 ctl, div; + + regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl); + + ctl >>= PLL_POST_DIV_SHIFT; + ctl &= BIT(pll->width) - 1; + div = 1 << fls(ctl); + + if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) + *prate = clk_hw_round_rate(clk_hw_get_parent(hw), div * rate); + + return DIV_ROUND_UP_ULL((u64)*prate, div); +} + static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -807,3 +826,9 @@ const struct clk_ops clk_alpha_pll_postdiv_ops = { .set_rate = clk_alpha_pll_postdiv_set_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ops); + +const struct clk_ops clk_alpha_pll_postdiv_ro_ops = { + .round_rate = clk_alpha_pll_postdiv_round_ro_rate, + .recalc_rate = clk_alpha_pll_postdiv_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ro_ops); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index bcc6676a8e7a..7593e8a56cf2 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -107,6 +107,7 @@ extern const struct clk_ops clk_alpha_pll_ops; extern const struct clk_ops clk_alpha_pll_hwfsm_ops; extern const struct clk_ops clk_alpha_pll_postdiv_ops; extern const struct clk_ops clk_alpha_pll_huayra_ops; +extern const struct clk_ops clk_alpha_pll_postdiv_ro_ops; void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); -- cgit