summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorA.s. Dong <aisheng.dong@nxp.com>2018-11-14 08:02:08 -0500
committerStephen Boyd <sboyd@kernel.org>2018-12-03 14:31:36 -0500
commitb1260067ac3dd5dcd40bcbcb2cc116a9f8b5016b (patch)
treefb56d8eeded75262f1cc09a4aecf41ccd13877bf
parent3b315214e09167c2dbcc5d9d5c999237e47ed182 (diff)
clk: imx: add imx7ulp clk driver
i.MX7ULP Clock functions are under joint control of the System Clock Generation (SCG) modules, Peripheral Clock Control (PCC) modules, and Core Mode Controller (CMC)1 blocks The clocking scheme provides clear separation between M4 domain and A7 domain. Except for a few clock sources shared between two domains, such as the System Oscillator clock, the Slow IRC (SIRC), and and the Fast IRC clock (FIRCLK), clock sources and clock management are separated and contained within each domain. M4 clock management consists of SCG0, PCC0, PCC1, and CMC0 modules. A7 clock management consists of SCG1, PCC2, PCC3, and CMC1 modules. This driver only adds clock support in A7 domain. Note that most clocks required to be operated when gated, e.g. pll, pfd, pcc. And more special cases that scs/ddr/nic mux selecting different clock source requires that clock to be enabled first, then we need set CLK_OPS_PARENT_ENABLE flag for them properly. Cc: Stephen Boyd <sboyd@codeaurora.org> Cc: Michael Turquette <mturquette@baylibre.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Anson Huang <Anson.Huang@nxp.com> Cc: Bai Ping <ping.bai@nxp.com> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r--drivers/clk/imx/Makefile1
-rw-r--r--drivers/clk/imx/clk-imx7ulp.c220
2 files changed, 221 insertions, 0 deletions
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index a5cab3e5b7aa..615b413c5681 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -30,4 +30,5 @@ obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o
30obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o 30obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o
31obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o 31obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o
32obj-$(CONFIG_SOC_IMX7D) += clk-imx7d.o 32obj-$(CONFIG_SOC_IMX7D) += clk-imx7d.o
33obj-$(CONFIG_SOC_IMX7ULP) += clk-imx7ulp.o
33obj-$(CONFIG_SOC_VF610) += clk-vf610.o 34obj-$(CONFIG_SOC_VF610) += clk-vf610.o
diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c
new file mode 100644
index 000000000000..3b7507ff7869
--- /dev/null
+++ b/drivers/clk/imx/clk-imx7ulp.c
@@ -0,0 +1,220 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
4 * Copyright 2017~2018 NXP
5 *
6 * Author: Dong Aisheng <aisheng.dong@nxp.com>
7 *
8 */
9
10#include <dt-bindings/clock/imx7ulp-clock.h>
11#include <linux/clk.h>
12#include <linux/err.h>
13#include <linux/init.h>
14#include <linux/io.h>
15#include <linux/of.h>
16#include <linux/of_address.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19
20#include "clk.h"
21
22static const char * const pll_pre_sels[] = { "sosc", "firc", };
23static const char * const spll_pfd_sels[] = { "spll_pfd0", "spll_pfd1", "spll_pfd2", "spll_pfd3", };
24static const char * const spll_sels[] = { "spll", "spll_pfd_sel", };
25static const char * const apll_pfd_sels[] = { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", };
26static const char * const apll_sels[] = { "apll", "apll_pfd_sel", };
27static const char * const scs_sels[] = { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "upll", };
28static const char * const ddr_sels[] = { "apll_pfd_sel", "upll", };
29static const char * const nic_sels[] = { "firc", "ddr_clk", };
30static const char * const periph_plat_sels[] = { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", };
31static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "mpll", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", };
32
33/* used by sosc/sirc/firc/ddr/spll/apll dividers */
34static const struct clk_div_table ulp_div_table[] = {
35 { .val = 1, .div = 1, },
36 { .val = 2, .div = 2, },
37 { .val = 3, .div = 4, },
38 { .val = 4, .div = 8, },
39 { .val = 5, .div = 16, },
40 { .val = 6, .div = 32, },
41 { .val = 7, .div = 64, },
42};
43
44static void __init imx7ulp_clk_scg1_init(struct device_node *np)
45{
46 struct clk_hw_onecell_data *clk_data;
47 struct clk_hw **clks;
48 void __iomem *base;
49
50 clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
51 IMX7ULP_CLK_SCG1_END, GFP_KERNEL);
52 if (!clk_data)
53 return;
54
55 clk_data->num = IMX7ULP_CLK_SCG1_END;
56 clks = clk_data->hws;
57
58 clks[IMX7ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
59
60 clks[IMX7ULP_CLK_ROSC] = imx_obtain_fixed_clk_hw(np, "rosc");
61 clks[IMX7ULP_CLK_SOSC] = imx_obtain_fixed_clk_hw(np, "sosc");
62 clks[IMX7ULP_CLK_SIRC] = imx_obtain_fixed_clk_hw(np, "sirc");
63 clks[IMX7ULP_CLK_FIRC] = imx_obtain_fixed_clk_hw(np, "firc");
64 clks[IMX7ULP_CLK_MIPI_PLL] = imx_obtain_fixed_clk_hw(np, "mpll");
65 clks[IMX7ULP_CLK_UPLL] = imx_obtain_fixed_clk_hw(np, "upll");
66
67 /* SCG1 */
68 base = of_iomap(np, 0);
69 WARN_ON(!base);
70
71 /* NOTE: xPLL config can't be changed when xPLL is enabled */
72 clks[IMX7ULP_CLK_APLL_PRE_SEL] = imx_clk_hw_mux_flags("apll_pre_sel", base + 0x508, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
73 clks[IMX7ULP_CLK_SPLL_PRE_SEL] = imx_clk_hw_mux_flags("spll_pre_sel", base + 0x608, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
74
75 /* name parent_name reg shift width flags */
76 clks[IMX7ULP_CLK_APLL_PRE_DIV] = imx_clk_hw_divider_flags("apll_pre_div", "apll_pre_sel", base + 0x508, 8, 3, CLK_SET_RATE_GATE);
77 clks[IMX7ULP_CLK_SPLL_PRE_DIV] = imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608, 8, 3, CLK_SET_RATE_GATE);
78
79 /* name parent_name base */
80 clks[IMX7ULP_CLK_APLL] = imx_clk_pllv4("apll", "apll_pre_div", base + 0x500);
81 clks[IMX7ULP_CLK_SPLL] = imx_clk_pllv4("spll", "spll_pre_div", base + 0x600);
82
83 /* APLL PFDs */
84 clks[IMX7ULP_CLK_APLL_PFD0] = imx_clk_pfdv2("apll_pfd0", "apll", base + 0x50c, 0);
85 clks[IMX7ULP_CLK_APLL_PFD1] = imx_clk_pfdv2("apll_pfd1", "apll", base + 0x50c, 1);
86 clks[IMX7ULP_CLK_APLL_PFD2] = imx_clk_pfdv2("apll_pfd2", "apll", base + 0x50c, 2);
87 clks[IMX7ULP_CLK_APLL_PFD3] = imx_clk_pfdv2("apll_pfd3", "apll", base + 0x50c, 3);
88
89 /* SPLL PFDs */
90 clks[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_pfdv2("spll_pfd0", "spll", base + 0x60C, 0);
91 clks[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_pfdv2("spll_pfd1", "spll", base + 0x60C, 1);
92 clks[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_pfdv2("spll_pfd2", "spll", base + 0x60C, 2);
93 clks[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_pfdv2("spll_pfd3", "spll", base + 0x60C, 3);
94
95 /* PLL Mux */
96 clks[IMX7ULP_CLK_APLL_PFD_SEL] = imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
97 clks[IMX7ULP_CLK_SPLL_PFD_SEL] = imx_clk_hw_mux_flags("spll_pfd_sel", base + 0x608, 14, 2, spll_pfd_sels, ARRAY_SIZE(spll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
98 clks[IMX7ULP_CLK_APLL_SEL] = imx_clk_hw_mux_flags("apll_sel", base + 0x508, 1, 1, apll_sels, ARRAY_SIZE(apll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
99 clks[IMX7ULP_CLK_SPLL_SEL] = imx_clk_hw_mux_flags("spll_sel", base + 0x608, 1, 1, spll_sels, ARRAY_SIZE(spll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
100
101 clks[IMX7ULP_CLK_SPLL_BUS_CLK] = imx_clk_divider_gate("spll_bus_clk", "spll_sel", CLK_SET_RATE_GATE, base + 0x604, 8, 3, 0, ulp_div_table, &imx_ccm_lock);
102
103 /* scs/ddr/nic select different clock source requires that clock to be enabled first */
104 clks[IMX7ULP_CLK_SYS_SEL] = imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels));
105 clks[IMX7ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels));
106 clks[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 1, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
107
108 clks[IMX7ULP_CLK_CORE_DIV] = imx_clk_hw_divider_flags("divcore", "scs_sel", base + 0x14, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
109
110 clks[IMX7ULP_CLK_DDR_DIV] = imx_clk_divider_gate("ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3,
111 0, ulp_div_table, &imx_ccm_lock);
112
113 clks[IMX7ULP_CLK_NIC0_DIV] = imx_clk_hw_divider_flags("nic0_clk", "nic_sel", base + 0x40, 24, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
114 clks[IMX7ULP_CLK_NIC1_DIV] = imx_clk_hw_divider_flags("nic1_clk", "nic0_clk", base + 0x40, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
115 clks[IMX7ULP_CLK_NIC1_BUS_DIV] = imx_clk_hw_divider_flags("nic1_bus_clk", "nic1_clk", base + 0x40, 4, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
116
117 clks[IMX7ULP_CLK_GPU_DIV] = imx_clk_hw_divider("gpu_clk", "nic0_clk", base + 0x40, 20, 4);
118
119 clks[IMX7ULP_CLK_SOSC_BUS_CLK] = imx_clk_divider_gate("sosc_bus_clk", "sosc", 0, base + 0x104, 8, 3,
120 CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock);
121 clks[IMX7ULP_CLK_FIRC_BUS_CLK] = imx_clk_divider_gate("firc_bus_clk", "firc", 0, base + 0x304, 8, 3,
122 CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock);
123
124 imx_check_clk_hws(clks, clk_data->num);
125
126 of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
127}
128CLK_OF_DECLARE(imx7ulp_clk_scg1, "fsl,imx7ulp-scg1", imx7ulp_clk_scg1_init);
129
130static void __init imx7ulp_clk_pcc2_init(struct device_node *np)
131{
132 struct clk_hw_onecell_data *clk_data;
133 struct clk_hw **clks;
134 void __iomem *base;
135
136 clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
137 IMX7ULP_CLK_PCC2_END, GFP_KERNEL);
138 if (!clk_data)
139 return;
140
141 clk_data->num = IMX7ULP_CLK_PCC2_END;
142 clks = clk_data->hws;
143
144 /* PCC2 */
145 base = of_iomap(np, 0);
146 WARN_ON(!base);
147
148 clks[IMX7ULP_CLK_DMA1] = imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30);
149 clks[IMX7ULP_CLK_RGPIO2P1] = imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30);
150 clks[IMX7ULP_CLK_DMA_MUX1] = imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30);
151 clks[IMX7ULP_CLK_SNVS] = imx_clk_hw_gate("snvs", "nic1_bus_clk", base + 0x8c, 30);
152 clks[IMX7ULP_CLK_CAAM] = imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30);
153 clks[IMX7ULP_CLK_LPTPM4] = imx7ulp_clk_composite("lptpm4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
154 clks[IMX7ULP_CLK_LPTPM5] = imx7ulp_clk_composite("lptpm5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
155 clks[IMX7ULP_CLK_LPIT1] = imx7ulp_clk_composite("lpit1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
156 clks[IMX7ULP_CLK_LPSPI2] = imx7ulp_clk_composite("lpspi2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa4);
157 clks[IMX7ULP_CLK_LPSPI3] = imx7ulp_clk_composite("lpspi3", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa8);
158 clks[IMX7ULP_CLK_LPI2C4] = imx7ulp_clk_composite("lpi2c4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xac);
159 clks[IMX7ULP_CLK_LPI2C5] = imx7ulp_clk_composite("lpi2c5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb0);
160 clks[IMX7ULP_CLK_LPUART4] = imx7ulp_clk_composite("lpuart4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb4);
161 clks[IMX7ULP_CLK_LPUART5] = imx7ulp_clk_composite("lpuart5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb8);
162 clks[IMX7ULP_CLK_FLEXIO1] = imx7ulp_clk_composite("flexio1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xc4);
163 clks[IMX7ULP_CLK_USB0] = imx7ulp_clk_composite("usb0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xcc);
164 clks[IMX7ULP_CLK_USB1] = imx7ulp_clk_composite("usb1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xd0);
165 clks[IMX7ULP_CLK_USB_PHY] = imx_clk_hw_gate("usb_phy", "nic1_bus_clk", base + 0xd4, 30);
166 clks[IMX7ULP_CLK_USDHC0] = imx7ulp_clk_composite("usdhc0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xdc);
167 clks[IMX7ULP_CLK_USDHC1] = imx7ulp_clk_composite("usdhc1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xe0);
168 clks[IMX7ULP_CLK_WDG1] = imx7ulp_clk_composite("wdg1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xf4);
169 clks[IMX7ULP_CLK_WDG2] = imx7ulp_clk_composite("sdg2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0x10c);
170
171 imx_check_clk_hws(clks, clk_data->num);
172
173 of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
174}
175CLK_OF_DECLARE(imx7ulp_clk_pcc2, "fsl,imx7ulp-pcc2", imx7ulp_clk_pcc2_init);
176
177static void __init imx7ulp_clk_pcc3_init(struct device_node *np)
178{
179 struct clk_hw_onecell_data *clk_data;
180 struct clk_hw **clks;
181 void __iomem *base;
182
183 clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) *
184 IMX7ULP_CLK_PCC3_END, GFP_KERNEL);
185 if (!clk_data)
186 return;
187
188 clk_data->num = IMX7ULP_CLK_PCC3_END;
189 clks = clk_data->hws;
190
191 /* PCC3 */
192 base = of_iomap(np, 0);
193 WARN_ON(!base);
194
195 clks[IMX7ULP_CLK_LPTPM6] = imx7ulp_clk_composite("lptpm6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x84);
196 clks[IMX7ULP_CLK_LPTPM7] = imx7ulp_clk_composite("lptpm7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x88);
197
198 clks[IMX7ULP_CLK_MMDC] = clk_hw_register_gate(NULL, "mmdc", "nic1_clk", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
199 base + 0xac, 30, 0, &imx_ccm_lock);
200 clks[IMX7ULP_CLK_LPI2C6] = imx7ulp_clk_composite("lpi2c6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x90);
201 clks[IMX7ULP_CLK_LPI2C7] = imx7ulp_clk_composite("lpi2c7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
202 clks[IMX7ULP_CLK_LPUART6] = imx7ulp_clk_composite("lpuart6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
203 clks[IMX7ULP_CLK_LPUART7] = imx7ulp_clk_composite("lpuart7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c);
204 clks[IMX7ULP_CLK_DSI] = imx7ulp_clk_composite("dsi", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xa4);
205 clks[IMX7ULP_CLK_LCDIF] = imx7ulp_clk_composite("lcdif", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xa8);
206
207 clks[IMX7ULP_CLK_VIU] = imx_clk_hw_gate("viu", "nic1_clk", base + 0xa0, 30);
208 clks[IMX7ULP_CLK_PCTLC] = imx_clk_hw_gate("pctlc", "nic1_bus_clk", base + 0xb8, 30);
209 clks[IMX7ULP_CLK_PCTLD] = imx_clk_hw_gate("pctld", "nic1_bus_clk", base + 0xbc, 30);
210 clks[IMX7ULP_CLK_PCTLE] = imx_clk_hw_gate("pctle", "nic1_bus_clk", base + 0xc0, 30);
211 clks[IMX7ULP_CLK_PCTLF] = imx_clk_hw_gate("pctlf", "nic1_bus_clk", base + 0xc4, 30);
212
213 clks[IMX7ULP_CLK_GPU3D] = imx7ulp_clk_composite("gpu3d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x140);
214 clks[IMX7ULP_CLK_GPU2D] = imx7ulp_clk_composite("gpu2d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x144);
215
216 imx_check_clk_hws(clks, clk_data->num);
217
218 of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
219}
220CLK_OF_DECLARE(imx7ulp_clk_pcc3, "fsl,imx7ulp-pcc3", imx7ulp_clk_pcc3_init);