Commit | Line | Data |
---|---|---|
ff1930c6 PB |
1 | /* |
2 | * Ingenic JZ4740 SoC CGU driver | |
3 | * | |
4 | * Copyright (c) 2015 Imagination Technologies | |
5 | * Author: Paul Burton <paul.burton@imgtec.com> | |
6 | * | |
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. | |
11 | * | |
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. | |
16 | */ | |
17 | ||
18 | #include <linux/clk-provider.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/of.h> | |
21 | #include <dt-bindings/clock/jz4740-cgu.h> | |
41dd641e | 22 | #include <asm/mach-jz4740/clock.h> |
ff1930c6 PB |
23 | #include "cgu.h" |
24 | ||
25 | /* CGU register offsets */ | |
26 | #define CGU_REG_CPCCR 0x00 | |
41dd641e | 27 | #define CGU_REG_LCR 0x04 |
ff1930c6 | 28 | #define CGU_REG_CPPCR 0x10 |
ed286ca5 | 29 | #define CGU_REG_CLKGR 0x20 |
ff1930c6 PB |
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 | |
36 | ||
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) | |
47 | ||
41dd641e PB |
48 | /* bits within the LCR register */ |
49 | #define LCR_SLEEP (1 << 0) | |
50 | ||
ed286ca5 PB |
51 | /* bits within the CLKGR register */ |
52 | #define CLKGR_UDC (1 << 11) | |
53 | ||
ff1930c6 PB |
54 | static struct ingenic_cgu *cgu; |
55 | ||
56 | static const s8 pll_od_encoding[4] = { | |
57 | 0x0, 0x1, -1, 0x3, | |
58 | }; | |
59 | ||
60 | static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { | |
61 | ||
62 | /* External clocks */ | |
63 | ||
64 | [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT }, | |
65 | [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT }, | |
66 | ||
67 | [JZ4740_CLK_PLL] = { | |
68 | "pll", CGU_CLK_PLL, | |
69 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | |
70 | .pll = { | |
71 | .reg = CGU_REG_CPPCR, | |
72 | .m_shift = 23, | |
73 | .m_bits = 9, | |
74 | .m_offset = 2, | |
75 | .n_shift = 18, | |
76 | .n_bits = 5, | |
77 | .n_offset = 2, | |
78 | .od_shift = 16, | |
79 | .od_bits = 2, | |
80 | .od_max = 4, | |
81 | .od_encoding = pll_od_encoding, | |
82 | .stable_bit = 10, | |
83 | .bypass_bit = 9, | |
84 | .enable_bit = 8, | |
85 | }, | |
86 | }, | |
87 | ||
88 | /* Muxes & dividers */ | |
89 | ||
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 }, | |
94 | }, | |
95 | ||
96 | [JZ4740_CLK_CCLK] = { | |
97 | "cclk", CGU_CLK_DIV, | |
98 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | |
99 | .div = { CGU_REG_CPCCR, 0, 4, 22, -1, -1 }, | |
100 | }, | |
101 | ||
102 | [JZ4740_CLK_HCLK] = { | |
103 | "hclk", CGU_CLK_DIV, | |
104 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | |
105 | .div = { CGU_REG_CPCCR, 4, 4, 22, -1, -1 }, | |
106 | }, | |
107 | ||
108 | [JZ4740_CLK_PCLK] = { | |
109 | "pclk", CGU_CLK_DIV, | |
110 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | |
111 | .div = { CGU_REG_CPCCR, 8, 4, 22, -1, -1 }, | |
112 | }, | |
113 | ||
114 | [JZ4740_CLK_MCLK] = { | |
115 | "mclk", CGU_CLK_DIV, | |
116 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, | |
117 | .div = { CGU_REG_CPCCR, 12, 4, 22, -1, -1 }, | |
118 | }, | |
119 | ||
120 | [JZ4740_CLK_LCD] = { | |
121 | "lcd", CGU_CLK_DIV | CGU_CLK_GATE, | |
122 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | |
123 | .div = { CGU_REG_CPCCR, 16, 5, 22, -1, -1 }, | |
124 | .gate = { CGU_REG_CLKGR, 10 }, | |
125 | }, | |
126 | ||
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, 11, -1, -1, -1 }, | |
131 | }, | |
132 | ||
133 | [JZ4740_CLK_I2S] = { | |
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, 8, -1, -1, -1 }, | |
138 | .gate = { CGU_REG_CLKGR, 6 }, | |
139 | }, | |
140 | ||
141 | [JZ4740_CLK_SPI] = { | |
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, 4, -1, -1, -1 }, | |
146 | .gate = { CGU_REG_CLKGR, 4 }, | |
147 | }, | |
148 | ||
149 | [JZ4740_CLK_MMC] = { | |
150 | "mmc", CGU_CLK_DIV | CGU_CLK_GATE, | |
151 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | |
152 | .div = { CGU_REG_MSCCDR, 0, 5, -1, -1, -1 }, | |
153 | .gate = { CGU_REG_CLKGR, 7 }, | |
154 | }, | |
155 | ||
156 | [JZ4740_CLK_UHC] = { | |
157 | "uhc", CGU_CLK_DIV | CGU_CLK_GATE, | |
158 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, | |
159 | .div = { CGU_REG_UHCCDR, 0, 4, -1, -1, -1 }, | |
160 | .gate = { CGU_REG_CLKGR, 14 }, | |
161 | }, | |
162 | ||
163 | [JZ4740_CLK_UDC] = { | |
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, 6, -1, -1, -1 }, | |
168 | .gate = { CGU_REG_SCR, 6 }, | |
169 | }, | |
170 | ||
171 | /* Gate-only clocks */ | |
172 | ||
173 | [JZ4740_CLK_UART0] = { | |
174 | "uart0", CGU_CLK_GATE, | |
175 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | |
176 | .gate = { CGU_REG_CLKGR, 0 }, | |
177 | }, | |
178 | ||
179 | [JZ4740_CLK_UART1] = { | |
180 | "uart1", CGU_CLK_GATE, | |
181 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | |
182 | .gate = { CGU_REG_CLKGR, 15 }, | |
183 | }, | |
184 | ||
185 | [JZ4740_CLK_DMA] = { | |
186 | "dma", CGU_CLK_GATE, | |
187 | .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, | |
188 | .gate = { CGU_REG_CLKGR, 12 }, | |
189 | }, | |
190 | ||
191 | [JZ4740_CLK_IPU] = { | |
192 | "ipu", CGU_CLK_GATE, | |
193 | .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, | |
194 | .gate = { CGU_REG_CLKGR, 13 }, | |
195 | }, | |
196 | ||
197 | [JZ4740_CLK_ADC] = { | |
198 | "adc", CGU_CLK_GATE, | |
199 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | |
200 | .gate = { CGU_REG_CLKGR, 8 }, | |
201 | }, | |
202 | ||
203 | [JZ4740_CLK_I2C] = { | |
204 | "i2c", CGU_CLK_GATE, | |
205 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | |
206 | .gate = { CGU_REG_CLKGR, 3 }, | |
207 | }, | |
208 | ||
209 | [JZ4740_CLK_AIC] = { | |
210 | "aic", CGU_CLK_GATE, | |
211 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, | |
212 | .gate = { CGU_REG_CLKGR, 5 }, | |
213 | }, | |
214 | }; | |
215 | ||
216 | static void __init jz4740_cgu_init(struct device_node *np) | |
217 | { | |
218 | int retval; | |
219 | ||
220 | cgu = ingenic_cgu_new(jz4740_cgu_clocks, | |
221 | ARRAY_SIZE(jz4740_cgu_clocks), np); | |
222 | if (!cgu) { | |
223 | pr_err("%s: failed to initialise CGU\n", __func__); | |
224 | return; | |
225 | } | |
226 | ||
227 | retval = ingenic_cgu_register_clocks(cgu); | |
228 | if (retval) | |
229 | pr_err("%s: failed to register CGU Clocks\n", __func__); | |
230 | } | |
231 | CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); | |
41dd641e PB |
232 | |
233 | void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) | |
234 | { | |
235 | uint32_t lcr = readl(cgu->base + CGU_REG_LCR); | |
236 | ||
237 | switch (mode) { | |
238 | case JZ4740_WAIT_MODE_IDLE: | |
239 | lcr &= ~LCR_SLEEP; | |
240 | break; | |
241 | ||
242 | case JZ4740_WAIT_MODE_SLEEP: | |
243 | lcr |= LCR_SLEEP; | |
244 | break; | |
245 | } | |
246 | ||
247 | writel(lcr, cgu->base + CGU_REG_LCR); | |
248 | } | |
ed286ca5 PB |
249 | |
250 | void jz4740_clock_udc_disable_auto_suspend(void) | |
251 | { | |
252 | uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); | |
253 | ||
254 | clkgr &= ~CLKGR_UDC; | |
255 | writel(clkgr, cgu->base + CGU_REG_CLKGR); | |
256 | } | |
257 | EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); | |
258 | ||
259 | void jz4740_clock_udc_enable_auto_suspend(void) | |
260 | { | |
261 | uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); | |
262 | ||
263 | clkgr |= CLKGR_UDC; | |
264 | writel(clkgr, cgu->base + CGU_REG_CLKGR); | |
265 | } | |
266 | EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); | |
50d893ff PB |
267 | |
268 | #define JZ_CLOCK_GATE_UART0 BIT(0) | |
269 | #define JZ_CLOCK_GATE_TCU BIT(1) | |
270 | #define JZ_CLOCK_GATE_DMAC BIT(12) | |
271 | ||
272 | void jz4740_clock_suspend(void) | |
273 | { | |
274 | uint32_t clkgr, cppcr; | |
275 | ||
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); | |
279 | ||
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); | |
283 | } | |
284 | ||
285 | void jz4740_clock_resume(void) | |
286 | { | |
287 | uint32_t clkgr, cppcr, stable; | |
288 | ||
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); | |
292 | ||
293 | stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); | |
294 | do { | |
295 | cppcr = readl(cgu->base + CGU_REG_CPPCR); | |
296 | } while (!(cppcr & stable)); | |
297 | ||
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); | |
303 | } |