diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-21 00:45:07 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-21 00:45:07 +0200 |
commit | a703d279c57e1bfe2b6536c3a17c1c498b416d24 (patch) | |
tree | 3b11cc5bf81720b3cd07b0c07d04d4724d65b972 /drivers/clk/qcom/clk-alpha-pll.c | |
parent | Merge tag 'vfio-v5.4-rc1' of git://github.com/awilliam/linux-vfio (diff) | |
parent | Merge branches 'clk-bulk-fix', 'clk-at91' and 'clk-sprd' into clk-next (diff) | |
download | linux-a703d279c57e1bfe2b6536c3a17c1c498b416d24.tar.xz linux-a703d279c57e1bfe2b6536c3a17c1c498b416d24.zip |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd:
"We have a small collection of core framework updates this time, mostly
around clk registration by clk providers and debugfs "nice to haves"
for rate constraints. I'll highlight that we're now setting the
clk_init_data pointer inside struct clk_hw to NULL during
clk_register(), which may break some drivers that thought they could
use that pointer during normal operations. That change has been
sitting in next for a while now but maybe something is still broken.
We'l see. Other than that the core framework changes aren't invasive
and they're fixing bugs, simplifying, and making things better.
On the clk driver side we got the usual addition of new SoC support,
new features for existing drivers, and bug fixes scattered throughout.
The biggest diffstat is the Amlogic driver that gained CPU clk support
in addition to migrating to the new way of specifying clk parents.
After that the Qualcomm, i.MX, Mediatek, and Rockchip clk drivers got
support for various new SoCs and clock controllers from those vendors.
Core:
- Drop NULL checks in clk debugfs
- Add min/max rates to clk debugfs
- Set clk_init_data pointer inside clk_hw to NULL after registration
- Make clk_bulk_get_all() return an 'id' corresponding to clock-names
- Evict parents from parent cache when they're unregistered
New Drivers:
- Add clock driver for i.MX8MN SoCs
- Support aspeed AST2600 SoCs
- Support for Mediatek MT6779 SoCs
- Support qcom SM8150 GCC and RPMh clks
- Support qcom QCS404 WCSS clks
- Add CPU clock support for Armada 7K/8K (specifically AP806 and AP807)
- Addition of clock driver for Rockchip rk3308 SoCs
Updates:
- Add regulator support to the cdce925 clk driver
- Add support for Raspberry Pi 4 bcm2711 SoCs
- Add SDIO gate support to aspeed driver
- Add missing of_node_put() calls in various clk drivers
- Migrate Amlogic driver to new clock parent description method
- Add DVFS support to Amlogic Meson g12
- Add Amlogic Meson g12a reset support to the axg audio clock controller
- Add sm1 support to the Amlogic Meson g12a clock controller
- Switch i.MX8MM clock driver to platform driver
- Add Hifi4 DSP related clocks for i.MX8QXP SoC
- Fix Audio PLL setting and parent clock for USB
- Misc i.MX8 clock driver improvements and corrections
- Set floor ops for Qualcomm SD clks so that rounding works
- Fix "always-on" Clock Domains on Renesas R-Car M1A, RZ/A1, RZ/A2, and RZ/N1
- Enable the Allwinner V3 SoC and fix the i2s clock for H6"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (137 commits)
clk: Drop !clk checks in debugfs dumping
clk: imx: imx8mn: fix pll mux bit
clk: imx: imx8mm: fix pll mux bit
clk: imx: clk-pll14xx: unbypass PLL by default
clk: imx: pll14xx: avoid glitch when set rate
clk: mvebu: ap80x: add AP807 clock support
clk: mvebu: ap806: Prepare the introduction of AP807 clock support
clk: mvebu: ap806: add AP-DCLK (hclk) to system controller driver
clk: mvebu: ap806: be more explicit on what SaR is
clk: mvebu: ap80x-cpu: add AP807 CPU clock support
clk: mvebu: ap806-cpu: prepare mapping of AP807 CPU clock
dt-bindings: ap806: Document AP807 clock compatible
dt-bindings: ap80x: Document AP807 CPU clock compatible
clk: sprd: add missing kfree
clk: at91: allow 24 Mhz clock as input for PLL
clk: Make clk_bulk_get_all() return a valid "id"
clk: actions: Fix factor clk struct member access
clk: qcom: rcg: Return failure for RCG update
clk: remove extra ---help--- tags in Kconfig
clk: add include guard to clk-conf.h
...
Diffstat (limited to 'drivers/clk/qcom/clk-alpha-pll.c')
-rw-r--r-- | drivers/clk/qcom/clk-alpha-pll.c | 236 |
1 files changed, 219 insertions, 17 deletions
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 0ced4a5a9a17..055318f97991 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -32,6 +32,7 @@ # define PLL_LOCK_DET BIT(31) #define PLL_L_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_L_VAL]) +#define PLL_CAL_L_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_CAL_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]) @@ -44,14 +45,17 @@ # define PLL_VCO_MASK 0x3 #define PLL_USER_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL_U]) +#define PLL_USER_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL_U1]) #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_CONFIG_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL_U1]) #define PLL_TEST_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL]) #define PLL_TEST_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U]) #define PLL_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS]) #define PLL_OPMODE(p) ((p)->offset + (p)->regs[PLL_OFF_OPMODE]) #define PLL_FRAC(p) ((p)->offset + (p)->regs[PLL_OFF_FRAC]) +#define PLL_CAL_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_CAL_VAL]) const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [CLK_ALPHA_PLL_TYPE_DEFAULT] = { @@ -96,6 +100,22 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_OPMODE] = 0x2c, [PLL_OFF_FRAC] = 0x38, }, + [CLK_ALPHA_PLL_TYPE_TRION] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_CAL_L_VAL] = 0x08, + [PLL_OFF_USER_CTL] = 0x0c, + [PLL_OFF_USER_CTL_U] = 0x10, + [PLL_OFF_USER_CTL_U1] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL_U] = 0x1c, + [PLL_OFF_CONFIG_CTL_U1] = 0x20, + [PLL_OFF_TEST_CTL] = 0x24, + [PLL_OFF_TEST_CTL_U] = 0x28, + [PLL_OFF_STATUS] = 0x30, + [PLL_OFF_OPMODE] = 0x38, + [PLL_OFF_ALPHA_VAL] = 0x40, + [PLL_OFF_CAL_VAL] = 0x44, + }, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); @@ -120,6 +140,10 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); #define FABIA_PLL_OUT_MASK 0x7 #define FABIA_PLL_RATE_MARGIN 500 +#define TRION_PLL_STANDBY 0x0 +#define TRION_PLL_RUN 0x1 +#define TRION_PLL_OUT_MASK 0x7 + #define pll_alpha_width(p) \ ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH) @@ -730,6 +754,130 @@ static long alpha_pll_huayra_round_rate(struct clk_hw *hw, unsigned long rate, return alpha_huayra_pll_round_rate(rate, *prate, &l, &a); } +static int trion_pll_is_enabled(struct clk_alpha_pll *pll, + struct regmap *regmap) +{ + u32 mode_regval, opmode_regval; + int ret; + + ret = regmap_read(regmap, PLL_MODE(pll), &mode_regval); + ret |= regmap_read(regmap, PLL_OPMODE(pll), &opmode_regval); + if (ret) + return 0; + + return ((opmode_regval & TRION_PLL_RUN) && (mode_regval & PLL_OUTCTRL)); +} + +static int clk_trion_pll_is_enabled(struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + + return trion_pll_is_enabled(pll, pll->clkr.regmap); +} + +static int clk_trion_pll_enable(struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + struct regmap *regmap = pll->clkr.regmap; + u32 val; + int ret; + + ret = regmap_read(regmap, PLL_MODE(pll), &val); + if (ret) + return ret; + + /* If in FSM mode, just vote for it */ + if (val & PLL_VOTE_FSM_ENA) { + ret = clk_enable_regmap(hw); + if (ret) + return ret; + return wait_for_pll_enable_active(pll); + } + + /* Set operation mode to RUN */ + regmap_write(regmap, PLL_OPMODE(pll), TRION_PLL_RUN); + + ret = wait_for_pll_enable_lock(pll); + if (ret) + return ret; + + /* Enable the PLL outputs */ + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), + TRION_PLL_OUT_MASK, TRION_PLL_OUT_MASK); + if (ret) + return ret; + + /* Enable the global PLL outputs */ + return regmap_update_bits(regmap, PLL_MODE(pll), + PLL_OUTCTRL, PLL_OUTCTRL); +} + +static void clk_trion_pll_disable(struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + struct regmap *regmap = pll->clkr.regmap; + u32 val; + int ret; + + ret = regmap_read(regmap, PLL_MODE(pll), &val); + if (ret) + return; + + /* If in FSM mode, just unvote it */ + if (val & PLL_VOTE_FSM_ENA) { + clk_disable_regmap(hw); + return; + } + + /* Disable the global PLL output */ + ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); + if (ret) + return; + + /* Disable the PLL outputs */ + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), + TRION_PLL_OUT_MASK, 0); + if (ret) + return; + + /* Place the PLL mode in STANDBY */ + regmap_write(regmap, PLL_OPMODE(pll), TRION_PLL_STANDBY); + regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); +} + +static unsigned long +clk_trion_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + struct regmap *regmap = pll->clkr.regmap; + u32 l, frac; + u64 prate = parent_rate; + + regmap_read(regmap, PLL_L_VAL(pll), &l); + regmap_read(regmap, PLL_ALPHA_VAL(pll), &frac); + + return alpha_pll_calc_rate(prate, l, frac, ALPHA_REG_16BIT_WIDTH); +} + +static long clk_trion_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + unsigned long min_freq, max_freq; + u32 l; + u64 a; + + rate = alpha_pll_round_rate(rate, *prate, + &l, &a, ALPHA_REG_16BIT_WIDTH); + if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) + return rate; + + min_freq = pll->vco_table[0].min_freq; + max_freq = pll->vco_table[pll->num_vco - 1].max_freq; + + return clamp(rate, min_freq, max_freq); +} + const struct clk_ops clk_alpha_pll_ops = { .enable = clk_alpha_pll_enable, .disable = clk_alpha_pll_disable, @@ -760,6 +908,15 @@ const struct clk_ops clk_alpha_pll_hwfsm_ops = { }; EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops); +const struct clk_ops clk_trion_fixed_pll_ops = { + .enable = clk_trion_pll_enable, + .disable = clk_trion_pll_disable, + .is_enabled = clk_trion_pll_is_enabled, + .recalc_rate = clk_trion_pll_recalc_rate, + .round_rate = clk_trion_pll_round_rate, +}; +EXPORT_SYMBOL_GPL(clk_trion_fixed_pll_ops); + static unsigned long clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -832,7 +989,7 @@ static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, int div; /* 16 -> 0xf, 8 -> 0x7, 4 -> 0x3, 2 -> 0x1, 1 -> 0x0 */ - div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1; + div = DIV_ROUND_UP_ULL(parent_rate, rate) - 1; return regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT, @@ -1036,11 +1193,6 @@ static unsigned long clk_alpha_pll_postdiv_fabia_recalc_rate(struct clk_hw *hw, u32 i, div = 1, val; int ret; - if (!pll->post_div_table) { - pr_err("Missing the post_div_table for the PLL\n"); - return -EINVAL; - } - ret = regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &val); if (ret) return ret; @@ -1058,16 +1210,71 @@ static unsigned long clk_alpha_pll_postdiv_fabia_recalc_rate(struct clk_hw *hw, return (parent_rate / div); } -static long clk_alpha_pll_postdiv_fabia_round_rate(struct clk_hw *hw, - unsigned long rate, unsigned long *prate) +static unsigned long +clk_trion_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + struct regmap *regmap = pll->clkr.regmap; + u32 i, div = 1, val; - if (!pll->post_div_table) { - pr_err("Missing the post_div_table for the PLL\n"); - return -EINVAL; + regmap_read(regmap, PLL_USER_CTL(pll), &val); + + val >>= pll->post_div_shift; + val &= PLL_POST_DIV_MASK(pll); + + for (i = 0; i < pll->num_post_div; i++) { + if (pll->post_div_table[i].val == val) { + div = pll->post_div_table[i].div; + break; + } + } + + return (parent_rate / div); +} + +static long +clk_trion_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); + + return divider_round_rate(hw, rate, prate, pll->post_div_table, + pll->width, CLK_DIVIDER_ROUND_CLOSEST); +}; + +static int +clk_trion_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + struct regmap *regmap = pll->clkr.regmap; + int i, val = 0, div; + + div = DIV_ROUND_UP_ULL(parent_rate, rate); + for (i = 0; i < pll->num_post_div; i++) { + if (pll->post_div_table[i].div == div) { + val = pll->post_div_table[i].val; + break; + } } + return regmap_update_bits(regmap, PLL_USER_CTL(pll), + PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT, + val << PLL_POST_DIV_SHIFT); +} + +const struct clk_ops clk_trion_pll_postdiv_ops = { + .recalc_rate = clk_trion_pll_postdiv_recalc_rate, + .round_rate = clk_trion_pll_postdiv_round_rate, + .set_rate = clk_trion_pll_postdiv_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_trion_pll_postdiv_ops); + +static long clk_alpha_pll_postdiv_fabia_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *prate) +{ + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + return divider_round_rate(hw, rate, prate, pll->post_div_table, pll->width, CLK_DIVIDER_ROUND_CLOSEST); } @@ -1089,12 +1296,7 @@ static int clk_alpha_pll_postdiv_fabia_set_rate(struct clk_hw *hw, if (val & PLL_VOTE_FSM_ENA) return 0; - if (!pll->post_div_table) { - pr_err("Missing the post_div_table for the PLL\n"); - return -EINVAL; - } - - div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + div = DIV_ROUND_UP_ULL(parent_rate, rate); for (i = 0; i < pll->num_post_div; i++) { if (pll->post_div_table[i].div == div) { val = pll->post_div_table[i].val; |