xref: /openbmc/linux/drivers/clk/ingenic/jz4740-cgu.c (revision 2ee93e3c)
1 /*
2  * Ingenic JZ4740 SoC CGU driver
3  *
4  * Copyright (c) 2015 Imagination Technologies
5  * Author: Paul Burton <paul.burton@mips.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/io.h>
21 #include <linux/of.h>
22 #include <dt-bindings/clock/jz4740-cgu.h>
23 #include <asm/mach-jz4740/clock.h>
24 #include "cgu.h"
25 #include "pm.h"
26 
27 /* CGU register offsets */
28 #define CGU_REG_CPCCR		0x00
29 #define CGU_REG_LCR		0x04
30 #define CGU_REG_CPPCR		0x10
31 #define CGU_REG_CLKGR		0x20
32 #define CGU_REG_SCR		0x24
33 #define CGU_REG_I2SCDR		0x60
34 #define CGU_REG_LPCDR		0x64
35 #define CGU_REG_MSCCDR		0x68
36 #define CGU_REG_UHCCDR		0x6c
37 #define CGU_REG_SSICDR		0x74
38 
39 /* bits within a PLL control register */
40 #define PLLCTL_M_SHIFT		23
41 #define PLLCTL_M_MASK		(0x1ff << PLLCTL_M_SHIFT)
42 #define PLLCTL_N_SHIFT		18
43 #define PLLCTL_N_MASK		(0x1f << PLLCTL_N_SHIFT)
44 #define PLLCTL_OD_SHIFT		16
45 #define PLLCTL_OD_MASK		(0x3 << PLLCTL_OD_SHIFT)
46 #define PLLCTL_STABLE		(1 << 10)
47 #define PLLCTL_BYPASS		(1 << 9)
48 #define PLLCTL_ENABLE		(1 << 8)
49 
50 /* bits within the LCR register */
51 #define LCR_SLEEP		(1 << 0)
52 
53 /* bits within the CLKGR register */
54 #define CLKGR_UDC		(1 << 11)
55 
56 static struct ingenic_cgu *cgu;
57 
58 static const s8 pll_od_encoding[4] = {
59 	0x0, 0x1, -1, 0x3,
60 };
61 
62 static const u8 jz4740_cgu_cpccr_div_table[] = {
63 	1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
64 };
65 
66 static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
67 
68 	/* External clocks */
69 
70 	[JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT },
71 	[JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT },
72 
73 	[JZ4740_CLK_PLL] = {
74 		"pll", CGU_CLK_PLL,
75 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
76 		.pll = {
77 			.reg = CGU_REG_CPPCR,
78 			.m_shift = 23,
79 			.m_bits = 9,
80 			.m_offset = 2,
81 			.n_shift = 18,
82 			.n_bits = 5,
83 			.n_offset = 2,
84 			.od_shift = 16,
85 			.od_bits = 2,
86 			.od_max = 4,
87 			.od_encoding = pll_od_encoding,
88 			.stable_bit = 10,
89 			.bypass_bit = 9,
90 			.enable_bit = 8,
91 		},
92 	},
93 
94 	/* Muxes & dividers */
95 
96 	[JZ4740_CLK_PLL_HALF] = {
97 		"pll half", CGU_CLK_DIV,
98 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
99 		.div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 },
100 	},
101 
102 	[JZ4740_CLK_CCLK] = {
103 		"cclk", CGU_CLK_DIV,
104 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
105 		.div = {
106 			CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1,
107 			jz4740_cgu_cpccr_div_table,
108 		},
109 	},
110 
111 	[JZ4740_CLK_HCLK] = {
112 		"hclk", CGU_CLK_DIV,
113 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
114 		.div = {
115 			CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1,
116 			jz4740_cgu_cpccr_div_table,
117 		},
118 	},
119 
120 	[JZ4740_CLK_PCLK] = {
121 		"pclk", CGU_CLK_DIV,
122 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
123 		.div = {
124 			CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1,
125 			jz4740_cgu_cpccr_div_table,
126 		},
127 	},
128 
129 	[JZ4740_CLK_MCLK] = {
130 		"mclk", CGU_CLK_DIV,
131 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
132 		.div = {
133 			CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1,
134 			jz4740_cgu_cpccr_div_table,
135 		},
136 	},
137 
138 	[JZ4740_CLK_LCD] = {
139 		"lcd", CGU_CLK_DIV | CGU_CLK_GATE,
140 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
141 		.div = {
142 			CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1,
143 			jz4740_cgu_cpccr_div_table,
144 		},
145 		.gate = { CGU_REG_CLKGR, 10 },
146 	},
147 
148 	[JZ4740_CLK_LCD_PCLK] = {
149 		"lcd_pclk", CGU_CLK_DIV,
150 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
151 		.div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
152 	},
153 
154 	[JZ4740_CLK_I2S] = {
155 		"i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
156 		.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
157 		.mux = { CGU_REG_CPCCR, 31, 1 },
158 		.div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
159 		.gate = { CGU_REG_CLKGR, 6 },
160 	},
161 
162 	[JZ4740_CLK_SPI] = {
163 		"spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
164 		.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 },
165 		.mux = { CGU_REG_SSICDR, 31, 1 },
166 		.div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
167 		.gate = { CGU_REG_CLKGR, 4 },
168 	},
169 
170 	[JZ4740_CLK_MMC] = {
171 		"mmc", CGU_CLK_DIV | CGU_CLK_GATE,
172 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
173 		.div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
174 		.gate = { CGU_REG_CLKGR, 7 },
175 	},
176 
177 	[JZ4740_CLK_UHC] = {
178 		"uhc", CGU_CLK_DIV | CGU_CLK_GATE,
179 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
180 		.div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 },
181 		.gate = { CGU_REG_CLKGR, 14 },
182 	},
183 
184 	[JZ4740_CLK_UDC] = {
185 		"udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
186 		.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
187 		.mux = { CGU_REG_CPCCR, 29, 1 },
188 		.div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
189 		.gate = { CGU_REG_SCR, 6, true },
190 	},
191 
192 	/* Gate-only clocks */
193 
194 	[JZ4740_CLK_UART0] = {
195 		"uart0", CGU_CLK_GATE,
196 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
197 		.gate = { CGU_REG_CLKGR, 0 },
198 	},
199 
200 	[JZ4740_CLK_UART1] = {
201 		"uart1", CGU_CLK_GATE,
202 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
203 		.gate = { CGU_REG_CLKGR, 15 },
204 	},
205 
206 	[JZ4740_CLK_DMA] = {
207 		"dma", CGU_CLK_GATE,
208 		.parents = { JZ4740_CLK_PCLK, -1, -1, -1 },
209 		.gate = { CGU_REG_CLKGR, 12 },
210 	},
211 
212 	[JZ4740_CLK_IPU] = {
213 		"ipu", CGU_CLK_GATE,
214 		.parents = { JZ4740_CLK_PCLK, -1, -1, -1 },
215 		.gate = { CGU_REG_CLKGR, 13 },
216 	},
217 
218 	[JZ4740_CLK_ADC] = {
219 		"adc", CGU_CLK_GATE,
220 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
221 		.gate = { CGU_REG_CLKGR, 8 },
222 	},
223 
224 	[JZ4740_CLK_I2C] = {
225 		"i2c", CGU_CLK_GATE,
226 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
227 		.gate = { CGU_REG_CLKGR, 3 },
228 	},
229 
230 	[JZ4740_CLK_AIC] = {
231 		"aic", CGU_CLK_GATE,
232 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
233 		.gate = { CGU_REG_CLKGR, 5 },
234 	},
235 };
236 
237 static void __init jz4740_cgu_init(struct device_node *np)
238 {
239 	int retval;
240 
241 	cgu = ingenic_cgu_new(jz4740_cgu_clocks,
242 			      ARRAY_SIZE(jz4740_cgu_clocks), np);
243 	if (!cgu) {
244 		pr_err("%s: failed to initialise CGU\n", __func__);
245 		return;
246 	}
247 
248 	retval = ingenic_cgu_register_clocks(cgu);
249 	if (retval)
250 		pr_err("%s: failed to register CGU Clocks\n", __func__);
251 
252 	ingenic_cgu_register_syscore_ops(cgu);
253 }
254 CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);
255 
256 void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode)
257 {
258 	uint32_t lcr = readl(cgu->base + CGU_REG_LCR);
259 
260 	switch (mode) {
261 	case JZ4740_WAIT_MODE_IDLE:
262 		lcr &= ~LCR_SLEEP;
263 		break;
264 
265 	case JZ4740_WAIT_MODE_SLEEP:
266 		lcr |= LCR_SLEEP;
267 		break;
268 	}
269 
270 	writel(lcr, cgu->base + CGU_REG_LCR);
271 }
272 
273 void jz4740_clock_udc_disable_auto_suspend(void)
274 {
275 	uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR);
276 
277 	clkgr &= ~CLKGR_UDC;
278 	writel(clkgr, cgu->base + CGU_REG_CLKGR);
279 }
280 EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend);
281 
282 void jz4740_clock_udc_enable_auto_suspend(void)
283 {
284 	uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR);
285 
286 	clkgr |= CLKGR_UDC;
287 	writel(clkgr, cgu->base + CGU_REG_CLKGR);
288 }
289 EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend);
290 
291 #define JZ_CLOCK_GATE_UART0	BIT(0)
292 #define JZ_CLOCK_GATE_TCU	BIT(1)
293 #define JZ_CLOCK_GATE_DMAC	BIT(12)
294 
295 void jz4740_clock_suspend(void)
296 {
297 	uint32_t clkgr, cppcr;
298 
299 	clkgr = readl(cgu->base + CGU_REG_CLKGR);
300 	clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0;
301 	writel(clkgr, cgu->base + CGU_REG_CLKGR);
302 
303 	cppcr = readl(cgu->base + CGU_REG_CPPCR);
304 	cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit);
305 	writel(cppcr, cgu->base + CGU_REG_CPPCR);
306 }
307 
308 void jz4740_clock_resume(void)
309 {
310 	uint32_t clkgr, cppcr, stable;
311 
312 	cppcr = readl(cgu->base + CGU_REG_CPPCR);
313 	cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit);
314 	writel(cppcr, cgu->base + CGU_REG_CPPCR);
315 
316 	stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit);
317 	do {
318 		cppcr = readl(cgu->base + CGU_REG_CPPCR);
319 	} while (!(cppcr & stable));
320 
321 	clkgr = readl(cgu->base + CGU_REG_CLKGR);
322 	clkgr &= ~JZ_CLOCK_GATE_TCU;
323 	clkgr &= ~JZ_CLOCK_GATE_DMAC;
324 	clkgr &= ~JZ_CLOCK_GATE_UART0;
325 	writel(clkgr, cgu->base + CGU_REG_CLKGR);
326 }
327