diff options
author | Shengjiu Wang <shengjiu.wang@freescale.com> | 2016-11-28 13:36:55 +0800 |
---|---|---|
committer | Anson Huang <Anson.Huang@nxp.com> | 2017-06-08 19:27:17 +0800 |
commit | d29959f0f66264dce0e587641116504902b2050a (patch) | |
tree | 2ff97b35a1c97383c65ba8e2bd9336a5507f4139 /drivers/clk | |
parent | 511eacb77e74e9a23546b3afb0d2b2208eb4621c (diff) |
MLK-13568: ARM: clk-imx7ulp: Add clock tree for m4 core
SAI in M4 domain, and the clock used by SAI is in M4 domain
Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/imx/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx7ulp.c | 116 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pllv4.c | 4 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pllv5.c | 199 | ||||
-rw-r--r-- | drivers/clk/imx/clk.h | 2 |
5 files changed, 320 insertions, 2 deletions
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 8ccdaf2a8acf..bb27b178c8c1 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -15,6 +15,7 @@ obj-y += \ clk-composite.o \ clk-frac-divider.o \ clk-pllv4.o \ + clk-pllv5.o \ clk-pfdv2.o obj-$(CONFIG_SOC_IMX1) += clk-imx1.o diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index c55770f8d120..ddc51dfa9f03 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -33,6 +33,19 @@ static const char *periph_slow_sels[] = { "dummy", "osc", "mpll", "firc", "ckil" static struct clk *clks[IMX7ULP_CLK_END]; static struct clk_onecell_data clk_data; +static const char *cm4_pll_pre_sels[] = { "cm4_osc", "cm4_firc", }; +static const char *cm4_spll_pfd_sels[] = { "cm4_spll_pfd0", "cm4_spll_pfd1", "cm4_spll_pfd2", "cm4_spll_pfd3", }; +static const char *cm4_spll_sels[] = { "cm4_spll_vco", "cm4_spll_pfd_sel", }; +static const char *cm4_apll_pfd_sels[] = { "cm4_apll_pfd0", "cm4_apll_pfd1", "cm4_apll_pfd2", "cm4_apll_pfd3", }; +static const char *cm4_apll_sels[] = { "cm4_apll_vco_post_div2", "cm4_apll_pfd_sel", }; +static const char *cm4_sys_sels[] = { "cm4_dummy", "cm4_osc", "cm4_sirc", "cm4_firc", "cm4_ckil", "cm4_apll_sel", "cm4_spll_sel", "cm4_dummy", }; +static const char *cm4_periph_plat_sels[] = { "cm4_dummy", "cm4_osc", "cm4_sirc", "cm4_firc", "cm4_ckil" "cm4_plat_div", "cm4_spll_sel", "cm4_spll_pfd3", }; +static const char *cm4_periph_slow_sels[] = { "cm4_dummy", "cm4_osc", "cm4_sirc", "cm4_firc", "cm4_ckil", "cm4_bus_div", "cm4_spll_pfd2", "cm4_apll_pfd0_pre_div", }; +static const char *scg0_clkout_sels[] = { "dummy", "cm4_osc", "cm4_sirc", "cm4_firc", "cm4_ckil", "cm4_apll_sel", "cm4_spll_sel", "dummy"}; +static struct clk *clks_cm4[IMX7ULP_CM4_CLK_END]; +static struct clk_onecell_data clk_data_cm4; + + static int const clks_init_on[] __initconst = { IMX7ULP_CLK_BUS_DIV, IMX7ULP_CLK_PLAT_DIV, @@ -168,3 +181,106 @@ static void __init imx7ulp_clocks_init(struct device_node *scg_node) } CLK_OF_DECLARE(imx7ulp, "fsl,imx7ulp-scg1", imx7ulp_clocks_init); + +static struct clk_div_table apll_pfd0_div_table[] = { + { .val = 1, .div = 1, }, + { .val = 0, .div = 2, }, + { /* sentinel */ } +}; + +static u32 share_count_sai0; +static u32 share_count_sai1; + +static void __init imx7ulp_cm4_clocks_init(struct device_node *scg_node) +{ + struct device_node *np, *np_sim; + void __iomem *base; + void __iomem *base_sim; + + clks_cm4[IMX7ULP_CM4_CLK_DUMMY] = imx_clk_fixed("cm4_dummy", 0); + + clks_cm4[IMX7ULP_CM4_CLK_CKIL] = of_clk_get_by_name(scg_node, "cm4_ckil"); + clks_cm4[IMX7ULP_CM4_CLK_OSC] = of_clk_get_by_name(scg_node, "cm4_osc"); + clks_cm4[IMX7ULP_CM4_CLK_SIRC] = of_clk_get_by_name(scg_node, "cm4_sirc"); + clks_cm4[IMX7ULP_CM4_CLK_FIRC] = of_clk_get_by_name(scg_node, "cm4_firc"); + + np = scg_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + np_sim = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-sim"); + base_sim = of_iomap(np_sim, 0); + WARN_ON(!base_sim); + + clks_cm4[IMX7ULP_CM4_CLK_SPLL_VCO_PRE_SEL] = imx_clk_mux("cm4_spll_vco_pre_sel", base + 0x608, 0, 1, cm4_pll_pre_sels, ARRAY_SIZE(cm4_pll_pre_sels)); + clks_cm4[IMX7ULP_CM4_CLK_APLL_VCO_PRE_SEL] = imx_clk_mux("cm4_apll_vco_pre_sel", base + 0x508, 0, 1, cm4_pll_pre_sels, ARRAY_SIZE(cm4_pll_pre_sels)); + /* name parent_name reg shift width */ + clks_cm4[IMX7ULP_CM4_CLK_SPLL_VCO_PRE_DIV] = imx_clk_divider("cm4_spll_vco_pre_div", "cm4_spll_vco_pre_sel", base + 0x608, 8, 3); + clks_cm4[IMX7ULP_CM4_CLK_APLL_VCO_PRE_DIV] = imx_clk_divider("cm4_apll_vco_pre_div", "cm4_apll_vco_pre_sel", base + 0x508, 8, 3); + /* name parent_name base*/ + clks_cm4[IMX7ULP_CM4_CLK_SPLL_VCO] = imx_clk_pllv5("cm4_spll_vco", "cm4_spll_vco_pre_div", base + 0x600); + clks_cm4[IMX7ULP_CM4_CLK_APLL_VCO] = imx_clk_pllv4("cm4_apll_vco", "cm4_apll_vco_pre_div", base + 0x500); + + clks_cm4[IMX7ULP_CM4_CLK_APLL_VCO_POST_DIV1] = imx_clk_divider("cm4_apll_vco_post_div1", "cm4_apll_vco", base + 0x508, 24, 4); + clks_cm4[IMX7ULP_CM4_CLK_APLL_VCO_POST_DIV2] = imx_clk_divider("cm4_apll_vco_post_div2", "cm4_apll_vco_post_div1", base + 0x508, 28, 4); + + /* SPLL PFDs */ + clks_cm4[IMX7ULP_CM4_CLK_SPLL_PFD0] = imx_clk_pfdv2("cm4_spll_pfd0", "cm4_spll_vco", base + 0x60C, 0); + clks_cm4[IMX7ULP_CM4_CLK_SPLL_PFD1] = imx_clk_pfdv2("cm4_spll_pfd1", "cm4_spll_vco", base + 0x60C, 1); + clks_cm4[IMX7ULP_CM4_CLK_SPLL_PFD2] = imx_clk_pfdv2("cm4_spll_pfd2", "cm4_spll_vco", base + 0x60C, 2); + clks_cm4[IMX7ULP_CM4_CLK_SPLL_PFD3] = imx_clk_pfdv2("cm4_spll_pfd3", "cm4_spll_vco", base + 0x60C, 3); + /* APLL PFDs */ + clks_cm4[IMX7ULP_CM4_CLK_APLL_PFD0] = imx_clk_pfdv2("cm4_apll_pfd0", "cm4_apll_vco", base + 0x50C, 0); + clks_cm4[IMX7ULP_CM4_CLK_APLL_PFD1] = imx_clk_pfdv2("cm4_apll_pfd1", "cm4_apll_vco", base + 0x50C, 1); + clks_cm4[IMX7ULP_CM4_CLK_APLL_PFD2] = imx_clk_pfdv2("cm4_apll_pfd2", "cm4_apll_vco", base + 0x50C, 2); + clks_cm4[IMX7ULP_CM4_CLK_APLL_PFD3] = imx_clk_pfdv2("cm4_apll_pfd3", "cm4_apll_vco", base + 0x50C, 3); + + clks_cm4[IMX7ULP_CM4_CLK_APLL_PFD0_PRE_DIV] = clk_register_divider_table(NULL, "cm4_apll_pfd0_pre_div", "cm4_apll_pfd0", CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base_sim + 0x2c, 5, 1, 0, apll_pfd0_div_table, &imx_ccm_lock); + + clks_cm4[IMX7ULP_CM4_CLK_SPLL_PFD_SEL] = imx_clk_mux("cm4_spll_pfd_sel", base + 0x608, 14, 2, cm4_spll_pfd_sels, ARRAY_SIZE(cm4_spll_pfd_sels)); + clks_cm4[IMX7ULP_CM4_CLK_APLL_PFD_SEL] = imx_clk_mux("cm4_apll_pfd_sel", base + 0x508, 14, 2, cm4_apll_pfd_sels, ARRAY_SIZE(cm4_apll_pfd_sels)); + + clks_cm4[IMX7ULP_CM4_CLK_SPLL_SEL] = imx_clk_mux("cm4_spll_sel", base + 0x608, 1, 1, cm4_spll_sels, ARRAY_SIZE(cm4_spll_sels)); + clks_cm4[IMX7ULP_CM4_CLK_APLL_SEL] = imx_clk_mux("cm4_apll_sel", base + 0x508, 1, 1, cm4_apll_sels, ARRAY_SIZE(cm4_apll_sels)); + + clks_cm4[IMX7ULP_CM4_CLK_SYS_SEL] = imx_clk_mux("cm4_sys_sel", base + 0x14, 24, 4, cm4_sys_sels, ARRAY_SIZE(cm4_sys_sels)); + + clks_cm4[IMX7ULP_CM4_CLK_CORE_DIV] = imx_clk_divider("cm4_core_div", "cm4_sys_sel", base + 0x14, 16, 4); + clks_cm4[IMX7ULP_CM4_CLK_PLAT_DIV] = imx_clk_divider("cm4_plat_div", "cm4_core_div", base + 0x14, 12, 4); + clks_cm4[IMX7ULP_CM4_CLK_BUS_DIV] = imx_clk_divider("cm4_bus_div", "cm4_core_div", base + 0x14, 4, 4); + clks_cm4[IMX7ULP_CM4_CLK_SLOW_DIV] = imx_clk_divider("cm4_slow_div", "cm4_core_div", base + 0x14, 0, 4); + + clks_cm4[IMX7ULP_CLK_SCG0_CLKOUT] = imx_clk_mux("scg0_clkout", base + 0x20, 24, 4, scg0_clkout_sels, ARRAY_SIZE(scg0_clkout_sels)); + + /* PCG0 */ + np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pcc0"); + base = of_iomap(np, 0); + WARN_ON(!base); + + clks_cm4[IMX7ULP_CM4_CLK_SAI0_SEL] = imx_clk_mux("cm4_sai0_sel", base + 0xDC, 24, 3, cm4_periph_slow_sels, ARRAY_SIZE(cm4_periph_slow_sels)); + clks_cm4[IMX7ULP_CM4_CLK_SAI0_DIV] = imx_clk_divider("cm4_sai0_div", "cm4_sai0_sel", base + 0xDC, 0, 8); + clks_cm4[IMX7ULP_CM4_CLK_SAI0_ROOT] = imx_clk_gate2_shared("cm4_sai0_root", "cm4_sai0_div", base + 0xDC, 30, &share_count_sai0); + clks_cm4[IMX7ULP_CM4_CLK_SAI0_IPG] = imx_clk_gate2_shared("cm4_sai0_ipg", "cm4_bus_div", base + 0xDC, 30, &share_count_sai0); + + /* PCG1 */ + np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pcc1"); + base = of_iomap(np, 0); + WARN_ON(!base); + + + clks_cm4[IMX7ULP_CM4_CLK_SAI1_SEL] = imx_clk_mux("cm4_sai1_sel", base + 0xA8, 24, 3, cm4_periph_slow_sels, ARRAY_SIZE(cm4_periph_slow_sels)); + clks_cm4[IMX7ULP_CM4_CLK_SAI1_DIV] = imx_clk_divider("cm4_sai1_div", "cm4_sai1_sel", base + 0xA8, 0, 8); + clks_cm4[IMX7ULP_CM4_CLK_SAI1_ROOT] = imx_clk_gate2_shared("cm4_sai1_root", "cm4_sai1_div", base + 0xA8, 30, &share_count_sai1); + clks_cm4[IMX7ULP_CM4_CLK_SAI1_IPG] = imx_clk_gate2_shared("cm4_sai1_ipg", "cm4_bus_div", base + 0xA8, 30, &share_count_sai1); + + imx_check_clocks(clks_cm4, ARRAY_SIZE(clks_cm4)); + + clk_data_cm4.clks = clks_cm4; + clk_data_cm4.clk_num = ARRAY_SIZE(clks_cm4); + of_clk_add_provider(scg_node, of_clk_src_onecell_get, &clk_data_cm4); + + imx_clk_prepare_enable(clks_cm4[IMX7ULP_CM4_CLK_SYS_SEL]); + + pr_info("i.MX7ULP cm4 clock tree init.\n"); +} +CLK_OF_DECLARE(imx7ulp_cm4, "fsl,imx7ulp-scg0", imx7ulp_cm4_clocks_init); diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c index 9c5829c7a11f..4f4c02bd6d4e 100644 --- a/drivers/clk/imx/clk-pllv4.c +++ b/drivers/clk/imx/clk-pllv4.c @@ -1,5 +1,5 @@ /* - * Copyright 2016 Freescale Semiconductor, Inc. + * Copyright (C) 2016 Freescale Semiconductor, Inc. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -96,7 +96,7 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, val = readl_relaxed(pll->base + pll->cfg_offset); val &= ~pll->div_mask; - val |= div; + val |= (div << pll->div_shift); writel_relaxed(val, pll->base + pll->cfg_offset); writel_relaxed(mfn, pll->base + pll->num_offset); writel_relaxed(mfd, pll->base + pll->denom_offset); diff --git a/drivers/clk/imx/clk-pllv5.c b/drivers/clk/imx/clk-pllv5.c new file mode 100644 index 000000000000..c45704c7942e --- /dev/null +++ b/drivers/clk/imx/clk-pllv5.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> + +#include "clk.h" + +#define PLL_EN BIT(0) +#define BP_PLL_DIV 16 +#define BM_PLL_DIV (0x7 << 16) +#define PLL_CFG_OFFSET 0x08 + +struct clk_pllv5 { + struct clk_hw hw; + void __iomem *base; + u32 div_mask; + u32 div_shift; + u32 cfg_offset; +}; + +#define to_clk_pllv5(__hw) container_of(__hw, struct clk_pllv5, hw) + +static unsigned long clk_pllv5_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv5 *pll = to_clk_pllv5(hw); + u32 val = (readl_relaxed(pll->base + pll->cfg_offset) & pll->div_mask) >> pll->div_shift; + u32 div; + + switch (val) { + case 1: + div = 15; + break; + case 2: + div = 16; + break; + case 3: + div = 20; + break; + case 4: + div = 22; + break; + case 5: + div = 25; + break; + case 6: + div = 30; + break; + default: + div = 20; + break; + } + + return parent_rate * div; +} + +static long clk_pllv5_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + u32 div; + + div = rate / parent_rate; + + if (div == 15 || div == 16 || + div == 20 || div == 22 || + div == 25 || div == 30) + return parent_rate * div; + else + return parent_rate * 20; +} + +static int clk_pllv5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv5 *pll = to_clk_pllv5(hw); + unsigned long min_rate = parent_rate * 15; + unsigned long max_rate = parent_rate * 30; + u32 val, div, reg; + + if (rate < min_rate || rate > max_rate) + return -EINVAL; + + div = rate / parent_rate; + + switch (div) { + case 15: + val = 1; + break; + case 16: + val = 2; + break; + case 20: + val = 3; + break; + case 22: + val = 4; + break; + case 25: + val = 5; + break; + case 30: + val = 6; + break; + default: + val = 3; + break; + } + + reg = readl_relaxed(pll->base + pll->cfg_offset); + reg &= ~pll->div_mask; + reg |= (val << pll->div_shift); + writel_relaxed(val, pll->base + pll->cfg_offset); + + return 0; +} + +static int clk_pllv5_enable(struct clk_hw *hw) +{ + u32 val; + struct clk_pllv5 *pll = to_clk_pllv5(hw); + + val = readl_relaxed(pll->base); + val |= PLL_EN; + writel_relaxed(val, pll->base); + + return 0; +} + +static void clk_pllv5_disable(struct clk_hw *hw) +{ + u32 val; + struct clk_pllv5 *pll = to_clk_pllv5(hw); + + val = readl_relaxed(pll->base); + val &= ~PLL_EN; + writel_relaxed(val, pll->base); +} + +static int clk_pllv5_is_enabled(struct clk_hw *hw) +{ + struct clk_pllv5 *pll = to_clk_pllv5(hw); + + if (readl_relaxed(pll->base) & PLL_EN) + return 0; + + return 1; +} + +static const struct clk_ops clk_pllv5_ops = { + .recalc_rate = clk_pllv5_recalc_rate, + .round_rate = clk_pllv5_round_rate, + .set_rate = clk_pllv5_set_rate, + .enable = clk_pllv5_enable, + .disable = clk_pllv5_disable, + .is_enabled = clk_pllv5_is_enabled, +}; + +struct clk *imx_clk_pllv5(const char *name, const char *parent_name, + void __iomem *base) +{ + struct clk_pllv5 *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + pll->div_mask = BM_PLL_DIV; + pll->div_shift = BP_PLL_DIV; + pll->cfg_offset = PLL_CFG_OFFSET; + + init.name = name; + init.ops = &clk_pllv5_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index d758dd42d5bd..cf41e1e2cc11 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -85,6 +85,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, struct clk *imx_clk_pllv4(const char *name, const char *parent_name, void __iomem *base); +struct clk *imx_clk_pllv5(const char *name, const char *parent_name, + void __iomem *base); struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, |