1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
3 * Copyright (c) 2016 BayLibre, SAS.
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
6 #include <linux/platform_device.h>
7 #include <linux/mfd/syscon.h>
8 #include <linux/module.h>
9 #include "meson-aoclk.h"
11 #include "clk-regmap.h"
12 #include "clk-dualdiv.h"
14 #include <dt-bindings/clock/gxbb-aoclkc.h>
15 #include <dt-bindings/reset/gxbb-aoclkc.h>
17 /* AO Configuration Clock registers offsets */
18 #define AO_RTI_PWR_CNTL_REG1 0x0c
19 #define AO_RTI_PWR_CNTL_REG0 0x10
20 #define AO_RTI_GEN_CNTL_REG0 0x40
21 #define AO_OSCIN_CNTL 0x58
22 #define AO_CRT_CLK_CNTL1 0x68
23 #define AO_RTC_ALT_CLK_CNTL0 0x94
24 #define AO_RTC_ALT_CLK_CNTL1 0x98
26 #define GXBB_AO_GATE(_name, _bit) \
27 static struct clk_regmap _name##_ao = { \
28 .data = &(struct clk_regmap_gate_data) { \
29 .offset = AO_RTI_GEN_CNTL_REG0, \
32 .hw.init = &(struct clk_init_data) { \
33 .name = #_name "_ao", \
34 .ops = &clk_regmap_gate_ops, \
35 .parent_data = &(const struct clk_parent_data) { \
36 .fw_name = "mpeg-clk", \
39 .flags = CLK_IGNORE_UNUSED, \
43 GXBB_AO_GATE(remote
, 0);
44 GXBB_AO_GATE(i2c_master
, 1);
45 GXBB_AO_GATE(i2c_slave
, 2);
46 GXBB_AO_GATE(uart1
, 3);
47 GXBB_AO_GATE(uart2
, 5);
48 GXBB_AO_GATE(ir_blaster
, 6);
50 static struct clk_regmap ao_cts_oscin
= {
51 .data
= &(struct clk_regmap_gate_data
){
52 .offset
= AO_RTI_PWR_CNTL_REG0
,
55 .hw
.init
= &(struct clk_init_data
){
56 .name
= "ao_cts_oscin",
57 .ops
= &clk_regmap_gate_ro_ops
,
58 .parent_data
= &(const struct clk_parent_data
) {
65 static struct clk_regmap ao_32k_pre
= {
66 .data
= &(struct clk_regmap_gate_data
){
67 .offset
= AO_RTC_ALT_CLK_CNTL0
,
70 .hw
.init
= &(struct clk_init_data
){
72 .ops
= &clk_regmap_gate_ops
,
73 .parent_hws
= (const struct clk_hw
*[]) { &ao_cts_oscin
.hw
},
78 static const struct meson_clk_dualdiv_param gxbb_32k_div_table
[] = {
88 static struct clk_regmap ao_32k_div
= {
89 .data
= &(struct meson_clk_dualdiv_data
){
91 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
96 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
101 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
106 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
111 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
115 .table
= gxbb_32k_div_table
,
117 .hw
.init
= &(struct clk_init_data
){
118 .name
= "ao_32k_div",
119 .ops
= &meson_clk_dualdiv_ops
,
120 .parent_hws
= (const struct clk_hw
*[]) { &ao_32k_pre
.hw
},
125 static struct clk_regmap ao_32k_sel
= {
126 .data
= &(struct clk_regmap_mux_data
) {
127 .offset
= AO_RTC_ALT_CLK_CNTL1
,
130 .flags
= CLK_MUX_ROUND_CLOSEST
,
132 .hw
.init
= &(struct clk_init_data
){
133 .name
= "ao_32k_sel",
134 .ops
= &clk_regmap_mux_ops
,
135 .parent_hws
= (const struct clk_hw
*[]) {
140 .flags
= CLK_SET_RATE_PARENT
,
144 static struct clk_regmap ao_32k
= {
145 .data
= &(struct clk_regmap_gate_data
){
146 .offset
= AO_RTC_ALT_CLK_CNTL0
,
149 .hw
.init
= &(struct clk_init_data
){
151 .ops
= &clk_regmap_gate_ops
,
152 .parent_hws
= (const struct clk_hw
*[]) { &ao_32k_sel
.hw
},
154 .flags
= CLK_SET_RATE_PARENT
,
158 static struct clk_regmap ao_cts_rtc_oscin
= {
159 .data
= &(struct clk_regmap_mux_data
) {
160 .offset
= AO_RTI_PWR_CNTL_REG0
,
163 .table
= (u32
[]){ 1, 2, 3, 4 },
164 .flags
= CLK_MUX_ROUND_CLOSEST
,
166 .hw
.init
= &(struct clk_init_data
){
167 .name
= "ao_cts_rtc_oscin",
168 .ops
= &clk_regmap_mux_ops
,
169 .parent_data
= (const struct clk_parent_data
[]) {
170 { .fw_name
= "ext-32k-0", },
171 { .fw_name
= "ext-32k-1", },
172 { .fw_name
= "ext-32k-2", },
173 { .hw
= &ao_32k
.hw
},
176 .flags
= CLK_SET_RATE_PARENT
,
180 static struct clk_regmap ao_clk81
= {
181 .data
= &(struct clk_regmap_mux_data
) {
182 .offset
= AO_RTI_PWR_CNTL_REG0
,
185 .flags
= CLK_MUX_ROUND_CLOSEST
,
187 .hw
.init
= &(struct clk_init_data
){
189 .ops
= &clk_regmap_mux_ro_ops
,
190 .parent_data
= (const struct clk_parent_data
[]) {
191 { .fw_name
= "mpeg-clk", },
192 { .hw
= &ao_cts_rtc_oscin
.hw
},
195 .flags
= CLK_SET_RATE_PARENT
,
199 static struct clk_regmap ao_cts_cec
= {
200 .data
= &(struct clk_regmap_mux_data
) {
201 .offset
= AO_CRT_CLK_CNTL1
,
204 .flags
= CLK_MUX_ROUND_CLOSEST
,
206 .hw
.init
= &(struct clk_init_data
){
207 .name
= "ao_cts_cec",
208 .ops
= &clk_regmap_mux_ops
,
210 * FIXME: The 'fixme' parent obviously does not exist.
212 * ATM, CCF won't call get_parent() if num_parents is 1. It
213 * does not allow NULL as a parent name either.
215 * On this particular mux, we only know the input #1 parent
216 * but, on boot, unknown input #0 is set, so it is critical
217 * to call .get_parent() on it
219 * Until CCF gets fixed, adding this fake parent that won't
220 * ever be registered should work around the problem
222 .parent_data
= (const struct clk_parent_data
[]) {
223 { .name
= "fixme", .index
= -1, },
224 { .hw
= &ao_cts_rtc_oscin
.hw
},
227 .flags
= CLK_SET_RATE_PARENT
,
231 static const unsigned int gxbb_aoclk_reset
[] = {
232 [RESET_AO_REMOTE
] = 16,
233 [RESET_AO_I2C_MASTER
] = 18,
234 [RESET_AO_I2C_SLAVE
] = 19,
235 [RESET_AO_UART1
] = 17,
236 [RESET_AO_UART2
] = 22,
237 [RESET_AO_IR_BLASTER
] = 23,
240 static struct clk_regmap
*gxbb_aoclk
[] = {
257 static struct clk_hw
*gxbb_aoclk_hw_clks
[] = {
258 [CLKID_AO_REMOTE
] = &remote_ao
.hw
,
259 [CLKID_AO_I2C_MASTER
] = &i2c_master_ao
.hw
,
260 [CLKID_AO_I2C_SLAVE
] = &i2c_slave_ao
.hw
,
261 [CLKID_AO_UART1
] = &uart1_ao
.hw
,
262 [CLKID_AO_UART2
] = &uart2_ao
.hw
,
263 [CLKID_AO_IR_BLASTER
] = &ir_blaster_ao
.hw
,
264 [CLKID_AO_CEC_32K
] = &ao_cts_cec
.hw
,
265 [CLKID_AO_CTS_OSCIN
] = &ao_cts_oscin
.hw
,
266 [CLKID_AO_32K_PRE
] = &ao_32k_pre
.hw
,
267 [CLKID_AO_32K_DIV
] = &ao_32k_div
.hw
,
268 [CLKID_AO_32K_SEL
] = &ao_32k_sel
.hw
,
269 [CLKID_AO_32K
] = &ao_32k
.hw
,
270 [CLKID_AO_CTS_RTC_OSCIN
] = &ao_cts_rtc_oscin
.hw
,
271 [CLKID_AO_CLK81
] = &ao_clk81
.hw
,
274 static const struct meson_aoclk_data gxbb_aoclkc_data
= {
275 .reset_reg
= AO_RTI_GEN_CNTL_REG0
,
276 .num_reset
= ARRAY_SIZE(gxbb_aoclk_reset
),
277 .reset
= gxbb_aoclk_reset
,
278 .num_clks
= ARRAY_SIZE(gxbb_aoclk
),
281 .hws
= gxbb_aoclk_hw_clks
,
282 .num
= ARRAY_SIZE(gxbb_aoclk_hw_clks
),
286 static const struct of_device_id gxbb_aoclkc_match_table
[] = {
288 .compatible
= "amlogic,meson-gx-aoclkc",
289 .data
= &gxbb_aoclkc_data
,
293 MODULE_DEVICE_TABLE(of
, gxbb_aoclkc_match_table
);
295 static struct platform_driver gxbb_aoclkc_driver
= {
296 .probe
= meson_aoclkc_probe
,
298 .name
= "gxbb-aoclkc",
299 .of_match_table
= gxbb_aoclkc_match_table
,
302 module_platform_driver(gxbb_aoclkc_driver
);
304 MODULE_DESCRIPTION("Amlogic GXBB Always-ON Clock Controller driver");
305 MODULE_LICENSE("GPL");
306 MODULE_IMPORT_NS("CLK_MESON");