xref: /linux/drivers/clk/meson/gxbb-aoclk.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2 /*
3  * Copyright (c) 2016 BayLibre, SAS.
4  * Author: Neil Armstrong <narmstrong@baylibre.com>
5  */
6 #include <linux/platform_device.h>
7 #include <linux/mfd/syscon.h>
8 #include <linux/module.h>
9 #include "meson-aoclk.h"
10 
11 #include "clk-regmap.h"
12 #include "clk-dualdiv.h"
13 
14 #include <dt-bindings/clock/gxbb-aoclkc.h>
15 #include <dt-bindings/reset/gxbb-aoclkc.h>
16 
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
25 
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,				\
30 		.bit_idx = (_bit),					\
31 	},								\
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",				\
37 		},							\
38 		.num_parents = 1,					\
39 		.flags = CLK_IGNORE_UNUSED,				\
40 	},								\
41 }
42 
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);
49 
50 static struct clk_regmap ao_cts_oscin = {
51 	.data = &(struct clk_regmap_gate_data){
52 		.offset = AO_RTI_PWR_CNTL_REG0,
53 		.bit_idx = 6,
54 	},
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) {
59 			.fw_name = "xtal",
60 		},
61 		.num_parents = 1,
62 	},
63 };
64 
65 static struct clk_regmap ao_32k_pre = {
66 	.data = &(struct clk_regmap_gate_data){
67 		.offset = AO_RTC_ALT_CLK_CNTL0,
68 		.bit_idx = 31,
69 	},
70 	.hw.init = &(struct clk_init_data){
71 		.name = "ao_32k_pre",
72 		.ops = &clk_regmap_gate_ops,
73 		.parent_hws = (const struct clk_hw *[]) { &ao_cts_oscin.hw },
74 		.num_parents = 1,
75 	},
76 };
77 
78 static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = {
79 	{
80 		.dual	= 1,
81 		.n1	= 733,
82 		.m1	= 8,
83 		.n2	= 732,
84 		.m2	= 11,
85 	}, {}
86 };
87 
88 static struct clk_regmap ao_32k_div = {
89 	.data = &(struct meson_clk_dualdiv_data){
90 		.n1 = {
91 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
92 			.shift   = 0,
93 			.width   = 12,
94 		},
95 		.n2 = {
96 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
97 			.shift   = 12,
98 			.width   = 12,
99 		},
100 		.m1 = {
101 			.reg_off = AO_RTC_ALT_CLK_CNTL1,
102 			.shift   = 0,
103 			.width   = 12,
104 		},
105 		.m2 = {
106 			.reg_off = AO_RTC_ALT_CLK_CNTL1,
107 			.shift   = 12,
108 			.width   = 12,
109 		},
110 		.dual = {
111 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
112 			.shift   = 28,
113 			.width   = 1,
114 		},
115 		.table = gxbb_32k_div_table,
116 	},
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 },
121 		.num_parents = 1,
122 	},
123 };
124 
125 static struct clk_regmap ao_32k_sel = {
126 	.data = &(struct clk_regmap_mux_data) {
127 		.offset = AO_RTC_ALT_CLK_CNTL1,
128 		.mask = 0x1,
129 		.shift = 24,
130 		.flags = CLK_MUX_ROUND_CLOSEST,
131 	},
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 *[]) {
136 			&ao_32k_div.hw,
137 			&ao_32k_pre.hw
138 		},
139 		.num_parents = 2,
140 		.flags = CLK_SET_RATE_PARENT,
141 	},
142 };
143 
144 static struct clk_regmap ao_32k = {
145 	.data = &(struct clk_regmap_gate_data){
146 		.offset = AO_RTC_ALT_CLK_CNTL0,
147 		.bit_idx = 30,
148 	},
149 	.hw.init = &(struct clk_init_data){
150 		.name = "ao_32k",
151 		.ops = &clk_regmap_gate_ops,
152 		.parent_hws = (const struct clk_hw *[]) { &ao_32k_sel.hw },
153 		.num_parents = 1,
154 		.flags = CLK_SET_RATE_PARENT,
155 	},
156 };
157 
158 static struct clk_regmap ao_cts_rtc_oscin = {
159 	.data = &(struct clk_regmap_mux_data) {
160 		.offset = AO_RTI_PWR_CNTL_REG0,
161 		.mask = 0x7,
162 		.shift = 10,
163 		.table = (u32[]){ 1, 2, 3, 4 },
164 		.flags = CLK_MUX_ROUND_CLOSEST,
165 	},
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 },
174 		},
175 		.num_parents = 4,
176 		.flags = CLK_SET_RATE_PARENT,
177 	},
178 };
179 
180 static struct clk_regmap ao_clk81 = {
181 	.data = &(struct clk_regmap_mux_data) {
182 		.offset = AO_RTI_PWR_CNTL_REG0,
183 		.mask = 0x1,
184 		.shift = 0,
185 		.flags = CLK_MUX_ROUND_CLOSEST,
186 	},
187 	.hw.init = &(struct clk_init_data){
188 		.name = "ao_clk81",
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 },
193 		},
194 		.num_parents = 2,
195 		.flags = CLK_SET_RATE_PARENT,
196 	},
197 };
198 
199 static struct clk_regmap ao_cts_cec = {
200 	.data = &(struct clk_regmap_mux_data) {
201 		.offset = AO_CRT_CLK_CNTL1,
202 		.mask = 0x1,
203 		.shift = 27,
204 		.flags = CLK_MUX_ROUND_CLOSEST,
205 	},
206 	.hw.init = &(struct clk_init_data){
207 		.name = "ao_cts_cec",
208 		.ops = &clk_regmap_mux_ops,
209 		/*
210 		 * FIXME: The 'fixme' parent obviously does not exist.
211 		 *
212 		 * ATM, CCF won't call get_parent() if num_parents is 1. It
213 		 * does not allow NULL as a parent name either.
214 		 *
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
218 		 *
219 		 * Until CCF gets fixed, adding this fake parent that won't
220 		 * ever be registered should work around the problem
221 		 */
222 		.parent_data = (const struct clk_parent_data []) {
223 			{ .name = "fixme", .index = -1, },
224 			{ .hw = &ao_cts_rtc_oscin.hw },
225 		},
226 		.num_parents = 2,
227 		.flags = CLK_SET_RATE_PARENT,
228 	},
229 };
230 
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,
238 };
239 
240 static struct clk_regmap *gxbb_aoclk[] = {
241 	&remote_ao,
242 	&i2c_master_ao,
243 	&i2c_slave_ao,
244 	&uart1_ao,
245 	&uart2_ao,
246 	&ir_blaster_ao,
247 	&ao_cts_oscin,
248 	&ao_32k_pre,
249 	&ao_32k_div,
250 	&ao_32k_sel,
251 	&ao_32k,
252 	&ao_cts_rtc_oscin,
253 	&ao_clk81,
254 	&ao_cts_cec,
255 };
256 
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,
272 };
273 
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),
279 	.clks		= gxbb_aoclk,
280 	.hw_clks	= {
281 		.hws	= gxbb_aoclk_hw_clks,
282 		.num	= ARRAY_SIZE(gxbb_aoclk_hw_clks),
283 	},
284 };
285 
286 static const struct of_device_id gxbb_aoclkc_match_table[] = {
287 	{
288 		.compatible	= "amlogic,meson-gx-aoclkc",
289 		.data		= &gxbb_aoclkc_data,
290 	},
291 	{ }
292 };
293 MODULE_DEVICE_TABLE(of, gxbb_aoclkc_match_table);
294 
295 static struct platform_driver gxbb_aoclkc_driver = {
296 	.probe		= meson_aoclkc_probe,
297 	.driver		= {
298 		.name	= "gxbb-aoclkc",
299 		.of_match_table = gxbb_aoclkc_match_table,
300 	},
301 };
302 module_platform_driver(gxbb_aoclkc_driver);
303 
304 MODULE_DESCRIPTION("Amlogic GXBB Always-ON Clock Controller driver");
305 MODULE_LICENSE("GPL");
306 MODULE_IMPORT_NS(CLK_MESON);
307