diff options
author | Ingo Molnar <mingo@kernel.org> | 2017-03-01 09:02:26 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-03-01 09:02:26 +0100 |
commit | 0871d5a66da5c41151e0896a90298b163e42f2e0 (patch) | |
tree | 1ba71fab9016cb28bb9d18ffd62b6b744f2f761c /drivers/clk/qcom | |
parent | x86/boot: Fix pr_debug() API braindamage (diff) | |
parent | Merge tag 'for-linus-4.11' of git://git.code.sf.net/p/openipmi/linux-ipmi (diff) | |
download | linux-0871d5a66da5c41151e0896a90298b163e42f2e0.tar.xz linux-0871d5a66da5c41151e0896a90298b163e42f2e0.zip |
Merge branch 'linus' into WIP.x86/boot, to fix up conflicts and to pick up updates
Conflicts:
arch/x86/xen/setup.c
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/clk/qcom')
-rw-r--r-- | drivers/clk/qcom/clk-smd-rpm.c | 71 | ||||
-rw-r--r-- | drivers/clk/qcom/common.c | 1 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-ipq4019.c | 479 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-mdm9615.c | 30 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-msm8994.c | 18 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-msm8996.c | 1 | ||||
-rw-r--r-- | drivers/clk/qcom/gdsc.c | 58 |
7 files changed, 630 insertions, 28 deletions
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 07e2cc6ed781..3487c267833e 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -462,8 +462,79 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { .num_clks = ARRAY_SIZE(msm8916_clks), }; +/* msm8974 */ +DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8974, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8974, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3); +DEFINE_CLK_SMD_RPM(msm8974, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8974, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8974, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msm8974, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d0, cxo_d0_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d1, cxo_d1_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a0, cxo_a0_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a1, cxo_a1_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a2, cxo_a2_a, 6); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, diff_clk, diff_a_clk, 7); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk1, div_a_clk1, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk2, div_a_clk2, 12); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d0_pin, cxo_d0_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d1_pin, cxo_d1_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a0_pin, cxo_a0_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a1_pin, cxo_a1_a_pin, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a2_pin, cxo_a2_a_pin, 6); + +static struct clk_smd_rpm *msm8974_clks[] = { + [RPM_SMD_PNOC_CLK] = &msm8974_pnoc_clk, + [RPM_SMD_PNOC_A_CLK] = &msm8974_pnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8974_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8974_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8974_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8974_cnoc_a_clk, + [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8974_mmssnoc_ahb_clk, + [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8974_mmssnoc_ahb_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8974_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8974_bimc_a_clk, + [RPM_SMD_OCMEMGX_CLK] = &msm8974_ocmemgx_clk, + [RPM_SMD_OCMEMGX_A_CLK] = &msm8974_ocmemgx_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8974_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8974_qdss_a_clk, + [RPM_SMD_CXO_D0] = &msm8974_cxo_d0, + [RPM_SMD_CXO_D0_A] = &msm8974_cxo_d0_a, + [RPM_SMD_CXO_D1] = &msm8974_cxo_d1, + [RPM_SMD_CXO_D1_A] = &msm8974_cxo_d1_a, + [RPM_SMD_CXO_A0] = &msm8974_cxo_a0, + [RPM_SMD_CXO_A0_A] = &msm8974_cxo_a0_a, + [RPM_SMD_CXO_A1] = &msm8974_cxo_a1, + [RPM_SMD_CXO_A1_A] = &msm8974_cxo_a1_a, + [RPM_SMD_CXO_A2] = &msm8974_cxo_a2, + [RPM_SMD_CXO_A2_A] = &msm8974_cxo_a2_a, + [RPM_SMD_DIFF_CLK] = &msm8974_diff_clk, + [RPM_SMD_DIFF_A_CLK] = &msm8974_diff_a_clk, + [RPM_SMD_DIV_CLK1] = &msm8974_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8974_div_a_clk1, + [RPM_SMD_DIV_CLK2] = &msm8974_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8974_div_a_clk2, + [RPM_SMD_CXO_D0_PIN] = &msm8974_cxo_d0_pin, + [RPM_SMD_CXO_D0_A_PIN] = &msm8974_cxo_d0_a_pin, + [RPM_SMD_CXO_D1_PIN] = &msm8974_cxo_d1_pin, + [RPM_SMD_CXO_D1_A_PIN] = &msm8974_cxo_d1_a_pin, + [RPM_SMD_CXO_A0_PIN] = &msm8974_cxo_a0_pin, + [RPM_SMD_CXO_A0_A_PIN] = &msm8974_cxo_a0_a_pin, + [RPM_SMD_CXO_A1_PIN] = &msm8974_cxo_a1_pin, + [RPM_SMD_CXO_A1_A_PIN] = &msm8974_cxo_a1_a_pin, + [RPM_SMD_CXO_A2_PIN] = &msm8974_cxo_a2_pin, + [RPM_SMD_CXO_A2_A_PIN] = &msm8974_cxo_a2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8974 = { + .clks = msm8974_clks, + .num_clks = ARRAY_SIZE(msm8974_clks), +}; static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, + { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index cfab7b400381..03f9d316f969 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -145,7 +145,6 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path, clocks_node = of_find_node_by_path("/clocks"); if (clocks_node) node = of_find_node_by_name(clocks_node, path); - of_node_put(clocks_node); if (!node) { fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c index 33d09138f5e5..46cb256b4aa2 100644 --- a/drivers/clk/qcom/gcc-ipq4019.c +++ b/drivers/clk/qcom/gcc-ipq4019.c @@ -20,6 +20,9 @@ #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/reset-controller.h> +#include <linux/math64.h> +#include <linux/delay.h> +#include <linux/clk.h> #include <dt-bindings/clock/qcom,gcc-ipq4019.h> @@ -28,6 +31,13 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "clk-regmap-divider.h" + +#define to_clk_regmap_div(_hw) container_of(to_clk_regmap(_hw),\ + struct clk_regmap_div, clkr) + +#define to_clk_fepll(_hw) container_of(to_clk_regmap_div(_hw),\ + struct clk_fepll, cdiv) enum { P_XO, @@ -40,6 +50,41 @@ enum { P_DDRPLLAPSS, }; +/* + * struct clk_fepll_vco - vco feedback divider corresponds for FEPLL clocks + * @fdbkdiv_shift: lowest bit for FDBKDIV + * @fdbkdiv_width: number of bits in FDBKDIV + * @refclkdiv_shift: lowest bit for REFCLKDIV + * @refclkdiv_width: number of bits in REFCLKDIV + * @reg: PLL_DIV register address + */ +struct clk_fepll_vco { + u32 fdbkdiv_shift; + u32 fdbkdiv_width; + u32 refclkdiv_shift; + u32 refclkdiv_width; + u32 reg; +}; + +/* + * struct clk_fepll - clk divider corresponds to FEPLL clocks + * @fixed_div: fixed divider value if divider is fixed + * @parent_map: map from software's parent index to hardware's src_sel field + * @cdiv: divider values for PLL_DIV + * @pll_vco: vco feedback divider + * @div_table: mapping for actual divider value to register divider value + * in case of non fixed divider + * @freq_tbl: frequency table + */ +struct clk_fepll { + u32 fixed_div; + const u8 *parent_map; + struct clk_regmap_div cdiv; + const struct clk_fepll_vco *pll_vco; + const struct clk_div_table *div_table; + const struct freq_tbl *freq_tbl; +}; + static struct parent_map gcc_xo_200_500_map[] = { { P_XO, 0 }, { P_FEPLL200, 1 }, @@ -80,7 +125,7 @@ static struct parent_map gcc_xo_sdcc1_500_map[] = { static const char * const gcc_xo_sdcc1_500[] = { "xo", - "ddrpll", + "ddrpllsdcc", "fepll500", }; @@ -121,6 +166,12 @@ static struct parent_map gcc_xo_ddr_500_200_map[] = { { P_DDRPLLAPSS, 1 }, }; +/* + * Contains index for safe clock during APSS freq change. + * fepll500 is being used as safe clock so initialize it + * with its index in parents list gcc_xo_ddr_500_200. + */ +static const int gcc_ipq4019_cpu_safe_parent = 2; static const char * const gcc_xo_ddr_500_200[] = { "xo", "fepll200", @@ -505,7 +556,7 @@ static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = { F(25000000, P_FEPLL500, 1, 1, 20), F(50000000, P_FEPLL500, 1, 1, 10), F(100000000, P_FEPLL500, 1, 1, 5), - F(193000000, P_DDRPLL, 1, 0, 0), + F(192000000, P_DDRPLL, 1, 0, 0), { } }; @@ -524,10 +575,20 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { }; static const struct freq_tbl ftbl_gcc_apps_clk[] = { - F(48000000, P_XO, 1, 0, 0), + F(48000000, P_XO, 1, 0, 0), F(200000000, P_FEPLL200, 1, 0, 0), + F(384000000, P_DDRPLLAPSS, 1, 0, 0), + F(413000000, P_DDRPLLAPSS, 1, 0, 0), + F(448000000, P_DDRPLLAPSS, 1, 0, 0), + F(488000000, P_DDRPLLAPSS, 1, 0, 0), F(500000000, P_FEPLL500, 1, 0, 0), - F(626000000, P_DDRPLLAPSS, 1, 0, 0), + F(512000000, P_DDRPLLAPSS, 1, 0, 0), + F(537000000, P_DDRPLLAPSS, 1, 0, 0), + F(565000000, P_DDRPLLAPSS, 1, 0, 0), + F(597000000, P_DDRPLLAPSS, 1, 0, 0), + F(632000000, P_DDRPLLAPSS, 1, 0, 0), + F(672000000, P_DDRPLLAPSS, 1, 0, 0), + F(716000000, P_DDRPLLAPSS, 1, 0, 0), { } }; @@ -541,6 +602,7 @@ static struct clk_rcg2 apps_clk_src = { .parent_names = gcc_xo_ddr_500_200, .num_parents = 4, .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -1154,6 +1216,364 @@ static struct clk_branch gcc_wcss5g_rtc_clk = { }, }; +/* Calculates the VCO rate for FEPLL. */ +static u64 clk_fepll_vco_calc_rate(struct clk_fepll *pll_div, + unsigned long parent_rate) +{ + const struct clk_fepll_vco *pll_vco = pll_div->pll_vco; + u32 fdbkdiv, refclkdiv, cdiv; + u64 vco; + + regmap_read(pll_div->cdiv.clkr.regmap, pll_vco->reg, &cdiv); + refclkdiv = (cdiv >> pll_vco->refclkdiv_shift) & + (BIT(pll_vco->refclkdiv_width) - 1); + fdbkdiv = (cdiv >> pll_vco->fdbkdiv_shift) & + (BIT(pll_vco->fdbkdiv_width) - 1); + + vco = parent_rate / refclkdiv; + vco *= 2; + vco *= fdbkdiv; + + return vco; +} + +static const struct clk_fepll_vco gcc_apss_ddrpll_vco = { + .fdbkdiv_shift = 16, + .fdbkdiv_width = 8, + .refclkdiv_shift = 24, + .refclkdiv_width = 5, + .reg = 0x2e020, +}; + +static const struct clk_fepll_vco gcc_fepll_vco = { + .fdbkdiv_shift = 16, + .fdbkdiv_width = 8, + .refclkdiv_shift = 24, + .refclkdiv_width = 5, + .reg = 0x2f020, +}; + +/* + * Round rate function for APSS CPU PLL Clock divider. + * It looks up the frequency table and returns the next higher frequency + * supported in hardware. + */ +static long clk_cpu_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *p_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + struct clk_hw *p_hw; + const struct freq_tbl *f; + + f = qcom_find_freq(pll->freq_tbl, rate); + if (!f) + return -EINVAL; + + p_hw = clk_hw_get_parent_by_index(hw, f->src); + *p_rate = clk_hw_get_rate(p_hw); + + return f->freq; +}; + +/* + * Clock set rate function for APSS CPU PLL Clock divider. + * It looks up the frequency table and updates the PLL divider to corresponding + * divider value. + */ +static int clk_cpu_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + const struct freq_tbl *f; + u32 mask; + int ret; + + f = qcom_find_freq(pll->freq_tbl, rate); + if (!f) + return -EINVAL; + + mask = (BIT(pll->cdiv.width) - 1) << pll->cdiv.shift; + ret = regmap_update_bits(pll->cdiv.clkr.regmap, + pll->cdiv.reg, mask, + f->pre_div << pll->cdiv.shift); + /* + * There is no status bit which can be checked for successful CPU + * divider update operation so using delay for the same. + */ + udelay(1); + + return 0; +}; + +/* + * Clock frequency calculation function for APSS CPU PLL Clock divider. + * This clock divider is nonlinear so this function calculates the actual + * divider and returns the output frequency by dividing VCO Frequency + * with this actual divider value. + */ +static unsigned long +clk_cpu_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + u32 cdiv, pre_div; + u64 rate; + + regmap_read(pll->cdiv.clkr.regmap, pll->cdiv.reg, &cdiv); + cdiv = (cdiv >> pll->cdiv.shift) & (BIT(pll->cdiv.width) - 1); + + /* + * Some dividers have value in 0.5 fraction so multiply both VCO + * frequency(parent_rate) and pre_div with 2 to make integer + * calculation. + */ + if (cdiv > 10) + pre_div = (cdiv + 1) * 2; + else + pre_div = cdiv + 12; + + rate = clk_fepll_vco_calc_rate(pll, parent_rate) * 2; + do_div(rate, pre_div); + + return rate; +}; + +static const struct clk_ops clk_regmap_cpu_div_ops = { + .round_rate = clk_cpu_div_round_rate, + .set_rate = clk_cpu_div_set_rate, + .recalc_rate = clk_cpu_div_recalc_rate, +}; + +static const struct freq_tbl ftbl_apss_ddr_pll[] = { + { 384000000, P_XO, 0xd, 0, 0 }, + { 413000000, P_XO, 0xc, 0, 0 }, + { 448000000, P_XO, 0xb, 0, 0 }, + { 488000000, P_XO, 0xa, 0, 0 }, + { 512000000, P_XO, 0x9, 0, 0 }, + { 537000000, P_XO, 0x8, 0, 0 }, + { 565000000, P_XO, 0x7, 0, 0 }, + { 597000000, P_XO, 0x6, 0, 0 }, + { 632000000, P_XO, 0x5, 0, 0 }, + { 672000000, P_XO, 0x4, 0, 0 }, + { 716000000, P_XO, 0x3, 0, 0 }, + { 768000000, P_XO, 0x2, 0, 0 }, + { 823000000, P_XO, 0x1, 0, 0 }, + { 896000000, P_XO, 0x0, 0, 0 }, + { } +}; + +static struct clk_fepll gcc_apss_cpu_plldiv_clk = { + .cdiv.reg = 0x2e020, + .cdiv.shift = 4, + .cdiv.width = 4, + .cdiv.clkr = { + .enable_reg = 0x2e000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "ddrpllapss", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_regmap_cpu_div_ops, + }, + }, + .freq_tbl = ftbl_apss_ddr_pll, + .pll_vco = &gcc_apss_ddrpll_vco, +}; + +/* Calculates the rate for PLL divider. + * If the divider value is not fixed then it gets the actual divider value + * from divider table. Then, it calculate the clock rate by dividing the + * parent rate with actual divider value. + */ +static unsigned long +clk_regmap_clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + u32 cdiv, pre_div = 1; + u64 rate; + const struct clk_div_table *clkt; + + if (pll->fixed_div) { + pre_div = pll->fixed_div; + } else { + regmap_read(pll->cdiv.clkr.regmap, pll->cdiv.reg, &cdiv); + cdiv = (cdiv >> pll->cdiv.shift) & (BIT(pll->cdiv.width) - 1); + + for (clkt = pll->div_table; clkt->div; clkt++) { + if (clkt->val == cdiv) + pre_div = clkt->div; + } + } + + rate = clk_fepll_vco_calc_rate(pll, parent_rate); + do_div(rate, pre_div); + + return rate; +}; + +static const struct clk_ops clk_fepll_div_ops = { + .recalc_rate = clk_regmap_clk_div_recalc_rate, +}; + +static struct clk_fepll gcc_apss_sdcc_clk = { + .fixed_div = 28, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "ddrpllsdcc", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_apss_ddrpll_vco, +}; + +static struct clk_fepll gcc_fepll125_clk = { + .fixed_div = 32, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll125", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepll125dly_clk = { + .fixed_div = 32, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll125dly", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepll200_clk = { + .fixed_div = 20, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll200", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepll500_clk = { + .fixed_div = 8, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll500", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static const struct clk_div_table fepllwcss_clk_div_table[] = { + { 0, 15 }, + { 1, 16 }, + { 2, 18 }, + { 3, 20 }, + { }, +}; + +static struct clk_fepll gcc_fepllwcss2g_clk = { + .cdiv.reg = 0x2f020, + .cdiv.shift = 8, + .cdiv.width = 2, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepllwcss2g", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepllwcss5g_clk = { + .cdiv.reg = 0x2f020, + .cdiv.shift = 12, + .cdiv.width = 2, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepllwcss5g", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table, + .pll_vco = &gcc_fepll_vco, +}; + +static const struct freq_tbl ftbl_gcc_pcnoc_ahb_clk[] = { + F(48000000, P_XO, 1, 0, 0), + F(100000000, P_FEPLL200, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcnoc_ahb_clk_src = { + .cmd_rcgr = 0x21024, + .hid_width = 5, + .parent_map = gcc_xo_200_500_map, + .freq_tbl = ftbl_gcc_pcnoc_ahb_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_pcnoc_ahb_clk_src", + .parent_names = gcc_xo_200_500, + .num_parents = 3, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch pcnoc_clk_src = { + .halt_reg = 0x21030, + .clkr = { + .enable_reg = 0x21030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "pcnoc_clk_src", + .parent_names = (const char *[]){ + "gcc_pcnoc_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT | + CLK_IS_CRITICAL, + }, + }, +}; + static struct clk_regmap *gcc_ipq4019_clocks[] = { [AUDIO_CLK_SRC] = &audio_clk_src.clkr, [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, @@ -1214,6 +1634,16 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = { [GCC_WCSS5G_CLK] = &gcc_wcss5g_clk.clkr, [GCC_WCSS5G_REF_CLK] = &gcc_wcss5g_ref_clk.clkr, [GCC_WCSS5G_RTC_CLK] = &gcc_wcss5g_rtc_clk.clkr, + [GCC_SDCC_PLLDIV_CLK] = &gcc_apss_sdcc_clk.cdiv.clkr, + [GCC_FEPLL125_CLK] = &gcc_fepll125_clk.cdiv.clkr, + [GCC_FEPLL125DLY_CLK] = &gcc_fepll125dly_clk.cdiv.clkr, + [GCC_FEPLL200_CLK] = &gcc_fepll200_clk.cdiv.clkr, + [GCC_FEPLL500_CLK] = &gcc_fepll500_clk.cdiv.clkr, + [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr, + [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr, + [GCC_APSS_CPU_PLLDIV_CLK] = &gcc_apss_cpu_plldiv_clk.cdiv.clkr, + [GCC_PCNOC_AHB_CLK_SRC] = &gcc_pcnoc_ahb_clk_src.clkr, + [GCC_PCNOC_AHB_CLK] = &pcnoc_clk_src.clkr, }; static const struct qcom_reset_map gcc_ipq4019_resets[] = { @@ -1294,7 +1724,7 @@ static const struct regmap_config gcc_ipq4019_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x2dfff, + .max_register = 0x2ffff, .fast_io = true, }; @@ -1312,23 +1742,44 @@ static const struct of_device_id gcc_ipq4019_match_table[] = { }; MODULE_DEVICE_TABLE(of, gcc_ipq4019_match_table); +static int +gcc_ipq4019_cpu_clk_notifier_fn(struct notifier_block *nb, + unsigned long action, void *data) +{ + int err = 0; + + if (action == PRE_RATE_CHANGE) + err = clk_rcg2_ops.set_parent(&apps_clk_src.clkr.hw, + gcc_ipq4019_cpu_safe_parent); + + return notifier_from_errno(err); +} + +static struct notifier_block gcc_ipq4019_cpu_clk_notifier = { + .notifier_call = gcc_ipq4019_cpu_clk_notifier_fn, +}; + static int gcc_ipq4019_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; + int err; - clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000); + err = qcom_cc_probe(pdev, &gcc_ipq4019_desc); + if (err) + return err; - return qcom_cc_probe(pdev, &gcc_ipq4019_desc); + return clk_notifier_register(apps_clk_src.clkr.hw.clk, + &gcc_ipq4019_cpu_clk_notifier); +} + +static int gcc_ipq4019_remove(struct platform_device *pdev) +{ + return clk_notifier_unregister(apps_clk_src.clkr.hw.clk, + &gcc_ipq4019_cpu_clk_notifier); } static struct platform_driver gcc_ipq4019_driver = { .probe = gcc_ipq4019_probe, + .remove = gcc_ipq4019_remove, .driver = { .name = "qcom,gcc-ipq4019", .of_match_table = gcc_ipq4019_match_table, diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c index 581a17f67379..b99dd406e907 100644 --- a/drivers/clk/qcom/gcc-mdm9615.c +++ b/drivers/clk/qcom/gcc-mdm9615.c @@ -1563,6 +1563,34 @@ static struct clk_branch rpm_msg_ram_h_clk = { }, }; +static struct clk_branch ebi2_clk = { + .hwcg_reg = 0x2664, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 24, + .clkr = { + .enable_reg = 0x2664, + .enable_mask = BIT(6) | BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ebi2_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch ebi2_aon_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x2664, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "ebi2_aon_clk", + .ops = &clk_branch_ops, + }, + }, +}; + static struct clk_hw *gcc_mdm9615_hws[] = { &cxo.hw, }; @@ -1637,6 +1665,8 @@ static struct clk_regmap *gcc_mdm9615_clks[] = { [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, + [EBI2_CLK] = &ebi2_clk.clkr, + [EBI2_AON_CLK] = &ebi2_aon_clk.clkr, }; static const struct qcom_reset_map gcc_mdm9615_resets[] = { diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c index 8afd8304a070..7983288d9141 100644 --- a/drivers/clk/qcom/gcc-msm8994.c +++ b/drivers/clk/qcom/gcc-msm8994.c @@ -1888,6 +1888,23 @@ static struct clk_branch gcc_sdcc1_apps_clk = { }, }; +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x04c8, + .clkr = { + .enable_reg = 0x04c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sdcc1_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_sdcc2_apps_clk = { .halt_reg = 0x0504, .clkr = { @@ -2231,6 +2248,7 @@ static struct clk_regmap *gcc_msm8994_clocks[] = { [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, [GCC_SDCC3_APPS_CLK] = &gcc_sdcc3_apps_clk.clkr, [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, [GCC_SYS_NOC_UFS_AXI_CLK] = &gcc_sys_noc_ufs_axi_clk.clkr, [GCC_SYS_NOC_USB3_AXI_CLK] = &gcc_sys_noc_usb3_axi_clk.clkr, [GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr, diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 4b1fc1730d29..8abc200d4fd3 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -3448,6 +3448,7 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = { [GCC_MSMPU_BCR] = { 0x8d000 }, [GCC_MSS_Q6_BCR] = { 0x8e000 }, [GCC_QREFS_VBG_CAL_BCR] = { 0x88020 }, + [GCC_MSS_RESTART] = { 0x8f008 }, }; static const struct regmap_config gcc_msm8996_regmap_config = { diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 288186cce0ae..a4f3580587b7 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -63,11 +63,26 @@ static int gdsc_hwctrl(struct gdsc *sc, bool en) return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val); } +static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en) +{ + ktime_t start; + + start = ktime_get(); + do { + if (gdsc_is_enabled(sc, reg) == en) + return 0; + } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US); + + if (gdsc_is_enabled(sc, reg) == en) + return 0; + + return -ETIMEDOUT; +} + static int gdsc_toggle_logic(struct gdsc *sc, bool en) { int ret; u32 val = en ? 0 : SW_COLLAPSE_MASK; - ktime_t start; unsigned int status_reg = sc->gdscr; ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val); @@ -100,16 +115,7 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en) udelay(1); } - start = ktime_get(); - do { - if (gdsc_is_enabled(sc, status_reg) == en) - return 0; - } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US); - - if (gdsc_is_enabled(sc, status_reg) == en) - return 0; - - return -ETIMEDOUT; + return gdsc_poll_status(sc, status_reg, en); } static inline int gdsc_deassert_reset(struct gdsc *sc) @@ -188,8 +194,20 @@ static int gdsc_enable(struct generic_pm_domain *domain) udelay(1); /* Turn on HW trigger mode if supported */ - if (sc->flags & HW_CTRL) - return gdsc_hwctrl(sc, true); + if (sc->flags & HW_CTRL) { + ret = gdsc_hwctrl(sc, true); + if (ret) + return ret; + /* + * Wait for the GDSC to go through a power down and + * up cycle. In case a firmware ends up polling status + * bits for the gdsc, it might read an 'on' status before + * the GDSC can finish the power cycle. + * We wait 1us before returning to ensure the firmware + * can't immediately poll the status bits. + */ + udelay(1); + } return 0; } @@ -204,9 +222,23 @@ static int gdsc_disable(struct generic_pm_domain *domain) /* Turn off HW trigger mode if supported */ if (sc->flags & HW_CTRL) { + unsigned int reg; + ret = gdsc_hwctrl(sc, false); if (ret < 0) return ret; + /* + * Wait for the GDSC to go through a power down and + * up cycle. In case we end up polling status + * bits for the gdsc before the power cycle is completed + * it might read an 'on' status wrongly. + */ + udelay(1); + + reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr; + ret = gdsc_poll_status(sc, reg, true); + if (ret) + return ret; } if (sc->pwrsts & PWRSTS_OFF) |