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