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 <linux/module.h>
16 #include "meson-aoclk.h"
18 #include "clk-regmap.h"
19 #include "clk-dualdiv.h"
21 #include <dt-bindings/clock/g12a-aoclkc.h>
22 #include <dt-bindings/reset/g12a-aoclkc.h>
25 * AO Configuration Clock registers offsets
26 * Register offsets from the data sheet must be multiplied by 4.
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
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.
46 #define AXG_AO_GATE(_name, _reg, _bit) \
47 static struct clk_regmap g12a_aoclk_##_name = { \
48 .data = &(struct clk_regmap_gate_data) { \
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", \
59 .flags = CLK_IGNORE_UNUSED, \
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);
79 static struct clk_regmap g12a_aoclk_cts_oscin
= {
80 .data
= &(struct clk_regmap_gate_data
){
81 .offset
= AO_RTI_PWR_CNTL_REG0
,
84 .hw
.init
= &(struct clk_init_data
){
86 .ops
= &clk_regmap_gate_ro_ops
,
87 .parent_data
= &(const struct clk_parent_data
) {
94 static const struct meson_clk_dualdiv_param g12a_32k_div_table
[] = {
104 /* 32k_by_oscin clock */
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
,
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
121 static struct clk_regmap g12a_aoclk_32k_by_oscin_div
= {
122 .data
= &(struct meson_clk_dualdiv_data
){
124 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
129 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
134 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
139 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
144 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
148 .table
= g12a_32k_div_table
,
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
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
,
165 .flags
= CLK_MUX_ROUND_CLOSEST
,
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
,
175 .flags
= CLK_SET_RATE_PARENT
,
179 static struct clk_regmap g12a_aoclk_32k_by_oscin
= {
180 .data
= &(struct clk_regmap_gate_data
){
181 .offset
= AO_RTC_ALT_CLK_CNTL0
,
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
191 .flags
= CLK_SET_RATE_PARENT
,
197 static struct clk_regmap g12a_aoclk_cec_pre
= {
198 .data
= &(struct clk_regmap_gate_data
){
199 .offset
= AO_CEC_CLK_CNTL_REG0
,
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
212 static struct clk_regmap g12a_aoclk_cec_div
= {
213 .data
= &(struct meson_clk_dualdiv_data
){
215 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
220 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
225 .reg_off
= AO_CEC_CLK_CNTL_REG1
,
230 .reg_off
= AO_CEC_CLK_CNTL_REG1
,
235 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
239 .table
= g12a_32k_div_table
,
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
251 static struct clk_regmap g12a_aoclk_cec_sel
= {
252 .data
= &(struct clk_regmap_mux_data
) {
253 .offset
= AO_CEC_CLK_CNTL_REG1
,
256 .flags
= CLK_MUX_ROUND_CLOSEST
,
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
,
266 .flags
= CLK_SET_RATE_PARENT
,
270 static struct clk_regmap g12a_aoclk_cec
= {
271 .data
= &(struct clk_regmap_gate_data
){
272 .offset
= AO_CEC_CLK_CNTL_REG0
,
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
282 .flags
= CLK_SET_RATE_PARENT
,
286 static struct clk_regmap g12a_aoclk_cts_rtc_oscin
= {
287 .data
= &(struct clk_regmap_mux_data
) {
288 .offset
= AO_RTI_PWR_CNTL_REG0
,
291 .flags
= CLK_MUX_ROUND_CLOSEST
,
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", },
301 .flags
= CLK_SET_RATE_PARENT
,
305 static struct clk_regmap g12a_aoclk_clk81
= {
306 .data
= &(struct clk_regmap_mux_data
) {
307 .offset
= AO_RTI_PWR_CNTL_REG0
,
310 .flags
= CLK_MUX_ROUND_CLOSEST
,
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
},
320 .flags
= CLK_SET_RATE_PARENT
,
324 static struct clk_regmap g12a_aoclk_saradc_mux
= {
325 .data
= &(struct clk_regmap_mux_data
) {
326 .offset
= AO_SAR_CLK
,
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
},
341 static struct clk_regmap g12a_aoclk_saradc_div
= {
342 .data
= &(struct clk_regmap_div_data
) {
343 .offset
= AO_SAR_CLK
,
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
354 .flags
= CLK_SET_RATE_PARENT
,
358 static struct clk_regmap g12a_aoclk_saradc_gate
= {
359 .data
= &(struct clk_regmap_gate_data
) {
360 .offset
= AO_SAR_CLK
,
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
370 .flags
= CLK_SET_RATE_PARENT
,
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,
384 static struct clk_regmap
*g12a_aoclk_regmap
[] = {
390 &g12a_aoclk_prod_i2c
,
396 &g12a_aoclk_ahb_sram
,
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
,
409 &g12a_aoclk_cts_rtc_oscin
,
411 &g12a_aoclk_saradc_mux
,
412 &g12a_aoclk_saradc_div
,
413 &g12a_aoclk_saradc_gate
,
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
,
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
,
455 .hws
= g12a_aoclk_hw_clks
,
456 .num
= ARRAY_SIZE(g12a_aoclk_hw_clks
),
460 static const struct of_device_id g12a_aoclkc_match_table
[] = {
462 .compatible
= "amlogic,meson-g12a-aoclkc",
463 .data
= &g12a_aoclkc_data
,
467 MODULE_DEVICE_TABLE(of
, g12a_aoclkc_match_table
);
469 static struct platform_driver g12a_aoclkc_driver
= {
470 .probe
= meson_aoclkc_probe
,
472 .name
= "g12a-aoclkc",
473 .of_match_table
= g12a_aoclkc_match_table
,
476 module_platform_driver(g12a_aoclkc_driver
);
478 MODULE_DESCRIPTION("Amlogic G12A Always-ON Clock Controller driver");
479 MODULE_LICENSE("GPL");
480 MODULE_IMPORT_NS(CLK_MESON
);