1 // SPDX-License-Identifier: GPL-2.0+
3 * Amlogic Meson-AXG Clock Controller Driver
5 * Copyright (c) 2016 Baylibre SAS.
6 * Author: Michael Turquette <mturquette@baylibre.com>
8 * Copyright (c) 2019 Baylibre SAS.
9 * Author: Neil Armstrong <narmstrong@baylibre.com>
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"
18 #include "clk-regmap.h"
19 #include "clk-dualdiv.h"
22 * AO Configuration Clock registers offsets
23 * Register offsets from the data sheet must be multiplied by 4.
25 #define AO_RTI_STATUS_REG3 0x0C
26 #define AO_RTI_PWR_CNTL_REG0 0x10
27 #define AO_RTI_GEN_CNTL_REG0 0x40
28 #define AO_CLK_GATE0 0x4c
29 #define AO_CLK_GATE0_SP 0x50
30 #define AO_OSCIN_CNTL 0x58
31 #define AO_CEC_CLK_CNTL_REG0 0x74
32 #define AO_CEC_CLK_CNTL_REG1 0x78
33 #define AO_SAR_CLK 0x90
34 #define AO_RTC_ALT_CLK_CNTL0 0x94
35 #define AO_RTC_ALT_CLK_CNTL1 0x98
38 * Like every other peripheral clock gate in Amlogic Clock drivers,
39 * we are using CLK_IGNORE_UNUSED here, so we keep the state of the
40 * bootloader. The goal is to remove this flag at some point.
41 * Actually removing it will require some extensive test to be done safely.
43 #define AXG_AO_GATE(_name, _reg, _bit) \
44 static struct clk_regmap g12a_aoclk_##_name = { \
45 .data = &(struct clk_regmap_gate_data) { \
49 .hw.init = &(struct clk_init_data) { \
50 .name = "g12a_ao_" #_name, \
51 .ops = &clk_regmap_gate_ops, \
52 .parent_data = &(const struct clk_parent_data) { \
53 .fw_name = "mpeg-clk", \
56 .flags = CLK_IGNORE_UNUSED, \
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);
76 static struct clk_regmap g12a_aoclk_cts_oscin
= {
77 .data
= &(struct clk_regmap_gate_data
){
78 .offset
= AO_RTI_PWR_CNTL_REG0
,
81 .hw
.init
= &(struct clk_init_data
){
83 .ops
= &clk_regmap_gate_ro_ops
,
84 .parent_data
= &(const struct clk_parent_data
) {
91 static const struct meson_clk_dualdiv_param g12a_32k_div_table
[] = {
101 /* 32k_by_oscin clock */
103 static struct clk_regmap g12a_aoclk_32k_by_oscin_pre
= {
104 .data
= &(struct clk_regmap_gate_data
){
105 .offset
= AO_RTC_ALT_CLK_CNTL0
,
108 .hw
.init
= &(struct clk_init_data
){
109 .name
= "g12a_ao_32k_by_oscin_pre",
110 .ops
= &clk_regmap_gate_ops
,
111 .parent_hws
= (const struct clk_hw
*[]) {
112 &g12a_aoclk_cts_oscin
.hw
118 static struct clk_regmap g12a_aoclk_32k_by_oscin_div
= {
119 .data
= &(struct meson_clk_dualdiv_data
){
121 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
126 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
131 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
136 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
141 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
145 .table
= g12a_32k_div_table
,
147 .hw
.init
= &(struct clk_init_data
){
148 .name
= "g12a_ao_32k_by_oscin_div",
149 .ops
= &meson_clk_dualdiv_ops
,
150 .parent_hws
= (const struct clk_hw
*[]) {
151 &g12a_aoclk_32k_by_oscin_pre
.hw
157 static struct clk_regmap g12a_aoclk_32k_by_oscin_sel
= {
158 .data
= &(struct clk_regmap_mux_data
) {
159 .offset
= AO_RTC_ALT_CLK_CNTL1
,
162 .flags
= CLK_MUX_ROUND_CLOSEST
,
164 .hw
.init
= &(struct clk_init_data
){
165 .name
= "g12a_ao_32k_by_oscin_sel",
166 .ops
= &clk_regmap_mux_ops
,
167 .parent_hws
= (const struct clk_hw
*[]) {
168 &g12a_aoclk_32k_by_oscin_div
.hw
,
169 &g12a_aoclk_32k_by_oscin_pre
.hw
,
172 .flags
= CLK_SET_RATE_PARENT
,
176 static struct clk_regmap g12a_aoclk_32k_by_oscin
= {
177 .data
= &(struct clk_regmap_gate_data
){
178 .offset
= AO_RTC_ALT_CLK_CNTL0
,
181 .hw
.init
= &(struct clk_init_data
){
182 .name
= "g12a_ao_32k_by_oscin",
183 .ops
= &clk_regmap_gate_ops
,
184 .parent_hws
= (const struct clk_hw
*[]) {
185 &g12a_aoclk_32k_by_oscin_sel
.hw
188 .flags
= CLK_SET_RATE_PARENT
,
194 static struct clk_regmap g12a_aoclk_cec_pre
= {
195 .data
= &(struct clk_regmap_gate_data
){
196 .offset
= AO_CEC_CLK_CNTL_REG0
,
199 .hw
.init
= &(struct clk_init_data
){
200 .name
= "g12a_ao_cec_pre",
201 .ops
= &clk_regmap_gate_ops
,
202 .parent_hws
= (const struct clk_hw
*[]) {
203 &g12a_aoclk_cts_oscin
.hw
209 static struct clk_regmap g12a_aoclk_cec_div
= {
210 .data
= &(struct meson_clk_dualdiv_data
){
212 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
217 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
222 .reg_off
= AO_CEC_CLK_CNTL_REG1
,
227 .reg_off
= AO_CEC_CLK_CNTL_REG1
,
232 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
236 .table
= g12a_32k_div_table
,
238 .hw
.init
= &(struct clk_init_data
){
239 .name
= "g12a_ao_cec_div",
240 .ops
= &meson_clk_dualdiv_ops
,
241 .parent_hws
= (const struct clk_hw
*[]) {
242 &g12a_aoclk_cec_pre
.hw
248 static struct clk_regmap g12a_aoclk_cec_sel
= {
249 .data
= &(struct clk_regmap_mux_data
) {
250 .offset
= AO_CEC_CLK_CNTL_REG1
,
253 .flags
= CLK_MUX_ROUND_CLOSEST
,
255 .hw
.init
= &(struct clk_init_data
){
256 .name
= "g12a_ao_cec_sel",
257 .ops
= &clk_regmap_mux_ops
,
258 .parent_hws
= (const struct clk_hw
*[]) {
259 &g12a_aoclk_cec_div
.hw
,
260 &g12a_aoclk_cec_pre
.hw
,
263 .flags
= CLK_SET_RATE_PARENT
,
267 static struct clk_regmap g12a_aoclk_cec
= {
268 .data
= &(struct clk_regmap_gate_data
){
269 .offset
= AO_CEC_CLK_CNTL_REG0
,
272 .hw
.init
= &(struct clk_init_data
){
273 .name
= "g12a_ao_cec",
274 .ops
= &clk_regmap_gate_ops
,
275 .parent_hws
= (const struct clk_hw
*[]) {
276 &g12a_aoclk_cec_sel
.hw
279 .flags
= CLK_SET_RATE_PARENT
,
283 static struct clk_regmap g12a_aoclk_cts_rtc_oscin
= {
284 .data
= &(struct clk_regmap_mux_data
) {
285 .offset
= AO_RTI_PWR_CNTL_REG0
,
288 .flags
= CLK_MUX_ROUND_CLOSEST
,
290 .hw
.init
= &(struct clk_init_data
){
291 .name
= "g12a_ao_cts_rtc_oscin",
292 .ops
= &clk_regmap_mux_ops
,
293 .parent_data
= (const struct clk_parent_data
[]) {
294 { .hw
= &g12a_aoclk_32k_by_oscin
.hw
},
295 { .fw_name
= "ext-32k-0", },
298 .flags
= CLK_SET_RATE_PARENT
,
302 static struct clk_regmap g12a_aoclk_clk81
= {
303 .data
= &(struct clk_regmap_mux_data
) {
304 .offset
= AO_RTI_PWR_CNTL_REG0
,
307 .flags
= CLK_MUX_ROUND_CLOSEST
,
309 .hw
.init
= &(struct clk_init_data
){
310 .name
= "g12a_ao_clk81",
311 .ops
= &clk_regmap_mux_ro_ops
,
312 .parent_data
= (const struct clk_parent_data
[]) {
313 { .fw_name
= "mpeg-clk", },
314 { .hw
= &g12a_aoclk_cts_rtc_oscin
.hw
},
317 .flags
= CLK_SET_RATE_PARENT
,
321 static struct clk_regmap g12a_aoclk_saradc_mux
= {
322 .data
= &(struct clk_regmap_mux_data
) {
323 .offset
= AO_SAR_CLK
,
327 .hw
.init
= &(struct clk_init_data
){
328 .name
= "g12a_ao_saradc_mux",
329 .ops
= &clk_regmap_mux_ops
,
330 .parent_data
= (const struct clk_parent_data
[]) {
331 { .fw_name
= "xtal", },
332 { .hw
= &g12a_aoclk_clk81
.hw
},
338 static struct clk_regmap g12a_aoclk_saradc_div
= {
339 .data
= &(struct clk_regmap_div_data
) {
340 .offset
= AO_SAR_CLK
,
344 .hw
.init
= &(struct clk_init_data
){
345 .name
= "g12a_ao_saradc_div",
346 .ops
= &clk_regmap_divider_ops
,
347 .parent_hws
= (const struct clk_hw
*[]) {
348 &g12a_aoclk_saradc_mux
.hw
351 .flags
= CLK_SET_RATE_PARENT
,
355 static struct clk_regmap g12a_aoclk_saradc_gate
= {
356 .data
= &(struct clk_regmap_gate_data
) {
357 .offset
= AO_SAR_CLK
,
360 .hw
.init
= &(struct clk_init_data
){
361 .name
= "g12a_ao_saradc_gate",
362 .ops
= &clk_regmap_gate_ops
,
363 .parent_hws
= (const struct clk_hw
*[]) {
364 &g12a_aoclk_saradc_div
.hw
367 .flags
= CLK_SET_RATE_PARENT
,
371 static const unsigned int g12a_aoclk_reset
[] = {
372 [RESET_AO_IR_IN
] = 16,
373 [RESET_AO_UART
] = 17,
374 [RESET_AO_I2C_M
] = 18,
375 [RESET_AO_I2C_S
] = 19,
376 [RESET_AO_SAR_ADC
] = 20,
377 [RESET_AO_UART2
] = 22,
378 [RESET_AO_IR_OUT
] = 23,
381 static struct clk_regmap
*g12a_aoclk_regmap
[] = {
387 &g12a_aoclk_prod_i2c
,
393 &g12a_aoclk_ahb_sram
,
397 &g12a_aoclk_cts_oscin
,
398 &g12a_aoclk_32k_by_oscin_pre
,
399 &g12a_aoclk_32k_by_oscin_div
,
400 &g12a_aoclk_32k_by_oscin_sel
,
401 &g12a_aoclk_32k_by_oscin
,
406 &g12a_aoclk_cts_rtc_oscin
,
408 &g12a_aoclk_saradc_mux
,
409 &g12a_aoclk_saradc_div
,
410 &g12a_aoclk_saradc_gate
,
413 static const struct clk_hw_onecell_data g12a_aoclk_onecell_data
= {
415 [CLKID_AO_AHB
] = &g12a_aoclk_ahb
.hw
,
416 [CLKID_AO_IR_IN
] = &g12a_aoclk_ir_in
.hw
,
417 [CLKID_AO_I2C_M0
] = &g12a_aoclk_i2c_m0
.hw
,
418 [CLKID_AO_I2C_S0
] = &g12a_aoclk_i2c_s0
.hw
,
419 [CLKID_AO_UART
] = &g12a_aoclk_uart
.hw
,
420 [CLKID_AO_PROD_I2C
] = &g12a_aoclk_prod_i2c
.hw
,
421 [CLKID_AO_UART2
] = &g12a_aoclk_uart2
.hw
,
422 [CLKID_AO_IR_OUT
] = &g12a_aoclk_ir_out
.hw
,
423 [CLKID_AO_SAR_ADC
] = &g12a_aoclk_saradc
.hw
,
424 [CLKID_AO_MAILBOX
] = &g12a_aoclk_mailbox
.hw
,
425 [CLKID_AO_M3
] = &g12a_aoclk_m3
.hw
,
426 [CLKID_AO_AHB_SRAM
] = &g12a_aoclk_ahb_sram
.hw
,
427 [CLKID_AO_RTI
] = &g12a_aoclk_rti
.hw
,
428 [CLKID_AO_M4_FCLK
] = &g12a_aoclk_m4_fclk
.hw
,
429 [CLKID_AO_M4_HCLK
] = &g12a_aoclk_m4_hclk
.hw
,
430 [CLKID_AO_CLK81
] = &g12a_aoclk_clk81
.hw
,
431 [CLKID_AO_SAR_ADC_SEL
] = &g12a_aoclk_saradc_mux
.hw
,
432 [CLKID_AO_SAR_ADC_DIV
] = &g12a_aoclk_saradc_div
.hw
,
433 [CLKID_AO_SAR_ADC_CLK
] = &g12a_aoclk_saradc_gate
.hw
,
434 [CLKID_AO_CTS_OSCIN
] = &g12a_aoclk_cts_oscin
.hw
,
435 [CLKID_AO_32K_PRE
] = &g12a_aoclk_32k_by_oscin_pre
.hw
,
436 [CLKID_AO_32K_DIV
] = &g12a_aoclk_32k_by_oscin_div
.hw
,
437 [CLKID_AO_32K_SEL
] = &g12a_aoclk_32k_by_oscin_sel
.hw
,
438 [CLKID_AO_32K
] = &g12a_aoclk_32k_by_oscin
.hw
,
439 [CLKID_AO_CEC_PRE
] = &g12a_aoclk_cec_pre
.hw
,
440 [CLKID_AO_CEC_DIV
] = &g12a_aoclk_cec_div
.hw
,
441 [CLKID_AO_CEC_SEL
] = &g12a_aoclk_cec_sel
.hw
,
442 [CLKID_AO_CEC
] = &g12a_aoclk_cec
.hw
,
443 [CLKID_AO_CTS_RTC_OSCIN
] = &g12a_aoclk_cts_rtc_oscin
.hw
,
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_data
= &g12a_aoclk_onecell_data
,
457 static const struct of_device_id g12a_aoclkc_match_table
[] = {
459 .compatible
= "amlogic,meson-g12a-aoclkc",
460 .data
= &g12a_aoclkc_data
,
465 static struct platform_driver g12a_aoclkc_driver
= {
466 .probe
= meson_aoclkc_probe
,
468 .name
= "g12a-aoclkc",
469 .of_match_table
= g12a_aoclkc_match_table
,
473 builtin_platform_driver(g12a_aoclkc_driver
);