2 * Ingenic JZ4740 SoC CGU driver
4 * Copyright (c) 2015 Imagination Technologies
5 * Author: Paul Burton <paul.burton@mips.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
18 #include <linux/clk-provider.h>
19 #include <linux/delay.h>
21 #include <dt-bindings/clock/jz4740-cgu.h>
22 #include <asm/mach-jz4740/clock.h>
25 /* CGU register offsets */
26 #define CGU_REG_CPCCR 0x00
27 #define CGU_REG_LCR 0x04
28 #define CGU_REG_CPPCR 0x10
29 #define CGU_REG_CLKGR 0x20
30 #define CGU_REG_SCR 0x24
31 #define CGU_REG_I2SCDR 0x60
32 #define CGU_REG_LPCDR 0x64
33 #define CGU_REG_MSCCDR 0x68
34 #define CGU_REG_UHCCDR 0x6c
35 #define CGU_REG_SSICDR 0x74
37 /* bits within a PLL control register */
38 #define PLLCTL_M_SHIFT 23
39 #define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT)
40 #define PLLCTL_N_SHIFT 18
41 #define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT)
42 #define PLLCTL_OD_SHIFT 16
43 #define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT)
44 #define PLLCTL_STABLE (1 << 10)
45 #define PLLCTL_BYPASS (1 << 9)
46 #define PLLCTL_ENABLE (1 << 8)
48 /* bits within the LCR register */
49 #define LCR_SLEEP (1 << 0)
51 /* bits within the CLKGR register */
52 #define CLKGR_UDC (1 << 11)
54 static struct ingenic_cgu
*cgu
;
56 static const s8 pll_od_encoding
[4] = {
60 static const struct ingenic_cgu_clk_info jz4740_cgu_clocks
[] = {
64 [JZ4740_CLK_EXT
] = { "ext", CGU_CLK_EXT
},
65 [JZ4740_CLK_RTC
] = { "rtc", CGU_CLK_EXT
},
69 .parents
= { JZ4740_CLK_EXT
, -1, -1, -1 },
81 .od_encoding
= pll_od_encoding
,
88 /* Muxes & dividers */
90 [JZ4740_CLK_PLL_HALF
] = {
91 "pll half", CGU_CLK_DIV
,
92 .parents
= { JZ4740_CLK_PLL
, -1, -1, -1 },
93 .div
= { CGU_REG_CPCCR
, 21, 1, 1, -1, -1, -1 },
98 .parents
= { JZ4740_CLK_PLL
, -1, -1, -1 },
99 .div
= { CGU_REG_CPCCR
, 0, 1, 4, 22, -1, -1 },
102 [JZ4740_CLK_HCLK
] = {
104 .parents
= { JZ4740_CLK_PLL
, -1, -1, -1 },
105 .div
= { CGU_REG_CPCCR
, 4, 1, 4, 22, -1, -1 },
108 [JZ4740_CLK_PCLK
] = {
110 .parents
= { JZ4740_CLK_PLL
, -1, -1, -1 },
111 .div
= { CGU_REG_CPCCR
, 8, 1, 4, 22, -1, -1 },
114 [JZ4740_CLK_MCLK
] = {
116 .parents
= { JZ4740_CLK_PLL
, -1, -1, -1 },
117 .div
= { CGU_REG_CPCCR
, 12, 1, 4, 22, -1, -1 },
121 "lcd", CGU_CLK_DIV
| CGU_CLK_GATE
,
122 .parents
= { JZ4740_CLK_PLL_HALF
, -1, -1, -1 },
123 .div
= { CGU_REG_CPCCR
, 16, 1, 5, 22, -1, -1 },
124 .gate
= { CGU_REG_CLKGR
, 10 },
127 [JZ4740_CLK_LCD_PCLK
] = {
128 "lcd_pclk", CGU_CLK_DIV
,
129 .parents
= { JZ4740_CLK_PLL_HALF
, -1, -1, -1 },
130 .div
= { CGU_REG_LPCDR
, 0, 1, 11, -1, -1, -1 },
134 "i2s", CGU_CLK_MUX
| CGU_CLK_DIV
| CGU_CLK_GATE
,
135 .parents
= { JZ4740_CLK_EXT
, JZ4740_CLK_PLL_HALF
, -1, -1 },
136 .mux
= { CGU_REG_CPCCR
, 31, 1 },
137 .div
= { CGU_REG_I2SCDR
, 0, 1, 8, -1, -1, -1 },
138 .gate
= { CGU_REG_CLKGR
, 6 },
142 "spi", CGU_CLK_MUX
| CGU_CLK_DIV
| CGU_CLK_GATE
,
143 .parents
= { JZ4740_CLK_EXT
, JZ4740_CLK_PLL
, -1, -1 },
144 .mux
= { CGU_REG_SSICDR
, 31, 1 },
145 .div
= { CGU_REG_SSICDR
, 0, 1, 4, -1, -1, -1 },
146 .gate
= { CGU_REG_CLKGR
, 4 },
150 "mmc", CGU_CLK_DIV
| CGU_CLK_GATE
,
151 .parents
= { JZ4740_CLK_PLL_HALF
, -1, -1, -1 },
152 .div
= { CGU_REG_MSCCDR
, 0, 1, 5, -1, -1, -1 },
153 .gate
= { CGU_REG_CLKGR
, 7 },
157 "uhc", CGU_CLK_DIV
| CGU_CLK_GATE
,
158 .parents
= { JZ4740_CLK_PLL_HALF
, -1, -1, -1 },
159 .div
= { CGU_REG_UHCCDR
, 0, 1, 4, -1, -1, -1 },
160 .gate
= { CGU_REG_CLKGR
, 14 },
164 "udc", CGU_CLK_MUX
| CGU_CLK_DIV
,
165 .parents
= { JZ4740_CLK_EXT
, JZ4740_CLK_PLL_HALF
, -1, -1 },
166 .mux
= { CGU_REG_CPCCR
, 29, 1 },
167 .div
= { CGU_REG_CPCCR
, 23, 1, 6, -1, -1, -1 },
168 .gate
= { CGU_REG_SCR
, 6 },
171 /* Gate-only clocks */
173 [JZ4740_CLK_UART0
] = {
174 "uart0", CGU_CLK_GATE
,
175 .parents
= { JZ4740_CLK_EXT
, -1, -1, -1 },
176 .gate
= { CGU_REG_CLKGR
, 0 },
179 [JZ4740_CLK_UART1
] = {
180 "uart1", CGU_CLK_GATE
,
181 .parents
= { JZ4740_CLK_EXT
, -1, -1, -1 },
182 .gate
= { CGU_REG_CLKGR
, 15 },
187 .parents
= { JZ4740_CLK_PCLK
, -1, -1, -1 },
188 .gate
= { CGU_REG_CLKGR
, 12 },
193 .parents
= { JZ4740_CLK_PCLK
, -1, -1, -1 },
194 .gate
= { CGU_REG_CLKGR
, 13 },
199 .parents
= { JZ4740_CLK_EXT
, -1, -1, -1 },
200 .gate
= { CGU_REG_CLKGR
, 8 },
205 .parents
= { JZ4740_CLK_EXT
, -1, -1, -1 },
206 .gate
= { CGU_REG_CLKGR
, 3 },
211 .parents
= { JZ4740_CLK_EXT
, -1, -1, -1 },
212 .gate
= { CGU_REG_CLKGR
, 5 },
216 static void __init
jz4740_cgu_init(struct device_node
*np
)
220 cgu
= ingenic_cgu_new(jz4740_cgu_clocks
,
221 ARRAY_SIZE(jz4740_cgu_clocks
), np
);
223 pr_err("%s: failed to initialise CGU\n", __func__
);
227 retval
= ingenic_cgu_register_clocks(cgu
);
229 pr_err("%s: failed to register CGU Clocks\n", __func__
);
231 CLK_OF_DECLARE(jz4740_cgu
, "ingenic,jz4740-cgu", jz4740_cgu_init
);
233 void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode
)
235 uint32_t lcr
= readl(cgu
->base
+ CGU_REG_LCR
);
238 case JZ4740_WAIT_MODE_IDLE
:
242 case JZ4740_WAIT_MODE_SLEEP
:
247 writel(lcr
, cgu
->base
+ CGU_REG_LCR
);
250 void jz4740_clock_udc_disable_auto_suspend(void)
252 uint32_t clkgr
= readl(cgu
->base
+ CGU_REG_CLKGR
);
255 writel(clkgr
, cgu
->base
+ CGU_REG_CLKGR
);
257 EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend
);
259 void jz4740_clock_udc_enable_auto_suspend(void)
261 uint32_t clkgr
= readl(cgu
->base
+ CGU_REG_CLKGR
);
264 writel(clkgr
, cgu
->base
+ CGU_REG_CLKGR
);
266 EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend
);
268 #define JZ_CLOCK_GATE_UART0 BIT(0)
269 #define JZ_CLOCK_GATE_TCU BIT(1)
270 #define JZ_CLOCK_GATE_DMAC BIT(12)
272 void jz4740_clock_suspend(void)
274 uint32_t clkgr
, cppcr
;
276 clkgr
= readl(cgu
->base
+ CGU_REG_CLKGR
);
277 clkgr
|= JZ_CLOCK_GATE_TCU
| JZ_CLOCK_GATE_DMAC
| JZ_CLOCK_GATE_UART0
;
278 writel(clkgr
, cgu
->base
+ CGU_REG_CLKGR
);
280 cppcr
= readl(cgu
->base
+ CGU_REG_CPPCR
);
281 cppcr
&= ~BIT(jz4740_cgu_clocks
[JZ4740_CLK_PLL
].pll
.enable_bit
);
282 writel(cppcr
, cgu
->base
+ CGU_REG_CPPCR
);
285 void jz4740_clock_resume(void)
287 uint32_t clkgr
, cppcr
, stable
;
289 cppcr
= readl(cgu
->base
+ CGU_REG_CPPCR
);
290 cppcr
|= BIT(jz4740_cgu_clocks
[JZ4740_CLK_PLL
].pll
.enable_bit
);
291 writel(cppcr
, cgu
->base
+ CGU_REG_CPPCR
);
293 stable
= BIT(jz4740_cgu_clocks
[JZ4740_CLK_PLL
].pll
.stable_bit
);
295 cppcr
= readl(cgu
->base
+ CGU_REG_CPPCR
);
296 } while (!(cppcr
& stable
));
298 clkgr
= readl(cgu
->base
+ CGU_REG_CLKGR
);
299 clkgr
&= ~JZ_CLOCK_GATE_TCU
;
300 clkgr
&= ~JZ_CLOCK_GATE_DMAC
;
301 clkgr
&= ~JZ_CLOCK_GATE_UART0
;
302 writel(clkgr
, cgu
->base
+ CGU_REG_CLKGR
);