summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
authorShengjiu Wang <shengjiu.wang@freescale.com>2016-11-28 13:36:55 +0800
committerAnson Huang <Anson.Huang@nxp.com>2017-06-08 19:27:17 +0800
commitd29959f0f66264dce0e587641116504902b2050a (patch)
tree2ff97b35a1c97383c65ba8e2bd9336a5507f4139 /drivers/clk
parent511eacb77e74e9a23546b3afb0d2b2208eb4621c (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/Makefile1
-rw-r--r--drivers/clk/imx/clk-imx7ulp.c116
-rw-r--r--drivers/clk/imx/clk-pllv4.c4
-rw-r--r--drivers/clk/imx/clk-pllv5.c199
-rw-r--r--drivers/clk/imx/clk.h2
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,