1 // SPDX-License-Identifier: GPL-2.0
4 * Copyright (c) 2019 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
7 #include <linux/clk-provider.h>
8 #include <linux/delay.h>
12 #include <dt-bindings/clock/x1000-cgu.h>
17 /* CGU register offsets */
18 #define CGU_REG_CPCCR 0x00
19 #define CGU_REG_APLL 0x10
20 #define CGU_REG_MPLL 0x14
21 #define CGU_REG_CLKGR 0x20
22 #define CGU_REG_OPCR 0x24
23 #define CGU_REG_DDRCDR 0x2c
24 #define CGU_REG_USBPCR 0x3c
25 #define CGU_REG_USBPCR1 0x48
26 #define CGU_REG_USBCDR 0x50
27 #define CGU_REG_MACCDR 0x54
28 #define CGU_REG_I2SCDR 0x60
29 #define CGU_REG_LPCDR 0x64
30 #define CGU_REG_MSC0CDR 0x68
31 #define CGU_REG_I2SCDR1 0x70
32 #define CGU_REG_SSICDR 0x74
33 #define CGU_REG_CIMCDR 0x7c
34 #define CGU_REG_PCMCDR 0x84
35 #define CGU_REG_MSC1CDR 0xa4
36 #define CGU_REG_CMP_INTR 0xb0
37 #define CGU_REG_CMP_INTRE 0xb4
38 #define CGU_REG_DRCG 0xd0
39 #define CGU_REG_CPCSR 0xd4
40 #define CGU_REG_PCMCDR1 0xe0
41 #define CGU_REG_MACPHYC 0xe8
43 /* bits within the OPCR register */
44 #define OPCR_SPENDN0 BIT(7)
45 #define OPCR_SPENDN1 BIT(6)
47 /* bits within the USBPCR register */
48 #define USBPCR_SIDDQ BIT(21)
49 #define USBPCR_OTG_DISABLE BIT(20)
51 static struct ingenic_cgu *cgu;
53 static int x1000_usb_phy_enable(struct clk_hw *hw)
55 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
56 void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR;
58 writel(readl(reg_opcr) | OPCR_SPENDN0, reg_opcr);
59 writel(readl(reg_usbpcr) & ~USBPCR_OTG_DISABLE & ~USBPCR_SIDDQ, reg_usbpcr);
63 static void x1000_usb_phy_disable(struct clk_hw *hw)
65 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
66 void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR;
68 writel(readl(reg_opcr) & ~OPCR_SPENDN0, reg_opcr);
69 writel(readl(reg_usbpcr) | USBPCR_OTG_DISABLE | USBPCR_SIDDQ, reg_usbpcr);
72 static int x1000_usb_phy_is_enabled(struct clk_hw *hw)
74 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
75 void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR;
77 return (readl(reg_opcr) & OPCR_SPENDN0) &&
78 !(readl(reg_usbpcr) & USBPCR_SIDDQ) &&
79 !(readl(reg_usbpcr) & USBPCR_OTG_DISABLE);
82 static const struct clk_ops x1000_otg_phy_ops = {
83 .enable = x1000_usb_phy_enable,
84 .disable = x1000_usb_phy_disable,
85 .is_enabled = x1000_usb_phy_is_enabled,
88 static const s8 pll_od_encoding[8] = {
89 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
92 static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
96 [X1000_CLK_EXCLK] = { "ext", CGU_CLK_EXT },
97 [X1000_CLK_RTCLK] = { "rtc", CGU_CLK_EXT },
103 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
106 .rate_multiplier = 1,
116 .od_encoding = pll_od_encoding,
117 .bypass_reg = CGU_REG_APLL,
126 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
129 .rate_multiplier = 1,
139 .od_encoding = pll_od_encoding,
140 .bypass_reg = CGU_REG_MPLL,
148 /* Custom (SoC-specific) OTG PHY */
150 [X1000_CLK_OTGPHY] = {
151 "otg_phy", CGU_CLK_CUSTOM,
152 .parents = { -1, -1, X1000_CLK_EXCLK, -1 },
153 .custom = { &x1000_otg_phy_ops },
156 /* Muxes & dividers */
158 [X1000_CLK_SCLKA] = {
159 "sclk_a", CGU_CLK_MUX,
160 .parents = { -1, X1000_CLK_EXCLK, X1000_CLK_APLL, -1 },
161 .mux = { CGU_REG_CPCCR, 30, 2 },
164 [X1000_CLK_CPUMUX] = {
165 "cpu_mux", CGU_CLK_MUX,
166 .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 },
167 .mux = { CGU_REG_CPCCR, 28, 2 },
171 "cpu", CGU_CLK_DIV | CGU_CLK_GATE,
172 .parents = { X1000_CLK_CPUMUX, -1, -1, -1 },
173 .div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 },
174 .gate = { CGU_REG_CLKGR, 30 },
177 [X1000_CLK_L2CACHE] = {
178 "l2cache", CGU_CLK_DIV,
179 .parents = { X1000_CLK_CPUMUX, -1, -1, -1 },
180 .div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 },
184 "ahb0", CGU_CLK_MUX | CGU_CLK_DIV,
185 .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 },
186 .mux = { CGU_REG_CPCCR, 26, 2 },
187 .div = { CGU_REG_CPCCR, 8, 1, 4, 21, -1, -1 },
190 [X1000_CLK_AHB2PMUX] = {
191 "ahb2_apb_mux", CGU_CLK_MUX,
192 .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 },
193 .mux = { CGU_REG_CPCCR, 24, 2 },
198 .parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 },
199 .div = { CGU_REG_CPCCR, 12, 1, 4, 20, -1, -1 },
203 "pclk", CGU_CLK_DIV | CGU_CLK_GATE,
204 .parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 },
205 .div = { CGU_REG_CPCCR, 16, 1, 4, 20, -1, -1 },
206 .gate = { CGU_REG_CLKGR, 28 },
210 "ddr", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
211 .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 },
212 .mux = { CGU_REG_DDRCDR, 30, 2 },
213 .div = { CGU_REG_DDRCDR, 0, 1, 4, 29, 28, 27 },
214 .gate = { CGU_REG_CLKGR, 31 },
218 "mac", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
219 .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
220 .mux = { CGU_REG_MACCDR, 31, 1 },
221 .div = { CGU_REG_MACCDR, 0, 1, 8, 29, 28, 27 },
222 .gate = { CGU_REG_CLKGR, 25 },
226 "lcd", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
227 .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
228 .mux = { CGU_REG_LPCDR, 31, 1 },
229 .div = { CGU_REG_LPCDR, 0, 1, 8, 28, 27, 26 },
230 .gate = { CGU_REG_CLKGR, 23 },
233 [X1000_CLK_MSCMUX] = {
234 "msc_mux", CGU_CLK_MUX,
235 .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL},
236 .mux = { CGU_REG_MSC0CDR, 31, 1 },
240 "msc0", CGU_CLK_DIV | CGU_CLK_GATE,
241 .parents = { X1000_CLK_MSCMUX, -1, -1, -1 },
242 .div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 },
243 .gate = { CGU_REG_CLKGR, 4 },
247 "msc1", CGU_CLK_DIV | CGU_CLK_GATE,
248 .parents = { X1000_CLK_MSCMUX, -1, -1, -1 },
249 .div = { CGU_REG_MSC1CDR, 0, 2, 8, 29, 28, 27 },
250 .gate = { CGU_REG_CLKGR, 5 },
254 "otg", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
255 .parents = { X1000_CLK_EXCLK, -1,
256 X1000_CLK_APLL, X1000_CLK_MPLL },
257 .mux = { CGU_REG_USBCDR, 30, 2 },
258 .div = { CGU_REG_USBCDR, 0, 1, 8, 29, 28, 27 },
259 .gate = { CGU_REG_CLKGR, 3 },
262 [X1000_CLK_SSIPLL] = {
263 "ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV,
264 .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL, -1, -1 },
265 .mux = { CGU_REG_SSICDR, 31, 1 },
266 .div = { CGU_REG_SSICDR, 0, 1, 8, 29, 28, 27 },
269 [X1000_CLK_SSIPLL_DIV2] = {
270 "ssi_pll_div2", CGU_CLK_FIXDIV,
271 .parents = { X1000_CLK_SSIPLL },
275 [X1000_CLK_SSIMUX] = {
276 "ssi_mux", CGU_CLK_MUX,
277 .parents = { X1000_CLK_EXCLK, X1000_CLK_SSIPLL_DIV2, -1, -1 },
278 .mux = { CGU_REG_SSICDR, 30, 1 },
281 /* Gate-only clocks */
285 .parents = { X1000_CLK_AHB2, -1, -1, -1 },
286 .gate = { CGU_REG_CLKGR, 0 },
289 [X1000_CLK_EFUSE] = {
290 "efuse", CGU_CLK_GATE,
291 .parents = { X1000_CLK_AHB2, -1, -1, -1 },
292 .gate = { CGU_REG_CLKGR, 1 },
297 .parents = { X1000_CLK_SSIPLL, -1, -1, -1 },
298 .gate = { CGU_REG_CLKGR, 2 },
302 "i2c0", CGU_CLK_GATE,
303 .parents = { X1000_CLK_PCLK, -1, -1, -1 },
304 .gate = { CGU_REG_CLKGR, 7 },
308 "i2c1", CGU_CLK_GATE,
309 .parents = { X1000_CLK_PCLK, -1, -1, -1 },
310 .gate = { CGU_REG_CLKGR, 8 },
314 "i2c2", CGU_CLK_GATE,
315 .parents = { X1000_CLK_PCLK, -1, -1, -1 },
316 .gate = { CGU_REG_CLKGR, 9 },
319 [X1000_CLK_UART0] = {
320 "uart0", CGU_CLK_GATE,
321 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
322 .gate = { CGU_REG_CLKGR, 14 },
325 [X1000_CLK_UART1] = {
326 "uart1", CGU_CLK_GATE,
327 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
328 .gate = { CGU_REG_CLKGR, 15 },
331 [X1000_CLK_UART2] = {
332 "uart2", CGU_CLK_GATE,
333 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
334 .gate = { CGU_REG_CLKGR, 16 },
339 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
340 .gate = { CGU_REG_CLKGR, 18 },
345 .parents = { X1000_CLK_SSIMUX, -1, -1, -1 },
346 .gate = { CGU_REG_CLKGR, 19 },
351 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
352 .gate = { CGU_REG_CLKGR, 20 },
356 "pdma", CGU_CLK_GATE,
357 .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
358 .gate = { CGU_REG_CLKGR, 21 },
362 static void __init x1000_cgu_init(struct device_node *np)
366 cgu = ingenic_cgu_new(x1000_cgu_clocks,
367 ARRAY_SIZE(x1000_cgu_clocks), np);
369 pr_err("%s: failed to initialise CGU\n", __func__);
373 retval = ingenic_cgu_register_clocks(cgu);
375 pr_err("%s: failed to register CGU Clocks\n", __func__);
379 ingenic_cgu_register_syscore_ops(cgu);
382 * CGU has some children devices, this is useful for probing children devices
383 * in the case where the device node is compatible with "simple-mfd".
385 CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-cgu", x1000_cgu_init);