xref: /openbmc/linux/drivers/clk/ingenic/jz4740-cgu.c (revision 9a838844)
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 "cgu.h"
24 #include "pm.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 u8 jz4740_cgu_cpccr_div_table[] = {
62 	1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
63 };
64 
65 static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
66 
67 	/* External clocks */
68 
69 	[JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT },
70 	[JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT },
71 
72 	[JZ4740_CLK_PLL] = {
73 		"pll", CGU_CLK_PLL,
74 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
75 		.pll = {
76 			.reg = CGU_REG_CPPCR,
77 			.m_shift = 23,
78 			.m_bits = 9,
79 			.m_offset = 2,
80 			.n_shift = 18,
81 			.n_bits = 5,
82 			.n_offset = 2,
83 			.od_shift = 16,
84 			.od_bits = 2,
85 			.od_max = 4,
86 			.od_encoding = pll_od_encoding,
87 			.stable_bit = 10,
88 			.bypass_bit = 9,
89 			.enable_bit = 8,
90 		},
91 	},
92 
93 	/* Muxes & dividers */
94 
95 	[JZ4740_CLK_PLL_HALF] = {
96 		"pll half", CGU_CLK_DIV,
97 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
98 		.div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 },
99 	},
100 
101 	[JZ4740_CLK_CCLK] = {
102 		"cclk", CGU_CLK_DIV,
103 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
104 		.div = {
105 			CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1,
106 			jz4740_cgu_cpccr_div_table,
107 		},
108 	},
109 
110 	[JZ4740_CLK_HCLK] = {
111 		"hclk", CGU_CLK_DIV,
112 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
113 		.div = {
114 			CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1,
115 			jz4740_cgu_cpccr_div_table,
116 		},
117 	},
118 
119 	[JZ4740_CLK_PCLK] = {
120 		"pclk", CGU_CLK_DIV,
121 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
122 		.div = {
123 			CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1,
124 			jz4740_cgu_cpccr_div_table,
125 		},
126 	},
127 
128 	[JZ4740_CLK_MCLK] = {
129 		"mclk", CGU_CLK_DIV,
130 		.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
131 		.div = {
132 			CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1,
133 			jz4740_cgu_cpccr_div_table,
134 		},
135 	},
136 
137 	[JZ4740_CLK_LCD] = {
138 		"lcd", CGU_CLK_DIV | CGU_CLK_GATE,
139 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
140 		.div = {
141 			CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1,
142 			jz4740_cgu_cpccr_div_table,
143 		},
144 		.gate = { CGU_REG_CLKGR, 10 },
145 	},
146 
147 	[JZ4740_CLK_LCD_PCLK] = {
148 		"lcd_pclk", CGU_CLK_DIV,
149 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
150 		.div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
151 	},
152 
153 	[JZ4740_CLK_I2S] = {
154 		"i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
155 		.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
156 		.mux = { CGU_REG_CPCCR, 31, 1 },
157 		.div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
158 		.gate = { CGU_REG_CLKGR, 6 },
159 	},
160 
161 	[JZ4740_CLK_SPI] = {
162 		"spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
163 		.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 },
164 		.mux = { CGU_REG_SSICDR, 31, 1 },
165 		.div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
166 		.gate = { CGU_REG_CLKGR, 4 },
167 	},
168 
169 	[JZ4740_CLK_MMC] = {
170 		"mmc", CGU_CLK_DIV | CGU_CLK_GATE,
171 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
172 		.div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
173 		.gate = { CGU_REG_CLKGR, 7 },
174 	},
175 
176 	[JZ4740_CLK_UHC] = {
177 		"uhc", CGU_CLK_DIV | CGU_CLK_GATE,
178 		.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
179 		.div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 },
180 		.gate = { CGU_REG_CLKGR, 14 },
181 	},
182 
183 	[JZ4740_CLK_UDC] = {
184 		"udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
185 		.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
186 		.mux = { CGU_REG_CPCCR, 29, 1 },
187 		.div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
188 		.gate = { CGU_REG_SCR, 6, true },
189 	},
190 
191 	/* Gate-only clocks */
192 
193 	[JZ4740_CLK_UART0] = {
194 		"uart0", CGU_CLK_GATE,
195 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
196 		.gate = { CGU_REG_CLKGR, 0 },
197 	},
198 
199 	[JZ4740_CLK_UART1] = {
200 		"uart1", CGU_CLK_GATE,
201 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
202 		.gate = { CGU_REG_CLKGR, 15 },
203 	},
204 
205 	[JZ4740_CLK_DMA] = {
206 		"dma", CGU_CLK_GATE,
207 		.parents = { JZ4740_CLK_PCLK, -1, -1, -1 },
208 		.gate = { CGU_REG_CLKGR, 12 },
209 	},
210 
211 	[JZ4740_CLK_IPU] = {
212 		"ipu", CGU_CLK_GATE,
213 		.parents = { JZ4740_CLK_PCLK, -1, -1, -1 },
214 		.gate = { CGU_REG_CLKGR, 13 },
215 	},
216 
217 	[JZ4740_CLK_ADC] = {
218 		"adc", CGU_CLK_GATE,
219 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
220 		.gate = { CGU_REG_CLKGR, 8 },
221 	},
222 
223 	[JZ4740_CLK_I2C] = {
224 		"i2c", CGU_CLK_GATE,
225 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
226 		.gate = { CGU_REG_CLKGR, 3 },
227 	},
228 
229 	[JZ4740_CLK_AIC] = {
230 		"aic", CGU_CLK_GATE,
231 		.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
232 		.gate = { CGU_REG_CLKGR, 5 },
233 	},
234 };
235 
236 static void __init jz4740_cgu_init(struct device_node *np)
237 {
238 	int retval;
239 
240 	cgu = ingenic_cgu_new(jz4740_cgu_clocks,
241 			      ARRAY_SIZE(jz4740_cgu_clocks), np);
242 	if (!cgu) {
243 		pr_err("%s: failed to initialise CGU\n", __func__);
244 		return;
245 	}
246 
247 	retval = ingenic_cgu_register_clocks(cgu);
248 	if (retval)
249 		pr_err("%s: failed to register CGU Clocks\n", __func__);
250 
251 	ingenic_cgu_register_syscore_ops(cgu);
252 }
253 CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);
254