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