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"
17 #include "g12a-aoclk.h"
19 #include "clk-regmap.h"
20 #include "clk-dualdiv.h"
23 * AO Configuration Clock registers offsets
24 * Register offsets from the data sheet must be multiplied by 4.
26 #define AO_RTI_STATUS_REG3 0x0C
27 #define AO_RTI_PWR_CNTL_REG0 0x10
28 #define AO_RTI_GEN_CNTL_REG0 0x40
29 #define AO_CLK_GATE0 0x4c
30 #define AO_CLK_GATE0_SP 0x50
31 #define AO_OSCIN_CNTL 0x58
32 #define AO_CEC_CLK_CNTL_REG0 0x74
33 #define AO_CEC_CLK_CNTL_REG1 0x78
34 #define AO_SAR_CLK 0x90
35 #define AO_RTC_ALT_CLK_CNTL0 0x94
36 #define AO_RTC_ALT_CLK_CNTL1 0x98
39 * Like every other peripheral clock gate in Amlogic Clock drivers,
40 * we are using CLK_IGNORE_UNUSED here, so we keep the state of the
41 * bootloader. The goal is to remove this flag at some point.
42 * Actually removing it will require some extensive test to be done safely.
44 #define AXG_AO_GATE(_name, _reg, _bit) \
45 static struct clk_regmap g12a_aoclk_##_name = { \
46 .data = &(struct clk_regmap_gate_data) { \
50 .hw.init = &(struct clk_init_data) { \
51 .name = "g12a_ao_" #_name, \
52 .ops = &clk_regmap_gate_ops, \
53 .parent_data = &(const struct clk_parent_data) { \
54 .fw_name = "mpeg-clk", \
57 .flags = CLK_IGNORE_UNUSED, \
61 AXG_AO_GATE(ahb
, AO_CLK_GATE0
, 0);
62 AXG_AO_GATE(ir_in
, AO_CLK_GATE0
, 1);
63 AXG_AO_GATE(i2c_m0
, AO_CLK_GATE0
, 2);
64 AXG_AO_GATE(i2c_s0
, AO_CLK_GATE0
, 3);
65 AXG_AO_GATE(uart
, AO_CLK_GATE0
, 4);
66 AXG_AO_GATE(prod_i2c
, AO_CLK_GATE0
, 5);
67 AXG_AO_GATE(uart2
, AO_CLK_GATE0
, 6);
68 AXG_AO_GATE(ir_out
, AO_CLK_GATE0
, 7);
69 AXG_AO_GATE(saradc
, AO_CLK_GATE0
, 8);
70 AXG_AO_GATE(mailbox
, AO_CLK_GATE0_SP
, 0);
71 AXG_AO_GATE(m3
, AO_CLK_GATE0_SP
, 1);
72 AXG_AO_GATE(ahb_sram
, AO_CLK_GATE0_SP
, 2);
73 AXG_AO_GATE(rti
, AO_CLK_GATE0_SP
, 3);
74 AXG_AO_GATE(m4_fclk
, AO_CLK_GATE0_SP
, 4);
75 AXG_AO_GATE(m4_hclk
, AO_CLK_GATE0_SP
, 5);
77 static struct clk_regmap g12a_aoclk_cts_oscin
= {
78 .data
= &(struct clk_regmap_gate_data
){
79 .offset
= AO_RTI_PWR_CNTL_REG0
,
82 .hw
.init
= &(struct clk_init_data
){
84 .ops
= &clk_regmap_gate_ro_ops
,
85 .parent_data
= &(const struct clk_parent_data
) {
92 static const struct meson_clk_dualdiv_param g12a_32k_div_table
[] = {
102 /* 32k_by_oscin clock */
104 static struct clk_regmap g12a_aoclk_32k_by_oscin_pre
= {
105 .data
= &(struct clk_regmap_gate_data
){
106 .offset
= AO_RTC_ALT_CLK_CNTL0
,
109 .hw
.init
= &(struct clk_init_data
){
110 .name
= "g12a_ao_32k_by_oscin_pre",
111 .ops
= &clk_regmap_gate_ops
,
112 .parent_hws
= (const struct clk_hw
*[]) {
113 &g12a_aoclk_cts_oscin
.hw
119 static struct clk_regmap g12a_aoclk_32k_by_oscin_div
= {
120 .data
= &(struct meson_clk_dualdiv_data
){
122 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
127 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
132 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
137 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
142 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
146 .table
= g12a_32k_div_table
,
148 .hw
.init
= &(struct clk_init_data
){
149 .name
= "g12a_ao_32k_by_oscin_div",
150 .ops
= &meson_clk_dualdiv_ops
,
151 .parent_hws
= (const struct clk_hw
*[]) {
152 &g12a_aoclk_32k_by_oscin_pre
.hw
158 static struct clk_regmap g12a_aoclk_32k_by_oscin_sel
= {
159 .data
= &(struct clk_regmap_mux_data
) {
160 .offset
= AO_RTC_ALT_CLK_CNTL1
,
163 .flags
= CLK_MUX_ROUND_CLOSEST
,
165 .hw
.init
= &(struct clk_init_data
){
166 .name
= "g12a_ao_32k_by_oscin_sel",
167 .ops
= &clk_regmap_mux_ops
,
168 .parent_hws
= (const struct clk_hw
*[]) {
169 &g12a_aoclk_32k_by_oscin_div
.hw
,
170 &g12a_aoclk_32k_by_oscin_pre
.hw
,
173 .flags
= CLK_SET_RATE_PARENT
,
177 static struct clk_regmap g12a_aoclk_32k_by_oscin
= {
178 .data
= &(struct clk_regmap_gate_data
){
179 .offset
= AO_RTC_ALT_CLK_CNTL0
,
182 .hw
.init
= &(struct clk_init_data
){
183 .name
= "g12a_ao_32k_by_oscin",
184 .ops
= &clk_regmap_gate_ops
,
185 .parent_hws
= (const struct clk_hw
*[]) {
186 &g12a_aoclk_32k_by_oscin_sel
.hw
189 .flags
= CLK_SET_RATE_PARENT
,
195 static struct clk_regmap g12a_aoclk_cec_pre
= {
196 .data
= &(struct clk_regmap_gate_data
){
197 .offset
= AO_CEC_CLK_CNTL_REG0
,
200 .hw
.init
= &(struct clk_init_data
){
201 .name
= "g12a_ao_cec_pre",
202 .ops
= &clk_regmap_gate_ops
,
203 .parent_hws
= (const struct clk_hw
*[]) {
204 &g12a_aoclk_cts_oscin
.hw
210 static struct clk_regmap g12a_aoclk_cec_div
= {
211 .data
= &(struct meson_clk_dualdiv_data
){
213 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
218 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
223 .reg_off
= AO_CEC_CLK_CNTL_REG1
,
228 .reg_off
= AO_CEC_CLK_CNTL_REG1
,
233 .reg_off
= AO_CEC_CLK_CNTL_REG0
,
237 .table
= g12a_32k_div_table
,
239 .hw
.init
= &(struct clk_init_data
){
240 .name
= "g12a_ao_cec_div",
241 .ops
= &meson_clk_dualdiv_ops
,
242 .parent_hws
= (const struct clk_hw
*[]) {
243 &g12a_aoclk_cec_pre
.hw
249 static struct clk_regmap g12a_aoclk_cec_sel
= {
250 .data
= &(struct clk_regmap_mux_data
) {
251 .offset
= AO_CEC_CLK_CNTL_REG1
,
254 .flags
= CLK_MUX_ROUND_CLOSEST
,
256 .hw
.init
= &(struct clk_init_data
){
257 .name
= "g12a_ao_cec_sel",
258 .ops
= &clk_regmap_mux_ops
,
259 .parent_hws
= (const struct clk_hw
*[]) {
260 &g12a_aoclk_cec_div
.hw
,
261 &g12a_aoclk_cec_pre
.hw
,
264 .flags
= CLK_SET_RATE_PARENT
,
268 static struct clk_regmap g12a_aoclk_cec
= {
269 .data
= &(struct clk_regmap_gate_data
){
270 .offset
= AO_CEC_CLK_CNTL_REG0
,
273 .hw
.init
= &(struct clk_init_data
){
274 .name
= "g12a_ao_cec",
275 .ops
= &clk_regmap_gate_ops
,
276 .parent_hws
= (const struct clk_hw
*[]) {
277 &g12a_aoclk_cec_sel
.hw
280 .flags
= CLK_SET_RATE_PARENT
,
284 static struct clk_regmap g12a_aoclk_cts_rtc_oscin
= {
285 .data
= &(struct clk_regmap_mux_data
) {
286 .offset
= AO_RTI_PWR_CNTL_REG0
,
289 .flags
= CLK_MUX_ROUND_CLOSEST
,
291 .hw
.init
= &(struct clk_init_data
){
292 .name
= "g12a_ao_cts_rtc_oscin",
293 .ops
= &clk_regmap_mux_ops
,
294 .parent_data
= (const struct clk_parent_data
[]) {
295 { .hw
= &g12a_aoclk_32k_by_oscin
.hw
},
296 { .fw_name
= "ext-32k-0", },
299 .flags
= CLK_SET_RATE_PARENT
,
303 static struct clk_regmap g12a_aoclk_clk81
= {
304 .data
= &(struct clk_regmap_mux_data
) {
305 .offset
= AO_RTI_PWR_CNTL_REG0
,
308 .flags
= CLK_MUX_ROUND_CLOSEST
,
310 .hw
.init
= &(struct clk_init_data
){
311 .name
= "g12a_ao_clk81",
312 .ops
= &clk_regmap_mux_ro_ops
,
313 .parent_data
= (const struct clk_parent_data
[]) {
314 { .fw_name
= "mpeg-clk", },
315 { .hw
= &g12a_aoclk_cts_rtc_oscin
.hw
},
318 .flags
= CLK_SET_RATE_PARENT
,
322 static struct clk_regmap g12a_aoclk_saradc_mux
= {
323 .data
= &(struct clk_regmap_mux_data
) {
324 .offset
= AO_SAR_CLK
,
328 .hw
.init
= &(struct clk_init_data
){
329 .name
= "g12a_ao_saradc_mux",
330 .ops
= &clk_regmap_mux_ops
,
331 .parent_data
= (const struct clk_parent_data
[]) {
332 { .fw_name
= "xtal", },
333 { .hw
= &g12a_aoclk_clk81
.hw
},
339 static struct clk_regmap g12a_aoclk_saradc_div
= {
340 .data
= &(struct clk_regmap_div_data
) {
341 .offset
= AO_SAR_CLK
,
345 .hw
.init
= &(struct clk_init_data
){
346 .name
= "g12a_ao_saradc_div",
347 .ops
= &clk_regmap_divider_ops
,
348 .parent_hws
= (const struct clk_hw
*[]) {
349 &g12a_aoclk_saradc_mux
.hw
352 .flags
= CLK_SET_RATE_PARENT
,
356 static struct clk_regmap g12a_aoclk_saradc_gate
= {
357 .data
= &(struct clk_regmap_gate_data
) {
358 .offset
= AO_SAR_CLK
,
361 .hw
.init
= &(struct clk_init_data
){
362 .name
= "g12a_ao_saradc_gate",
363 .ops
= &clk_regmap_gate_ops
,
364 .parent_hws
= (const struct clk_hw
*[]) {
365 &g12a_aoclk_saradc_div
.hw
368 .flags
= CLK_SET_RATE_PARENT
,
372 static const unsigned int g12a_aoclk_reset
[] = {
373 [RESET_AO_IR_IN
] = 16,
374 [RESET_AO_UART
] = 17,
375 [RESET_AO_I2C_M
] = 18,
376 [RESET_AO_I2C_S
] = 19,
377 [RESET_AO_SAR_ADC
] = 20,
378 [RESET_AO_UART2
] = 22,
379 [RESET_AO_IR_OUT
] = 23,
382 static struct clk_regmap
*g12a_aoclk_regmap
[] = {
388 &g12a_aoclk_prod_i2c
,
394 &g12a_aoclk_ahb_sram
,
398 &g12a_aoclk_cts_oscin
,
399 &g12a_aoclk_32k_by_oscin_pre
,
400 &g12a_aoclk_32k_by_oscin_div
,
401 &g12a_aoclk_32k_by_oscin_sel
,
402 &g12a_aoclk_32k_by_oscin
,
407 &g12a_aoclk_cts_rtc_oscin
,
409 &g12a_aoclk_saradc_mux
,
410 &g12a_aoclk_saradc_div
,
411 &g12a_aoclk_saradc_gate
,
414 static const struct clk_hw_onecell_data g12a_aoclk_onecell_data
= {
416 [CLKID_AO_AHB
] = &g12a_aoclk_ahb
.hw
,
417 [CLKID_AO_IR_IN
] = &g12a_aoclk_ir_in
.hw
,
418 [CLKID_AO_I2C_M0
] = &g12a_aoclk_i2c_m0
.hw
,
419 [CLKID_AO_I2C_S0
] = &g12a_aoclk_i2c_s0
.hw
,
420 [CLKID_AO_UART
] = &g12a_aoclk_uart
.hw
,
421 [CLKID_AO_PROD_I2C
] = &g12a_aoclk_prod_i2c
.hw
,
422 [CLKID_AO_UART2
] = &g12a_aoclk_uart2
.hw
,
423 [CLKID_AO_IR_OUT
] = &g12a_aoclk_ir_out
.hw
,
424 [CLKID_AO_SAR_ADC
] = &g12a_aoclk_saradc
.hw
,
425 [CLKID_AO_MAILBOX
] = &g12a_aoclk_mailbox
.hw
,
426 [CLKID_AO_M3
] = &g12a_aoclk_m3
.hw
,
427 [CLKID_AO_AHB_SRAM
] = &g12a_aoclk_ahb_sram
.hw
,
428 [CLKID_AO_RTI
] = &g12a_aoclk_rti
.hw
,
429 [CLKID_AO_M4_FCLK
] = &g12a_aoclk_m4_fclk
.hw
,
430 [CLKID_AO_M4_HCLK
] = &g12a_aoclk_m4_hclk
.hw
,
431 [CLKID_AO_CLK81
] = &g12a_aoclk_clk81
.hw
,
432 [CLKID_AO_SAR_ADC_SEL
] = &g12a_aoclk_saradc_mux
.hw
,
433 [CLKID_AO_SAR_ADC_DIV
] = &g12a_aoclk_saradc_div
.hw
,
434 [CLKID_AO_SAR_ADC_CLK
] = &g12a_aoclk_saradc_gate
.hw
,
435 [CLKID_AO_CTS_OSCIN
] = &g12a_aoclk_cts_oscin
.hw
,
436 [CLKID_AO_32K_PRE
] = &g12a_aoclk_32k_by_oscin_pre
.hw
,
437 [CLKID_AO_32K_DIV
] = &g12a_aoclk_32k_by_oscin_div
.hw
,
438 [CLKID_AO_32K_SEL
] = &g12a_aoclk_32k_by_oscin_sel
.hw
,
439 [CLKID_AO_32K
] = &g12a_aoclk_32k_by_oscin
.hw
,
440 [CLKID_AO_CEC_PRE
] = &g12a_aoclk_cec_pre
.hw
,
441 [CLKID_AO_CEC_DIV
] = &g12a_aoclk_cec_div
.hw
,
442 [CLKID_AO_CEC_SEL
] = &g12a_aoclk_cec_sel
.hw
,
443 [CLKID_AO_CEC
] = &g12a_aoclk_cec
.hw
,
444 [CLKID_AO_CTS_RTC_OSCIN
] = &g12a_aoclk_cts_rtc_oscin
.hw
,
449 static const struct meson_aoclk_data g12a_aoclkc_data
= {
450 .reset_reg
= AO_RTI_GEN_CNTL_REG0
,
451 .num_reset
= ARRAY_SIZE(g12a_aoclk_reset
),
452 .reset
= g12a_aoclk_reset
,
453 .num_clks
= ARRAY_SIZE(g12a_aoclk_regmap
),
454 .clks
= g12a_aoclk_regmap
,
455 .hw_data
= &g12a_aoclk_onecell_data
,
458 static const struct of_device_id g12a_aoclkc_match_table
[] = {
460 .compatible
= "amlogic,meson-g12a-aoclkc",
461 .data
= &g12a_aoclkc_data
,
465 MODULE_DEVICE_TABLE(of
, g12a_aoclkc_match_table
);
467 static struct platform_driver g12a_aoclkc_driver
= {
468 .probe
= meson_aoclkc_probe
,
470 .name
= "g12a-aoclkc",
471 .of_match_table
= g12a_aoclkc_match_table
,
475 module_platform_driver(g12a_aoclkc_driver
);
476 MODULE_LICENSE("GPL v2");