Loading drivers/clk/qcom/apcs-msm8916.c +9 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,15 @@ #include "clk-regmap.h" #include "clk-regmap-mux-div.h" static const u32 gpll0_a53cc_map[] = { 4, 5 }; enum apcs_mux_clk_parent { P_GPLL0, P_APCS_CPU_PLL, }; static const struct parent_map gpll0_a53cc_map[] = { { P_GPLL0, 4 }, { P_APCS_CPU_PLL, 5 }, }; static const char * const gpll0_a53cc[] = { "gpll0_vote", Loading drivers/clk/qcom/clk-pll.c +62 −1 Original line number Diff line number Diff line /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Copyright (c) 2013, 2021, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and Loading Loading @@ -342,3 +342,64 @@ const struct clk_ops clk_pll_sr2_ops = { .determine_rate = clk_pll_determine_rate, }; EXPORT_SYMBOL_GPL(clk_pll_sr2_ops); static int clk_pll_hf_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) { struct clk_pll *pll = to_clk_pll(hw); bool enabled; u32 mode, l_val; u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N; regmap_read(pll->clkr.regmap, pll->mode_reg, &mode); enabled = (mode & enable_mask) == enable_mask; if (enabled) clk_pll_disable(hw); l_val = rate / prate; regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, l_val); regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, 0); regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, 1); if (enabled) clk_pll_sr2_enable(hw); return 0; } static void clk_pll_hf_list_registers(struct seq_file *f, struct clk_hw *hw) { struct clk_pll *pll = to_clk_pll(hw); int size, i, val; static struct clk_register_data data[] = { {"PLL_MODE", 0x0}, {"PLL_L_VAL", 0x4}, {"PLL_M_VAL", 0x8}, {"PLL_N_VAL", 0xC}, {"PLL_USER_CTL", 0x10}, {"PLL_CONFIG_CTL", 0x14}, {"PLL_STATUS_CTL", 0x1C}, }; size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->mode_reg + data[i].offset, &val); clock_debug_output(f, false, "%20s: 0x%.8x\n", data[i].name, val); } } const struct clk_ops clk_pll_hf_ops = { .enable = clk_pll_sr2_enable, .disable = clk_pll_disable, .set_rate = clk_pll_hf_set_rate, .recalc_rate = clk_pll_recalc_rate, .determine_rate = clk_pll_determine_rate, .list_registers = clk_pll_hf_list_registers, }; EXPORT_SYMBOL(clk_pll_hf_ops); drivers/clk/qcom/clk-pll.h +2 −1 Original line number Diff line number Diff line /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Copyright (c) 2013, 2021, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and Loading Loading @@ -63,6 +63,7 @@ struct clk_pll { extern const struct clk_ops clk_pll_ops; extern const struct clk_ops clk_pll_vote_ops; extern const struct clk_ops clk_pll_sr2_ops; extern const struct clk_ops clk_pll_hf_ops; #define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr) Loading drivers/clk/qcom/clk-regmap-mux-div.c +34 −10 Original line number Diff line number Diff line Loading @@ -56,20 +56,26 @@ int mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div) } EXPORT_SYMBOL_GPL(mux_div_set_src_div); static void mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, u32 *div) { int ret = 0; u32 val, d, s; const char *name = clk_hw_get_name(&md->clkr.hw); regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); if (ret) return ret; if (val & CMD_RCGR_DIRTY_CFG) { pr_err("%s: RCG configuration is pending\n", name); return; return -EBUSY; } regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); ret = regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); if (ret) return ret; s = (val >> md->src_shift); s &= BIT(md->src_width) - 1; *src = s; Loading @@ -77,6 +83,8 @@ static void mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, d = (val >> md->hid_shift); d &= BIT(md->hid_width) - 1; *div = d; return ret; } static inline bool is_better_rate(unsigned long req, unsigned long best, Loading Loading @@ -142,7 +150,7 @@ static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, if (is_better_rate(rate, best_rate, actual_rate)) { best_rate = actual_rate; best_src = md->parent_map[i]; best_src = md->parent_map[i].cfg; best_div = div - 1; } Loading @@ -169,7 +177,7 @@ static u8 mux_div_get_parent(struct clk_hw *hw) mux_div_get_src_div(md, &src, &div); for (i = 0; i < clk_hw_get_num_parents(hw); i++) if (src == md->parent_map[i]) if (src == md->parent_map[i].cfg) return i; pr_err("%s: Can't find parent with src %d\n", name, src); Loading @@ -180,7 +188,7 @@ static int mux_div_set_parent(struct clk_hw *hw, u8 index) { struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); return mux_div_set_src_div(md, md->parent_map[index], md->div); return mux_div_set_src_div(md, md->parent_map[index].cfg, md->div); } static int mux_div_set_rate(struct clk_hw *hw, Loading @@ -197,7 +205,7 @@ static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); return __mux_div_set_rate_and_parent(hw, rate, prate, md->parent_map[index]); md->parent_map[index].cfg); } static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) Loading @@ -209,7 +217,7 @@ static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) mux_div_get_src_div(md, &src, &div); for (i = 0; i < num_parents; i++) if (src == md->parent_map[i]) { if (src == md->parent_map[i].cfg) { struct clk_hw *p = clk_hw_get_parent_by_index(hw, i); unsigned long parent_rate = clk_hw_get_rate(p); Loading @@ -220,7 +228,23 @@ static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) return 0; } static int mux_div_enable(struct clk_hw *hw) { struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); return mux_div_set_src_div(md, md->src, md->div); } static void mux_div_disable(struct clk_hw *hw) { struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); mux_div_set_src_div(md, md->safe_src, md->safe_div); } const struct clk_ops clk_regmap_mux_div_ops = { .enable = mux_div_enable, .disable = mux_div_disable, .get_parent = mux_div_get_parent, .set_parent = mux_div_set_parent, .set_rate = mux_div_set_rate, Loading drivers/clk/qcom/clk-regmap-mux-div.h +19 −2 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ #define __QCOM_CLK_REGMAP_MUX_DIV_H__ #include <linux/clk-provider.h> #include "common.h" #include "clk-regmap.h" /** Loading @@ -19,7 +20,19 @@ * @src_shift: lowest bit of source select field * @div: the divider raw configuration value * @src: the mux index which will be used if the clock is enabled * @parent_map: map from parent_names index to src_sel field * @safe_src: the safe source mux value we switch to, while the main PLL is * reconfigured * @safe_div: the safe divider value that we set, while the main PLL is * reconfigured * @safe_freq: When switching rates from A to B, the mux div clock will * instead switch from A -> safe_freq -> B. This allows the * mux_div clock to change rates while enabled, even if this * behavior is not supported by the parent clocks. * If changing the rate of parent A also causes the rate of * parent B to change, then safe_freq must be defined. * safe_freq is expected to have a source clock which is always * on and runs at only one rate. * @parent_map: pointer to parent_map struct * @clkr: handle between common and hardware-specific interfaces * @pclk: the input PLL clock * @clk_nb: clock notifier for rate changes of the input PLL Loading @@ -32,7 +45,10 @@ struct clk_regmap_mux_div { u32 src_shift; u32 div; u32 src; const u32 *parent_map; u32 safe_src; u32 safe_div; unsigned long safe_freq; const struct parent_map *parent_map; struct clk_regmap clkr; struct clk *pclk; struct notifier_block clk_nb; Loading @@ -40,5 +56,6 @@ struct clk_regmap_mux_div { extern const struct clk_ops clk_regmap_mux_div_ops; extern int mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div); int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, u32 *div); #endif Loading
drivers/clk/qcom/apcs-msm8916.c +9 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,15 @@ #include "clk-regmap.h" #include "clk-regmap-mux-div.h" static const u32 gpll0_a53cc_map[] = { 4, 5 }; enum apcs_mux_clk_parent { P_GPLL0, P_APCS_CPU_PLL, }; static const struct parent_map gpll0_a53cc_map[] = { { P_GPLL0, 4 }, { P_APCS_CPU_PLL, 5 }, }; static const char * const gpll0_a53cc[] = { "gpll0_vote", Loading
drivers/clk/qcom/clk-pll.c +62 −1 Original line number Diff line number Diff line /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Copyright (c) 2013, 2021, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and Loading Loading @@ -342,3 +342,64 @@ const struct clk_ops clk_pll_sr2_ops = { .determine_rate = clk_pll_determine_rate, }; EXPORT_SYMBOL_GPL(clk_pll_sr2_ops); static int clk_pll_hf_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) { struct clk_pll *pll = to_clk_pll(hw); bool enabled; u32 mode, l_val; u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N; regmap_read(pll->clkr.regmap, pll->mode_reg, &mode); enabled = (mode & enable_mask) == enable_mask; if (enabled) clk_pll_disable(hw); l_val = rate / prate; regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, l_val); regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, 0); regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, 1); if (enabled) clk_pll_sr2_enable(hw); return 0; } static void clk_pll_hf_list_registers(struct seq_file *f, struct clk_hw *hw) { struct clk_pll *pll = to_clk_pll(hw); int size, i, val; static struct clk_register_data data[] = { {"PLL_MODE", 0x0}, {"PLL_L_VAL", 0x4}, {"PLL_M_VAL", 0x8}, {"PLL_N_VAL", 0xC}, {"PLL_USER_CTL", 0x10}, {"PLL_CONFIG_CTL", 0x14}, {"PLL_STATUS_CTL", 0x1C}, }; size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->mode_reg + data[i].offset, &val); clock_debug_output(f, false, "%20s: 0x%.8x\n", data[i].name, val); } } const struct clk_ops clk_pll_hf_ops = { .enable = clk_pll_sr2_enable, .disable = clk_pll_disable, .set_rate = clk_pll_hf_set_rate, .recalc_rate = clk_pll_recalc_rate, .determine_rate = clk_pll_determine_rate, .list_registers = clk_pll_hf_list_registers, }; EXPORT_SYMBOL(clk_pll_hf_ops);
drivers/clk/qcom/clk-pll.h +2 −1 Original line number Diff line number Diff line /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * Copyright (c) 2013, 2021, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and Loading Loading @@ -63,6 +63,7 @@ struct clk_pll { extern const struct clk_ops clk_pll_ops; extern const struct clk_ops clk_pll_vote_ops; extern const struct clk_ops clk_pll_sr2_ops; extern const struct clk_ops clk_pll_hf_ops; #define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr) Loading
drivers/clk/qcom/clk-regmap-mux-div.c +34 −10 Original line number Diff line number Diff line Loading @@ -56,20 +56,26 @@ int mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div) } EXPORT_SYMBOL_GPL(mux_div_set_src_div); static void mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, u32 *div) { int ret = 0; u32 val, d, s; const char *name = clk_hw_get_name(&md->clkr.hw); regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); if (ret) return ret; if (val & CMD_RCGR_DIRTY_CFG) { pr_err("%s: RCG configuration is pending\n", name); return; return -EBUSY; } regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); ret = regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); if (ret) return ret; s = (val >> md->src_shift); s &= BIT(md->src_width) - 1; *src = s; Loading @@ -77,6 +83,8 @@ static void mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, d = (val >> md->hid_shift); d &= BIT(md->hid_width) - 1; *div = d; return ret; } static inline bool is_better_rate(unsigned long req, unsigned long best, Loading Loading @@ -142,7 +150,7 @@ static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, if (is_better_rate(rate, best_rate, actual_rate)) { best_rate = actual_rate; best_src = md->parent_map[i]; best_src = md->parent_map[i].cfg; best_div = div - 1; } Loading @@ -169,7 +177,7 @@ static u8 mux_div_get_parent(struct clk_hw *hw) mux_div_get_src_div(md, &src, &div); for (i = 0; i < clk_hw_get_num_parents(hw); i++) if (src == md->parent_map[i]) if (src == md->parent_map[i].cfg) return i; pr_err("%s: Can't find parent with src %d\n", name, src); Loading @@ -180,7 +188,7 @@ static int mux_div_set_parent(struct clk_hw *hw, u8 index) { struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); return mux_div_set_src_div(md, md->parent_map[index], md->div); return mux_div_set_src_div(md, md->parent_map[index].cfg, md->div); } static int mux_div_set_rate(struct clk_hw *hw, Loading @@ -197,7 +205,7 @@ static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); return __mux_div_set_rate_and_parent(hw, rate, prate, md->parent_map[index]); md->parent_map[index].cfg); } static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) Loading @@ -209,7 +217,7 @@ static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) mux_div_get_src_div(md, &src, &div); for (i = 0; i < num_parents; i++) if (src == md->parent_map[i]) { if (src == md->parent_map[i].cfg) { struct clk_hw *p = clk_hw_get_parent_by_index(hw, i); unsigned long parent_rate = clk_hw_get_rate(p); Loading @@ -220,7 +228,23 @@ static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) return 0; } static int mux_div_enable(struct clk_hw *hw) { struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); return mux_div_set_src_div(md, md->src, md->div); } static void mux_div_disable(struct clk_hw *hw) { struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); mux_div_set_src_div(md, md->safe_src, md->safe_div); } const struct clk_ops clk_regmap_mux_div_ops = { .enable = mux_div_enable, .disable = mux_div_disable, .get_parent = mux_div_get_parent, .set_parent = mux_div_set_parent, .set_rate = mux_div_set_rate, Loading
drivers/clk/qcom/clk-regmap-mux-div.h +19 −2 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ #define __QCOM_CLK_REGMAP_MUX_DIV_H__ #include <linux/clk-provider.h> #include "common.h" #include "clk-regmap.h" /** Loading @@ -19,7 +20,19 @@ * @src_shift: lowest bit of source select field * @div: the divider raw configuration value * @src: the mux index which will be used if the clock is enabled * @parent_map: map from parent_names index to src_sel field * @safe_src: the safe source mux value we switch to, while the main PLL is * reconfigured * @safe_div: the safe divider value that we set, while the main PLL is * reconfigured * @safe_freq: When switching rates from A to B, the mux div clock will * instead switch from A -> safe_freq -> B. This allows the * mux_div clock to change rates while enabled, even if this * behavior is not supported by the parent clocks. * If changing the rate of parent A also causes the rate of * parent B to change, then safe_freq must be defined. * safe_freq is expected to have a source clock which is always * on and runs at only one rate. * @parent_map: pointer to parent_map struct * @clkr: handle between common and hardware-specific interfaces * @pclk: the input PLL clock * @clk_nb: clock notifier for rate changes of the input PLL Loading @@ -32,7 +45,10 @@ struct clk_regmap_mux_div { u32 src_shift; u32 div; u32 src; const u32 *parent_map; u32 safe_src; u32 safe_div; unsigned long safe_freq; const struct parent_map *parent_map; struct clk_regmap clkr; struct clk *pclk; struct notifier_block clk_nb; Loading @@ -40,5 +56,6 @@ struct clk_regmap_mux_div { extern const struct clk_ops clk_regmap_mux_div_ops; extern int mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div); int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, u32 *div); #endif