diff options
Diffstat (limited to 'drivers/clk/qcom')
-rw-r--r-- | drivers/clk/qcom/Kconfig | 9 | ||||
-rw-r--r-- | drivers/clk/qcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-alpha-pll.c | 516 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-alpha-pll.h | 35 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-spmi-pmic-div.c | 302 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-ipq8074.c | 2 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-msm8916.c | 13 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-msm8994.c | 4 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-msm8996.c | 4 | ||||
-rw-r--r-- | drivers/clk/qcom/mmcc-msm8996.c | 16 |
10 files changed, 805 insertions, 97 deletions
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 9f6c278deead..20b5d6fd501d 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -196,3 +196,12 @@ config MSM_MMCC_8996 Support for the multimedia clock controller on msm8996 devices. Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. + +config SPMI_PMIC_CLKDIV + tristate "SPMI PMIC clkdiv Support" + depends on (COMMON_CLK_QCOM && SPMI) || COMPILE_TEST + help + This driver supports the clkdiv functionality on the Qualcomm + Technologies, Inc. SPMI PMIC. It configures the frequency of + clkdiv outputs of the PMIC. These clocks are typically wired + through alternate functions on GPIO pins. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 26410d31446b..602af3841522 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -34,3 +34,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o +obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 47a1da3739ce..6d04cd96482a 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) @@ -32,35 +32,87 @@ # 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) -#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_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 # 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, + }, + [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, + }, + [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); /* * 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_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) + +#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) @@ -71,18 +123,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)) @@ -109,16 +160,30 @@ 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) { 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); + + if (pll_has_64bit_config(pll)) + 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; @@ -127,6 +192,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; @@ -136,20 +203,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 +224,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 +237,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 +256,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 +267,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 +291,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 +310,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 +322,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 +331,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 +343,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,23 +356,25 @@ 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) +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; @@ -327,14 +389,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 * @@ -356,71 +419,138 @@ 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; + u32 alpha_width = pll_alpha_width(pll); - 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); - 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(pll), &low); + 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, - 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; - u32 l, off = pll->offset; + 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) { + if (pll->vco_table && !vco) { pr_err("alpha pll not in a valid vco range\n"); 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, - a & ALPHA_16BIT_MASK); - } else { - a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); - regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, 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); + + 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, off + PLL_USER_CTL, - 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); - regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN, - PLL_ALPHA_EN); + return clk_alpha_pll_update_latch(pll, is_enabled); +} - return 0; +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, 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); - if (alpha_pll_find_vco(pll, rate)) + rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width); + if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) return rate; min_freq = pll->vco_table[0].min_freq; @@ -429,6 +559,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, @@ -439,13 +721,23 @@ 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, .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); @@ -455,10 +747,10 @@ 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; + ctl &= PLL_POST_DIV_MASK(pll); return parent_rate >> fls(ctl); } @@ -472,16 +764,48 @@ 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; - return divider_round_rate(hw, rate, prate, clk_alpha_div_table, + if (pll->width == 2) + table = clk_alpha_2bit_div_table; + else + table = clk_alpha_div_table; + + return divider_round_rate(hw, rate, prate, table, 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) { @@ -491,8 +815,8 @@ 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, - PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT, + return regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), + PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT, div << PLL_POST_DIV_SHIFT); } @@ -502,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 d6e1ee2c7348..7593e8a56cf2 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -17,6 +17,30 @@ #include <linux/clk-provider.h> #include "clk-regmap.h" +/* Alpha PLL types */ +enum { + CLK_ALPHA_PLL_TYPE_DEFAULT, + CLK_ALPHA_PLL_TYPE_HUAYRA, + CLK_ALPHA_PLL_TYPE_BRAMMO, + 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,16 +51,18 @@ 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; #define SUPPORTS_OFFLINE_REQ BIT(0) -#define SUPPORTS_16BIT_ALPHA BIT(1) #define SUPPORTS_FSM_MODE BIT(2) +#define SUPPORTS_DYNAMIC_UPDATE BIT(3) u8 flags; struct clk_regmap clkr; @@ -45,12 +71,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; }; @@ -58,12 +86,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; @@ -75,6 +106,8 @@ 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; +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); diff --git a/drivers/clk/qcom/clk-spmi-pmic-div.c b/drivers/clk/qcom/clk-spmi-pmic-div.c new file mode 100644 index 000000000000..8672ab84746f --- /dev/null +++ b/drivers/clk/qcom/clk-spmi-pmic-div.c @@ -0,0 +1,302 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define REG_DIV_CTL1 0x43 +#define DIV_CTL1_DIV_FACTOR_MASK GENMASK(2, 0) + +#define REG_EN_CTL 0x46 +#define REG_EN_MASK BIT(7) + +struct clkdiv { + struct regmap *regmap; + u16 base; + spinlock_t lock; + + struct clk_hw hw; + unsigned int cxo_period_ns; +}; + +static inline struct clkdiv *to_clkdiv(struct clk_hw *hw) +{ + return container_of(hw, struct clkdiv, hw); +} + +static inline unsigned int div_factor_to_div(unsigned int div_factor) +{ + if (!div_factor) + div_factor = 1; + + return 1 << (div_factor - 1); +} + +static inline unsigned int div_to_div_factor(unsigned int div) +{ + return min(ilog2(div) + 1, 7); +} + +static bool is_spmi_pmic_clkdiv_enabled(struct clkdiv *clkdiv) +{ + unsigned int val = 0; + + regmap_read(clkdiv->regmap, clkdiv->base + REG_EN_CTL, &val); + + return val & REG_EN_MASK; +} + +static int +__spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv, bool enable, + unsigned int div_factor) +{ + int ret; + unsigned int ns = clkdiv->cxo_period_ns; + unsigned int div = div_factor_to_div(div_factor); + + ret = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_EN_CTL, + REG_EN_MASK, enable ? REG_EN_MASK : 0); + if (ret) + return ret; + + if (enable) + ndelay((2 + 3 * div) * ns); + else + ndelay(3 * div * ns); + + return 0; +} + +static int spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv, bool enable) +{ + unsigned int div_factor; + + regmap_read(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, &div_factor); + div_factor &= DIV_CTL1_DIV_FACTOR_MASK; + + return __spmi_pmic_clkdiv_set_enable_state(clkdiv, enable, div_factor); +} + +static int clk_spmi_pmic_div_enable(struct clk_hw *hw) +{ + struct clkdiv *clkdiv = to_clkdiv(hw); + unsigned long flags; + int ret; + + spin_lock_irqsave(&clkdiv->lock, flags); + ret = spmi_pmic_clkdiv_set_enable_state(clkdiv, true); + spin_unlock_irqrestore(&clkdiv->lock, flags); + + return ret; +} + +static void clk_spmi_pmic_div_disable(struct clk_hw *hw) +{ + struct clkdiv *clkdiv = to_clkdiv(hw); + unsigned long flags; + + spin_lock_irqsave(&clkdiv->lock, flags); + spmi_pmic_clkdiv_set_enable_state(clkdiv, false); + spin_unlock_irqrestore(&clkdiv->lock, flags); +} + +static long clk_spmi_pmic_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div, div_factor; + + div = DIV_ROUND_UP(*parent_rate, rate); + div_factor = div_to_div_factor(div); + div = div_factor_to_div(div_factor); + + return *parent_rate / div; +} + +static unsigned long +clk_spmi_pmic_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clkdiv *clkdiv = to_clkdiv(hw); + unsigned int div_factor; + + regmap_read(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, &div_factor); + div_factor &= DIV_CTL1_DIV_FACTOR_MASK; + + return parent_rate / div_factor_to_div(div_factor); +} + +static int clk_spmi_pmic_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clkdiv *clkdiv = to_clkdiv(hw); + unsigned int div_factor = div_to_div_factor(parent_rate / rate); + unsigned long flags; + bool enabled; + int ret; + + spin_lock_irqsave(&clkdiv->lock, flags); + enabled = is_spmi_pmic_clkdiv_enabled(clkdiv); + if (enabled) { + ret = spmi_pmic_clkdiv_set_enable_state(clkdiv, false); + if (ret) + goto unlock; + } + + ret = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_DIV_CTL1, + DIV_CTL1_DIV_FACTOR_MASK, div_factor); + if (ret) + goto unlock; + + if (enabled) + ret = __spmi_pmic_clkdiv_set_enable_state(clkdiv, true, + div_factor); + +unlock: + spin_unlock_irqrestore(&clkdiv->lock, flags); + + return ret; +} + +static const struct clk_ops clk_spmi_pmic_div_ops = { + .enable = clk_spmi_pmic_div_enable, + .disable = clk_spmi_pmic_div_disable, + .set_rate = clk_spmi_pmic_div_set_rate, + .recalc_rate = clk_spmi_pmic_div_recalc_rate, + .round_rate = clk_spmi_pmic_div_round_rate, +}; + +struct spmi_pmic_div_clk_cc { + int nclks; + struct clkdiv clks[]; +}; + +static struct clk_hw * +spmi_pmic_div_clk_hw_get(struct of_phandle_args *clkspec, void *data) +{ + struct spmi_pmic_div_clk_cc *cc = data; + int idx = clkspec->args[0] - 1; /* Start at 1 instead of 0 */ + + if (idx < 0 || idx >= cc->nclks) { + pr_err("%s: index value %u is invalid; allowed range [1, %d]\n", + __func__, clkspec->args[0], cc->nclks); + return ERR_PTR(-EINVAL); + } + + return &cc->clks[idx].hw; +} + +static int spmi_pmic_clkdiv_probe(struct platform_device *pdev) +{ + struct spmi_pmic_div_clk_cc *cc; + struct clk_init_data init = {}; + struct clkdiv *clkdiv; + struct clk *cxo; + struct regmap *regmap; + struct device *dev = &pdev->dev; + struct device_node *of_node = dev->of_node; + const char *parent_name; + int nclks, i, ret, cxo_hz; + char name[20]; + u32 start; + + ret = of_property_read_u32(of_node, "reg", &start); + if (ret < 0) { + dev_err(dev, "reg property reading failed\n"); + return ret; + } + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) { + dev_err(dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + ret = of_property_read_u32(of_node, "qcom,num-clkdivs", &nclks); + if (ret < 0) { + dev_err(dev, "qcom,num-clkdivs property reading failed, ret=%d\n", + ret); + return ret; + } + + if (!nclks) + return -EINVAL; + + cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*cc->clks) * nclks, + GFP_KERNEL); + if (!cc) + return -ENOMEM; + cc->nclks = nclks; + + cxo = clk_get(dev, "xo"); + if (IS_ERR(cxo)) { + ret = PTR_ERR(cxo); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get xo clock\n"); + return ret; + } + cxo_hz = clk_get_rate(cxo); + clk_put(cxo); + + parent_name = of_clk_get_parent_name(of_node, 0); + if (!parent_name) { + dev_err(dev, "missing parent clock\n"); + return -ENODEV; + } + + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &clk_spmi_pmic_div_ops; + + for (i = 0, clkdiv = cc->clks; i < nclks; i++) { + snprintf(name, sizeof(name), "div_clk%d", i + 1); + + spin_lock_init(&clkdiv[i].lock); + clkdiv[i].base = start + i * 0x100; + clkdiv[i].regmap = regmap; + clkdiv[i].cxo_period_ns = NSEC_PER_SEC / cxo_hz; + clkdiv[i].hw.init = &init; + + ret = devm_clk_hw_register(dev, &clkdiv[i].hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(dev, spmi_pmic_div_clk_hw_get, cc); +} + +static const struct of_device_id spmi_pmic_clkdiv_match_table[] = { + { .compatible = "qcom,spmi-clkdiv" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, spmi_pmic_clkdiv_match_table); + +static struct platform_driver spmi_pmic_clkdiv_driver = { + .driver = { + .name = "qcom,spmi-pmic-clkdiv", + .of_match_table = spmi_pmic_clkdiv_match_table, + }, + .probe = spmi_pmic_clkdiv_probe, +}; +module_platform_driver(spmi_pmic_clkdiv_driver); + +MODULE_DESCRIPTION("QCOM SPMI PMIC clkdiv driver"); +MODULE_LICENSE("GPL v2"); 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-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 3410ee68d4bc..d6c7f50ba86a 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -1259,20 +1259,25 @@ static struct clk_branch gcc_ultaudio_ahbfabric_ixfabric_lpm_clk = { }; static const struct freq_tbl ftbl_gcc_ultaudio_lpaif_i2s_clk[] = { + F(128000, P_XO, 10, 1, 15), F(256000, P_XO, 5, 1, 15), + F(384000, P_XO, 5, 1, 10), F(512000, P_XO, 5, 2, 15), + F(576000, P_XO, 5, 3, 20), F(705600, P_GPLL1, 16, 1, 80), F(768000, P_XO, 5, 1, 5), F(800000, P_XO, 5, 5, 24), - F(1024000, P_GPLL1, 14, 1, 63), + F(1024000, P_XO, 5, 4, 15), F(1152000, P_XO, 1, 3, 50), F(1411200, P_GPLL1, 16, 1, 40), F(1536000, P_XO, 1, 2, 25), F(1600000, P_XO, 12, 0, 0), - F(2048000, P_GPLL1, 9, 1, 49), + F(1728000, P_XO, 5, 9, 20), + F(2048000, P_XO, 5, 8, 15), + F(2304000, P_XO, 5, 3, 5), F(2400000, P_XO, 8, 0, 0), F(2822400, P_GPLL1, 16, 1, 20), - F(3072000, P_GPLL1, 14, 1, 21), + F(3072000, P_XO, 5, 4, 5), F(4096000, P_GPLL1, 9, 2, 49), F(4800000, P_XO, 4, 0, 0), F(5644800, P_GPLL1, 16, 1, 10), @@ -1431,6 +1436,7 @@ static struct clk_branch gcc_ultaudio_stc_xo_clk = { static const struct freq_tbl ftbl_codec_clk[] = { F(9600000, P_XO, 2, 0, 0), + F(12288000, P_XO, 1, 16, 25), F(19200000, P_XO, 1, 0, 0), F(11289600, P_EXT_MCLK, 1, 0, 0), { } @@ -1438,6 +1444,7 @@ static const struct freq_tbl ftbl_codec_clk[] = { static struct clk_rcg2 codec_digcodec_clk_src = { .cmd_rcgr = 0x1c09c, + .mnd_width = 8, .hid_width = 5, .parent_map = gcc_xo_gpll1_emclk_sleep_map, .freq_tbl = ftbl_codec_clk, 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", |