xref: /openbmc/linux/drivers/clk/meson/g12a-aoclk.c (revision dfd4f649)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Amlogic Meson-AXG Clock Controller Driver
4  *
5  * Copyright (c) 2016 Baylibre SAS.
6  * Author: Michael Turquette <mturquette@baylibre.com>
7  *
8  * Copyright (c) 2019 Baylibre SAS.
9  * Author: Neil Armstrong <narmstrong@baylibre.com>
10  */
11 #include <linux/clk-provider.h>
12 #include <linux/platform_device.h>
13 #include <linux/reset-controller.h>
14 #include <linux/mfd/syscon.h>
15 #include "meson-aoclk.h"
16 #include "g12a-aoclk.h"
17 
18 #include "clk-regmap.h"
19 #include "clk-dualdiv.h"
20 
21 #define IN_PREFIX "ao-in-"
22 
23 /*
24  * AO Configuration Clock registers offsets
25  * Register offsets from the data sheet must be multiplied by 4.
26  */
27 #define AO_RTI_STATUS_REG3	0x0C
28 #define AO_RTI_PWR_CNTL_REG0	0x10
29 #define AO_RTI_GEN_CNTL_REG0	0x40
30 #define AO_CLK_GATE0		0x4c
31 #define AO_CLK_GATE0_SP		0x50
32 #define AO_OSCIN_CNTL		0x58
33 #define AO_CEC_CLK_CNTL_REG0	0x74
34 #define AO_CEC_CLK_CNTL_REG1	0x78
35 #define AO_SAR_CLK		0x90
36 #define AO_RTC_ALT_CLK_CNTL0	0x94
37 #define AO_RTC_ALT_CLK_CNTL1	0x98
38 
39 /*
40  * Like every other peripheral clock gate in Amlogic Clock drivers,
41  * we are using CLK_IGNORE_UNUSED here, so we keep the state of the
42  * bootloader. The goal is to remove this flag at some point.
43  * Actually removing it will require some extensive test to be done safely.
44  */
45 #define AXG_AO_GATE(_name, _reg, _bit)					\
46 static struct clk_regmap g12a_aoclk_##_name = {				\
47 	.data = &(struct clk_regmap_gate_data) {			\
48 		.offset = (_reg),					\
49 		.bit_idx = (_bit),					\
50 	},								\
51 	.hw.init = &(struct clk_init_data) {				\
52 		.name =  "g12a_ao_" #_name,				\
53 		.ops = &clk_regmap_gate_ops,				\
54 		.parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \
55 		.num_parents = 1,					\
56 		.flags = CLK_IGNORE_UNUSED,				\
57 	},								\
58 }
59 
60 AXG_AO_GATE(ahb, AO_CLK_GATE0, 0);
61 AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1);
62 AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2);
63 AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3);
64 AXG_AO_GATE(uart, AO_CLK_GATE0, 4);
65 AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5);
66 AXG_AO_GATE(uart2, AO_CLK_GATE0, 6);
67 AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7);
68 AXG_AO_GATE(saradc, AO_CLK_GATE0, 8);
69 AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0);
70 AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1);
71 AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2);
72 AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3);
73 AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4);
74 AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5);
75 
76 static struct clk_regmap g12a_aoclk_cts_oscin = {
77 	.data = &(struct clk_regmap_gate_data){
78 		.offset = AO_RTI_PWR_CNTL_REG0,
79 		.bit_idx = 14,
80 	},
81 	.hw.init = &(struct clk_init_data){
82 		.name = "cts_oscin",
83 		.ops = &clk_regmap_gate_ro_ops,
84 		.parent_names = (const char *[]){ IN_PREFIX "xtal" },
85 		.num_parents = 1,
86 	},
87 };
88 
89 static const struct meson_clk_dualdiv_param g12a_32k_div_table[] = {
90 	{
91 		.dual	= 1,
92 		.n1	= 733,
93 		.m1	= 8,
94 		.n2	= 732,
95 		.m2	= 11,
96 	}, {}
97 };
98 
99 /* 32k_by_oscin clock */
100 
101 static struct clk_regmap g12a_aoclk_32k_by_oscin_pre = {
102 	.data = &(struct clk_regmap_gate_data){
103 		.offset = AO_RTC_ALT_CLK_CNTL0,
104 		.bit_idx = 31,
105 	},
106 	.hw.init = &(struct clk_init_data){
107 		.name = "g12a_ao_32k_by_oscin_pre",
108 		.ops = &clk_regmap_gate_ops,
109 		.parent_names = (const char *[]){ "cts_oscin" },
110 		.num_parents = 1,
111 	},
112 };
113 
114 static struct clk_regmap g12a_aoclk_32k_by_oscin_div = {
115 	.data = &(struct meson_clk_dualdiv_data){
116 		.n1 = {
117 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
118 			.shift   = 0,
119 			.width   = 12,
120 		},
121 		.n2 = {
122 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
123 			.shift   = 12,
124 			.width   = 12,
125 		},
126 		.m1 = {
127 			.reg_off = AO_RTC_ALT_CLK_CNTL1,
128 			.shift   = 0,
129 			.width   = 12,
130 		},
131 		.m2 = {
132 			.reg_off = AO_RTC_ALT_CLK_CNTL1,
133 			.shift   = 12,
134 			.width   = 12,
135 		},
136 		.dual = {
137 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
138 			.shift   = 28,
139 			.width   = 1,
140 		},
141 		.table = g12a_32k_div_table,
142 	},
143 	.hw.init = &(struct clk_init_data){
144 		.name = "g12a_ao_32k_by_oscin_div",
145 		.ops = &meson_clk_dualdiv_ops,
146 		.parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_pre" },
147 		.num_parents = 1,
148 	},
149 };
150 
151 static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = {
152 	.data = &(struct clk_regmap_mux_data) {
153 		.offset = AO_RTC_ALT_CLK_CNTL1,
154 		.mask = 0x1,
155 		.shift = 24,
156 		.flags = CLK_MUX_ROUND_CLOSEST,
157 	},
158 	.hw.init = &(struct clk_init_data){
159 		.name = "g12a_ao_32k_by_oscin_sel",
160 		.ops = &clk_regmap_mux_ops,
161 		.parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_div",
162 						  "g12a_ao_32k_by_oscin_pre" },
163 		.num_parents = 2,
164 		.flags = CLK_SET_RATE_PARENT,
165 	},
166 };
167 
168 static struct clk_regmap g12a_aoclk_32k_by_oscin = {
169 	.data = &(struct clk_regmap_gate_data){
170 		.offset = AO_RTC_ALT_CLK_CNTL0,
171 		.bit_idx = 30,
172 	},
173 	.hw.init = &(struct clk_init_data){
174 		.name = "g12a_ao_32k_by_oscin",
175 		.ops = &clk_regmap_gate_ops,
176 		.parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_sel" },
177 		.num_parents = 1,
178 		.flags = CLK_SET_RATE_PARENT,
179 	},
180 };
181 
182 /* cec clock */
183 
184 static struct clk_regmap g12a_aoclk_cec_pre = {
185 	.data = &(struct clk_regmap_gate_data){
186 		.offset = AO_CEC_CLK_CNTL_REG0,
187 		.bit_idx = 31,
188 	},
189 	.hw.init = &(struct clk_init_data){
190 		.name = "g12a_ao_cec_pre",
191 		.ops = &clk_regmap_gate_ops,
192 		.parent_names = (const char *[]){ "cts_oscin" },
193 		.num_parents = 1,
194 	},
195 };
196 
197 static struct clk_regmap g12a_aoclk_cec_div = {
198 	.data = &(struct meson_clk_dualdiv_data){
199 		.n1 = {
200 			.reg_off = AO_CEC_CLK_CNTL_REG0,
201 			.shift   = 0,
202 			.width   = 12,
203 		},
204 		.n2 = {
205 			.reg_off = AO_CEC_CLK_CNTL_REG0,
206 			.shift   = 12,
207 			.width   = 12,
208 		},
209 		.m1 = {
210 			.reg_off = AO_CEC_CLK_CNTL_REG1,
211 			.shift   = 0,
212 			.width   = 12,
213 		},
214 		.m2 = {
215 			.reg_off = AO_CEC_CLK_CNTL_REG1,
216 			.shift   = 12,
217 			.width   = 12,
218 		},
219 		.dual = {
220 			.reg_off = AO_CEC_CLK_CNTL_REG0,
221 			.shift   = 28,
222 			.width   = 1,
223 		},
224 		.table = g12a_32k_div_table,
225 	},
226 	.hw.init = &(struct clk_init_data){
227 		.name = "g12a_ao_cec_div",
228 		.ops = &meson_clk_dualdiv_ops,
229 		.parent_names = (const char *[]){ "g12a_ao_cec_pre" },
230 		.num_parents = 1,
231 	},
232 };
233 
234 static struct clk_regmap g12a_aoclk_cec_sel = {
235 	.data = &(struct clk_regmap_mux_data) {
236 		.offset = AO_CEC_CLK_CNTL_REG1,
237 		.mask = 0x1,
238 		.shift = 24,
239 		.flags = CLK_MUX_ROUND_CLOSEST,
240 	},
241 	.hw.init = &(struct clk_init_data){
242 		.name = "g12a_ao_cec_sel",
243 		.ops = &clk_regmap_mux_ops,
244 		.parent_names = (const char *[]){ "g12a_ao_cec_div",
245 						  "g12a_ao_cec_pre" },
246 		.num_parents = 2,
247 		.flags = CLK_SET_RATE_PARENT,
248 	},
249 };
250 
251 static struct clk_regmap g12a_aoclk_cec = {
252 	.data = &(struct clk_regmap_gate_data){
253 		.offset = AO_CEC_CLK_CNTL_REG0,
254 		.bit_idx = 30,
255 	},
256 	.hw.init = &(struct clk_init_data){
257 		.name = "g12a_ao_cec",
258 		.ops = &clk_regmap_gate_ops,
259 		.parent_names = (const char *[]){ "g12a_ao_cec_sel" },
260 		.num_parents = 1,
261 		.flags = CLK_SET_RATE_PARENT,
262 	},
263 };
264 
265 static struct clk_regmap g12a_aoclk_cts_rtc_oscin = {
266 	.data = &(struct clk_regmap_mux_data) {
267 		.offset = AO_RTI_PWR_CNTL_REG0,
268 		.mask = 0x1,
269 		.shift = 10,
270 		.flags = CLK_MUX_ROUND_CLOSEST,
271 	},
272 	.hw.init = &(struct clk_init_data){
273 		.name = "g12a_ao_cts_rtc_oscin",
274 		.ops = &clk_regmap_mux_ops,
275 		.parent_names = (const char *[]){ "g12a_ao_32k_by_oscin",
276 						  IN_PREFIX "ext_32k-0" },
277 		.num_parents = 2,
278 		.flags = CLK_SET_RATE_PARENT,
279 	},
280 };
281 
282 static struct clk_regmap g12a_aoclk_clk81 = {
283 	.data = &(struct clk_regmap_mux_data) {
284 		.offset = AO_RTI_PWR_CNTL_REG0,
285 		.mask = 0x1,
286 		.shift = 8,
287 		.flags = CLK_MUX_ROUND_CLOSEST,
288 	},
289 	.hw.init = &(struct clk_init_data){
290 		.name = "g12a_ao_clk81",
291 		.ops = &clk_regmap_mux_ro_ops,
292 		.parent_names = (const char *[]){ IN_PREFIX "mpeg-clk",
293 						  "g12a_ao_cts_rtc_oscin"},
294 		.num_parents = 2,
295 		.flags = CLK_SET_RATE_PARENT,
296 	},
297 };
298 
299 static struct clk_regmap g12a_aoclk_saradc_mux = {
300 	.data = &(struct clk_regmap_mux_data) {
301 		.offset = AO_SAR_CLK,
302 		.mask = 0x3,
303 		.shift = 9,
304 	},
305 	.hw.init = &(struct clk_init_data){
306 		.name = "g12a_ao_saradc_mux",
307 		.ops = &clk_regmap_mux_ops,
308 		.parent_names = (const char *[]){ IN_PREFIX "xtal",
309 						  "g12a_ao_clk81" },
310 		.num_parents = 2,
311 	},
312 };
313 
314 static struct clk_regmap g12a_aoclk_saradc_div = {
315 	.data = &(struct clk_regmap_div_data) {
316 		.offset = AO_SAR_CLK,
317 		.shift = 0,
318 		.width = 8,
319 	},
320 	.hw.init = &(struct clk_init_data){
321 		.name = "g12a_ao_saradc_div",
322 		.ops = &clk_regmap_divider_ops,
323 		.parent_names = (const char *[]){ "g12a_ao_saradc_mux" },
324 		.num_parents = 1,
325 		.flags = CLK_SET_RATE_PARENT,
326 	},
327 };
328 
329 static struct clk_regmap g12a_aoclk_saradc_gate = {
330 	.data = &(struct clk_regmap_gate_data) {
331 		.offset = AO_SAR_CLK,
332 		.bit_idx = 8,
333 	},
334 	.hw.init = &(struct clk_init_data){
335 		.name = "g12a_ao_saradc_gate",
336 		.ops = &clk_regmap_gate_ops,
337 		.parent_names = (const char *[]){ "g12a_ao_saradc_div" },
338 		.num_parents = 1,
339 		.flags = CLK_SET_RATE_PARENT,
340 	},
341 };
342 
343 static const unsigned int g12a_aoclk_reset[] = {
344 	[RESET_AO_IR_IN]	= 16,
345 	[RESET_AO_UART]		= 17,
346 	[RESET_AO_I2C_M]	= 18,
347 	[RESET_AO_I2C_S]	= 19,
348 	[RESET_AO_SAR_ADC]	= 20,
349 	[RESET_AO_UART2]	= 22,
350 	[RESET_AO_IR_OUT]	= 23,
351 };
352 
353 static struct clk_regmap *g12a_aoclk_regmap[] = {
354 	&g12a_aoclk_ahb,
355 	&g12a_aoclk_ir_in,
356 	&g12a_aoclk_i2c_m0,
357 	&g12a_aoclk_i2c_s0,
358 	&g12a_aoclk_uart,
359 	&g12a_aoclk_prod_i2c,
360 	&g12a_aoclk_uart2,
361 	&g12a_aoclk_ir_out,
362 	&g12a_aoclk_saradc,
363 	&g12a_aoclk_mailbox,
364 	&g12a_aoclk_m3,
365 	&g12a_aoclk_ahb_sram,
366 	&g12a_aoclk_rti,
367 	&g12a_aoclk_m4_fclk,
368 	&g12a_aoclk_m4_hclk,
369 	&g12a_aoclk_cts_oscin,
370 	&g12a_aoclk_32k_by_oscin_pre,
371 	&g12a_aoclk_32k_by_oscin_div,
372 	&g12a_aoclk_32k_by_oscin_sel,
373 	&g12a_aoclk_32k_by_oscin,
374 	&g12a_aoclk_cec_pre,
375 	&g12a_aoclk_cec_div,
376 	&g12a_aoclk_cec_sel,
377 	&g12a_aoclk_cec,
378 	&g12a_aoclk_cts_rtc_oscin,
379 	&g12a_aoclk_clk81,
380 	&g12a_aoclk_saradc_mux,
381 	&g12a_aoclk_saradc_div,
382 	&g12a_aoclk_saradc_gate,
383 };
384 
385 static const struct clk_hw_onecell_data g12a_aoclk_onecell_data = {
386 	.hws = {
387 		[CLKID_AO_AHB]		= &g12a_aoclk_ahb.hw,
388 		[CLKID_AO_IR_IN]	= &g12a_aoclk_ir_in.hw,
389 		[CLKID_AO_I2C_M0]	= &g12a_aoclk_i2c_m0.hw,
390 		[CLKID_AO_I2C_S0]	= &g12a_aoclk_i2c_s0.hw,
391 		[CLKID_AO_UART]		= &g12a_aoclk_uart.hw,
392 		[CLKID_AO_PROD_I2C]	= &g12a_aoclk_prod_i2c.hw,
393 		[CLKID_AO_UART2]	= &g12a_aoclk_uart2.hw,
394 		[CLKID_AO_IR_OUT]	= &g12a_aoclk_ir_out.hw,
395 		[CLKID_AO_SAR_ADC]	= &g12a_aoclk_saradc.hw,
396 		[CLKID_AO_MAILBOX]	= &g12a_aoclk_mailbox.hw,
397 		[CLKID_AO_M3]		= &g12a_aoclk_m3.hw,
398 		[CLKID_AO_AHB_SRAM]	= &g12a_aoclk_ahb_sram.hw,
399 		[CLKID_AO_RTI]		= &g12a_aoclk_rti.hw,
400 		[CLKID_AO_M4_FCLK]	= &g12a_aoclk_m4_fclk.hw,
401 		[CLKID_AO_M4_HCLK]	= &g12a_aoclk_m4_hclk.hw,
402 		[CLKID_AO_CLK81]	= &g12a_aoclk_clk81.hw,
403 		[CLKID_AO_SAR_ADC_SEL]	= &g12a_aoclk_saradc_mux.hw,
404 		[CLKID_AO_SAR_ADC_DIV]	= &g12a_aoclk_saradc_div.hw,
405 		[CLKID_AO_SAR_ADC_CLK]	= &g12a_aoclk_saradc_gate.hw,
406 		[CLKID_AO_CTS_OSCIN]	= &g12a_aoclk_cts_oscin.hw,
407 		[CLKID_AO_32K_PRE]	= &g12a_aoclk_32k_by_oscin_pre.hw,
408 		[CLKID_AO_32K_DIV]	= &g12a_aoclk_32k_by_oscin_div.hw,
409 		[CLKID_AO_32K_SEL]	= &g12a_aoclk_32k_by_oscin_sel.hw,
410 		[CLKID_AO_32K]		= &g12a_aoclk_32k_by_oscin.hw,
411 		[CLKID_AO_CEC_PRE]	= &g12a_aoclk_cec_pre.hw,
412 		[CLKID_AO_CEC_DIV]	= &g12a_aoclk_cec_div.hw,
413 		[CLKID_AO_CEC_SEL]	= &g12a_aoclk_cec_sel.hw,
414 		[CLKID_AO_CEC]		= &g12a_aoclk_cec.hw,
415 		[CLKID_AO_CTS_RTC_OSCIN] = &g12a_aoclk_cts_rtc_oscin.hw,
416 	},
417 	.num = NR_CLKS,
418 };
419 
420 static const struct meson_aoclk_input g12a_aoclk_inputs[] = {
421 	{ .name = "xtal",	.required = true  },
422 	{ .name = "mpeg-clk",	.required = true  },
423 	{ .name = "ext-32k-0",	.required = false },
424 };
425 
426 static const struct meson_aoclk_data g12a_aoclkc_data = {
427 	.reset_reg	= AO_RTI_GEN_CNTL_REG0,
428 	.num_reset	= ARRAY_SIZE(g12a_aoclk_reset),
429 	.reset		= g12a_aoclk_reset,
430 	.num_clks	= ARRAY_SIZE(g12a_aoclk_regmap),
431 	.clks		= g12a_aoclk_regmap,
432 	.hw_data	= &g12a_aoclk_onecell_data,
433 	.inputs		= g12a_aoclk_inputs,
434 	.num_inputs	= ARRAY_SIZE(g12a_aoclk_inputs),
435 	.input_prefix	= IN_PREFIX,
436 };
437 
438 static const struct of_device_id g12a_aoclkc_match_table[] = {
439 	{
440 		.compatible	= "amlogic,meson-g12a-aoclkc",
441 		.data		= &g12a_aoclkc_data,
442 	},
443 	{ }
444 };
445 
446 static struct platform_driver g12a_aoclkc_driver = {
447 	.probe		= meson_aoclkc_probe,
448 	.driver		= {
449 		.name	= "g12a-aoclkc",
450 		.of_match_table = g12a_aoclkc_match_table,
451 	},
452 };
453 
454 builtin_platform_driver(g12a_aoclkc_driver);
455