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 "meson-aoclk.h"
9 #include "gxbb-aoclk.h"
11 #include "clk-regmap.h"
12 #include "clk-dualdiv.h"
14 #define IN_PREFIX "ao-in-"
16 /* AO Configuration Clock registers offsets */
17 #define AO_RTI_PWR_CNTL_REG1 0x0c
18 #define AO_RTI_PWR_CNTL_REG0 0x10
19 #define AO_RTI_GEN_CNTL_REG0 0x40
20 #define AO_OSCIN_CNTL 0x58
21 #define AO_CRT_CLK_CNTL1 0x68
22 #define AO_RTC_ALT_CLK_CNTL0 0x94
23 #define AO_RTC_ALT_CLK_CNTL1 0x98
25 #define GXBB_AO_GATE(_name, _bit) \
26 static struct clk_regmap _name##_ao = { \
27 .data = &(struct clk_regmap_gate_data) { \
28 .offset = AO_RTI_GEN_CNTL_REG0, \
31 .hw.init = &(struct clk_init_data) { \
32 .name = #_name "_ao", \
33 .ops = &clk_regmap_gate_ops, \
34 .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \
36 .flags = CLK_IGNORE_UNUSED, \
40 GXBB_AO_GATE(remote
, 0);
41 GXBB_AO_GATE(i2c_master
, 1);
42 GXBB_AO_GATE(i2c_slave
, 2);
43 GXBB_AO_GATE(uart1
, 3);
44 GXBB_AO_GATE(uart2
, 5);
45 GXBB_AO_GATE(ir_blaster
, 6);
47 static struct clk_regmap ao_cts_oscin
= {
48 .data
= &(struct clk_regmap_gate_data
){
49 .offset
= AO_RTI_PWR_CNTL_REG0
,
52 .hw
.init
= &(struct clk_init_data
){
53 .name
= "ao_cts_oscin",
54 .ops
= &clk_regmap_gate_ro_ops
,
55 .parent_names
= (const char *[]){ IN_PREFIX
"xtal" },
60 static struct clk_regmap ao_32k_pre
= {
61 .data
= &(struct clk_regmap_gate_data
){
62 .offset
= AO_RTC_ALT_CLK_CNTL0
,
65 .hw
.init
= &(struct clk_init_data
){
67 .ops
= &clk_regmap_gate_ops
,
68 .parent_names
= (const char *[]){ "ao_cts_oscin" },
73 static const struct meson_clk_dualdiv_param gxbb_32k_div_table
[] = {
83 static struct clk_regmap ao_32k_div
= {
84 .data
= &(struct meson_clk_dualdiv_data
){
86 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
91 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
96 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
101 .reg_off
= AO_RTC_ALT_CLK_CNTL1
,
106 .reg_off
= AO_RTC_ALT_CLK_CNTL0
,
110 .table
= gxbb_32k_div_table
,
112 .hw
.init
= &(struct clk_init_data
){
113 .name
= "ao_32k_div",
114 .ops
= &meson_clk_dualdiv_ops
,
115 .parent_names
= (const char *[]){ "ao_32k_pre" },
120 static struct clk_regmap ao_32k_sel
= {
121 .data
= &(struct clk_regmap_mux_data
) {
122 .offset
= AO_RTC_ALT_CLK_CNTL1
,
125 .flags
= CLK_MUX_ROUND_CLOSEST
,
127 .hw
.init
= &(struct clk_init_data
){
128 .name
= "ao_32k_sel",
129 .ops
= &clk_regmap_mux_ops
,
130 .parent_names
= (const char *[]){ "ao_32k_div",
133 .flags
= CLK_SET_RATE_PARENT
,
137 static struct clk_regmap ao_32k
= {
138 .data
= &(struct clk_regmap_gate_data
){
139 .offset
= AO_RTC_ALT_CLK_CNTL0
,
142 .hw
.init
= &(struct clk_init_data
){
144 .ops
= &clk_regmap_gate_ops
,
145 .parent_names
= (const char *[]){ "ao_32k_sel" },
147 .flags
= CLK_SET_RATE_PARENT
,
151 static struct clk_regmap ao_cts_rtc_oscin
= {
152 .data
= &(struct clk_regmap_mux_data
) {
153 .offset
= AO_RTI_PWR_CNTL_REG0
,
156 .table
= (u32
[]){ 1, 2, 3, 4 },
157 .flags
= CLK_MUX_ROUND_CLOSEST
,
159 .hw
.init
= &(struct clk_init_data
){
160 .name
= "ao_cts_rtc_oscin",
161 .ops
= &clk_regmap_mux_ops
,
162 .parent_names
= (const char *[]){ IN_PREFIX
"ext-32k-0",
163 IN_PREFIX
"ext-32k-1",
164 IN_PREFIX
"ext-32k-2",
167 .flags
= CLK_SET_RATE_PARENT
,
171 static struct clk_regmap ao_clk81
= {
172 .data
= &(struct clk_regmap_mux_data
) {
173 .offset
= AO_RTI_PWR_CNTL_REG0
,
176 .flags
= CLK_MUX_ROUND_CLOSEST
,
178 .hw
.init
= &(struct clk_init_data
){
180 .ops
= &clk_regmap_mux_ro_ops
,
181 .parent_names
= (const char *[]){ IN_PREFIX
"mpeg-clk",
182 "ao_cts_rtc_oscin" },
184 .flags
= CLK_SET_RATE_PARENT
,
188 static struct clk_regmap ao_cts_cec
= {
189 .data
= &(struct clk_regmap_mux_data
) {
190 .offset
= AO_CRT_CLK_CNTL1
,
193 .flags
= CLK_MUX_ROUND_CLOSEST
,
195 .hw
.init
= &(struct clk_init_data
){
196 .name
= "ao_cts_cec",
197 .ops
= &clk_regmap_mux_ops
,
199 * FIXME: The 'fixme' parent obviously does not exist.
201 * ATM, CCF won't call get_parent() if num_parents is 1. It
202 * does not allow NULL as a parent name either.
204 * On this particular mux, we only know the input #1 parent
205 * but, on boot, unknown input #0 is set, so it is critical
206 * to call .get_parent() on it
208 * Until CCF gets fixed, adding this fake parent that won't
209 * ever be registered should work around the problem
211 .parent_names
= (const char *[]){ "fixme",
212 "ao_cts_rtc_oscin" },
214 .flags
= CLK_SET_RATE_PARENT
,
218 static const unsigned int gxbb_aoclk_reset
[] = {
219 [RESET_AO_REMOTE
] = 16,
220 [RESET_AO_I2C_MASTER
] = 18,
221 [RESET_AO_I2C_SLAVE
] = 19,
222 [RESET_AO_UART1
] = 17,
223 [RESET_AO_UART2
] = 22,
224 [RESET_AO_IR_BLASTER
] = 23,
227 static struct clk_regmap
*gxbb_aoclk
[] = {
244 static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data
= {
246 [CLKID_AO_REMOTE
] = &remote_ao
.hw
,
247 [CLKID_AO_I2C_MASTER
] = &i2c_master_ao
.hw
,
248 [CLKID_AO_I2C_SLAVE
] = &i2c_slave_ao
.hw
,
249 [CLKID_AO_UART1
] = &uart1_ao
.hw
,
250 [CLKID_AO_UART2
] = &uart2_ao
.hw
,
251 [CLKID_AO_IR_BLASTER
] = &ir_blaster_ao
.hw
,
252 [CLKID_AO_CEC_32K
] = &ao_cts_cec
.hw
,
253 [CLKID_AO_CTS_OSCIN
] = &ao_cts_oscin
.hw
,
254 [CLKID_AO_32K_PRE
] = &ao_32k_pre
.hw
,
255 [CLKID_AO_32K_DIV
] = &ao_32k_div
.hw
,
256 [CLKID_AO_32K_SEL
] = &ao_32k_sel
.hw
,
257 [CLKID_AO_32K
] = &ao_32k
.hw
,
258 [CLKID_AO_CTS_RTC_OSCIN
] = &ao_cts_rtc_oscin
.hw
,
259 [CLKID_AO_CLK81
] = &ao_clk81
.hw
,
264 static const struct meson_aoclk_input gxbb_aoclk_inputs
[] = {
265 { .name
= "xtal", .required
= true, },
266 { .name
= "mpeg-clk", .required
= true, },
267 {. name
= "ext-32k-0", .required
= false, },
268 {. name
= "ext-32k-1", .required
= false, },
269 {. name
= "ext-32k-2", .required
= false, },
272 static const struct meson_aoclk_data gxbb_aoclkc_data
= {
273 .reset_reg
= AO_RTI_GEN_CNTL_REG0
,
274 .num_reset
= ARRAY_SIZE(gxbb_aoclk_reset
),
275 .reset
= gxbb_aoclk_reset
,
276 .num_clks
= ARRAY_SIZE(gxbb_aoclk
),
278 .hw_data
= &gxbb_aoclk_onecell_data
,
279 .inputs
= gxbb_aoclk_inputs
,
280 .num_inputs
= ARRAY_SIZE(gxbb_aoclk_inputs
),
281 .input_prefix
= IN_PREFIX
,
284 static const struct of_device_id gxbb_aoclkc_match_table
[] = {
286 .compatible
= "amlogic,meson-gx-aoclkc",
287 .data
= &gxbb_aoclkc_data
,
292 static struct platform_driver gxbb_aoclkc_driver
= {
293 .probe
= meson_aoclkc_probe
,
295 .name
= "gxbb-aoclkc",
296 .of_match_table
= gxbb_aoclkc_match_table
,
299 builtin_platform_driver(gxbb_aoclkc_driver
);