xref: /linux/drivers/clk/meson/gxbb-aoclk.c (revision 522ba450b56fff29f868b1552bdc2965f55de7ed)
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 static const struct clk_parent_data gxbb_ao_pclk_parents = { .fw_name = "mpeg-clk" };
27 
28 #define GXBB_AO_PCLK(_name, _bit, _flags)			\
29 	MESON_PCLK(gxbb_ao_##_name, AO_RTI_GEN_CNTL_REG0, _bit, \
30 		   &gxbb_ao_pclk_parents, _flags)
31 
32 static GXBB_AO_PCLK(remote,	0, CLK_IGNORE_UNUSED);
33 static GXBB_AO_PCLK(i2c_master,	1, CLK_IGNORE_UNUSED);
34 static GXBB_AO_PCLK(i2c_slave,	2, CLK_IGNORE_UNUSED);
35 static GXBB_AO_PCLK(uart1,	3, CLK_IGNORE_UNUSED);
36 static GXBB_AO_PCLK(uart2,	5, CLK_IGNORE_UNUSED);
37 static GXBB_AO_PCLK(ir_blaster,	6, CLK_IGNORE_UNUSED);
38 
39 static struct clk_regmap gxbb_ao_cts_oscin = {
40 	.data = &(struct clk_regmap_gate_data){
41 		.offset = AO_RTI_PWR_CNTL_REG0,
42 		.bit_idx = 6,
43 	},
44 	.hw.init = &(struct clk_init_data){
45 		.name = "ao_cts_oscin",
46 		.ops = &clk_regmap_gate_ro_ops,
47 		.parent_data = &(const struct clk_parent_data) {
48 			.fw_name = "xtal",
49 		},
50 		.num_parents = 1,
51 	},
52 };
53 
54 static struct clk_regmap gxbb_ao_32k_pre = {
55 	.data = &(struct clk_regmap_gate_data){
56 		.offset = AO_RTC_ALT_CLK_CNTL0,
57 		.bit_idx = 31,
58 	},
59 	.hw.init = &(struct clk_init_data){
60 		.name = "ao_32k_pre",
61 		.ops = &clk_regmap_gate_ops,
62 		.parent_hws = (const struct clk_hw *[]) { &gxbb_ao_cts_oscin.hw },
63 		.num_parents = 1,
64 	},
65 };
66 
67 static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = {
68 	{
69 		.dual	= 1,
70 		.n1	= 733,
71 		.m1	= 8,
72 		.n2	= 732,
73 		.m2	= 11,
74 	}, {}
75 };
76 
77 static struct clk_regmap gxbb_ao_32k_div = {
78 	.data = &(struct meson_clk_dualdiv_data){
79 		.n1 = {
80 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
81 			.shift   = 0,
82 			.width   = 12,
83 		},
84 		.n2 = {
85 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
86 			.shift   = 12,
87 			.width   = 12,
88 		},
89 		.m1 = {
90 			.reg_off = AO_RTC_ALT_CLK_CNTL1,
91 			.shift   = 0,
92 			.width   = 12,
93 		},
94 		.m2 = {
95 			.reg_off = AO_RTC_ALT_CLK_CNTL1,
96 			.shift   = 12,
97 			.width   = 12,
98 		},
99 		.dual = {
100 			.reg_off = AO_RTC_ALT_CLK_CNTL0,
101 			.shift   = 28,
102 			.width   = 1,
103 		},
104 		.table = gxbb_32k_div_table,
105 	},
106 	.hw.init = &(struct clk_init_data){
107 		.name = "ao_32k_div",
108 		.ops = &meson_clk_dualdiv_ops,
109 		.parent_hws = (const struct clk_hw *[]) { &gxbb_ao_32k_pre.hw },
110 		.num_parents = 1,
111 	},
112 };
113 
114 static struct clk_regmap gxbb_ao_32k_sel = {
115 	.data = &(struct clk_regmap_mux_data) {
116 		.offset = AO_RTC_ALT_CLK_CNTL1,
117 		.mask = 0x1,
118 		.shift = 24,
119 		.flags = CLK_MUX_ROUND_CLOSEST,
120 	},
121 	.hw.init = &(struct clk_init_data){
122 		.name = "ao_32k_sel",
123 		.ops = &clk_regmap_mux_ops,
124 		.parent_hws = (const struct clk_hw *[]) {
125 			&gxbb_ao_32k_div.hw,
126 			&gxbb_ao_32k_pre.hw
127 		},
128 		.num_parents = 2,
129 		.flags = CLK_SET_RATE_PARENT,
130 	},
131 };
132 
133 static struct clk_regmap gxbb_ao_32k = {
134 	.data = &(struct clk_regmap_gate_data){
135 		.offset = AO_RTC_ALT_CLK_CNTL0,
136 		.bit_idx = 30,
137 	},
138 	.hw.init = &(struct clk_init_data){
139 		.name = "ao_32k",
140 		.ops = &clk_regmap_gate_ops,
141 		.parent_hws = (const struct clk_hw *[]) { &gxbb_ao_32k_sel.hw },
142 		.num_parents = 1,
143 		.flags = CLK_SET_RATE_PARENT,
144 	},
145 };
146 
147 static struct clk_regmap gxbb_ao_cts_rtc_oscin = {
148 	.data = &(struct clk_regmap_mux_data) {
149 		.offset = AO_RTI_PWR_CNTL_REG0,
150 		.mask = 0x7,
151 		.shift = 10,
152 		.table = (u32[]){ 1, 2, 3, 4 },
153 		.flags = CLK_MUX_ROUND_CLOSEST,
154 	},
155 	.hw.init = &(struct clk_init_data){
156 		.name = "ao_cts_rtc_oscin",
157 		.ops = &clk_regmap_mux_ops,
158 		.parent_data = (const struct clk_parent_data []) {
159 			{ .fw_name = "ext-32k-0", },
160 			{ .fw_name = "ext-32k-1", },
161 			{ .fw_name = "ext-32k-2", },
162 			{ .hw = &gxbb_ao_32k.hw },
163 		},
164 		.num_parents = 4,
165 		.flags = CLK_SET_RATE_PARENT,
166 	},
167 };
168 
169 static struct clk_regmap gxbb_ao_clk81 = {
170 	.data = &(struct clk_regmap_mux_data) {
171 		.offset = AO_RTI_PWR_CNTL_REG0,
172 		.mask = 0x1,
173 		.shift = 0,
174 		.flags = CLK_MUX_ROUND_CLOSEST,
175 	},
176 	.hw.init = &(struct clk_init_data){
177 		.name = "ao_clk81",
178 		.ops = &clk_regmap_mux_ro_ops,
179 		.parent_data = (const struct clk_parent_data []) {
180 			{ .fw_name = "mpeg-clk", },
181 			{ .hw = &gxbb_ao_cts_rtc_oscin.hw },
182 		},
183 		.num_parents = 2,
184 		.flags = CLK_SET_RATE_PARENT,
185 	},
186 };
187 
188 static struct clk_regmap gxbb_ao_cts_cec = {
189 	.data = &(struct clk_regmap_mux_data) {
190 		.offset = AO_CRT_CLK_CNTL1,
191 		.mask = 0x1,
192 		.shift = 27,
193 		.flags = CLK_MUX_ROUND_CLOSEST,
194 	},
195 	.hw.init = &(struct clk_init_data){
196 		.name = "ao_cts_cec",
197 		.ops = &clk_regmap_mux_ops,
198 		/*
199 		 * FIXME: The 'fixme' parent obviously does not exist.
200 		 *
201 		 * ATM, CCF won't call get_parent() if num_parents is 1. It
202 		 * does not allow NULL as a parent name either.
203 		 *
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
207 		 *
208 		 * Until CCF gets fixed, adding this fake parent that won't
209 		 * ever be registered should work around the problem
210 		 */
211 		.parent_data = (const struct clk_parent_data []) {
212 			{ .name = "fixme", .index = -1, },
213 			{ .hw = &gxbb_ao_cts_rtc_oscin.hw },
214 		},
215 		.num_parents = 2,
216 		.flags = CLK_SET_RATE_PARENT,
217 	},
218 };
219 
220 static const unsigned int gxbb_ao_reset[] = {
221 	[RESET_AO_REMOTE] = 16,
222 	[RESET_AO_I2C_MASTER] = 18,
223 	[RESET_AO_I2C_SLAVE] = 19,
224 	[RESET_AO_UART1] = 17,
225 	[RESET_AO_UART2] = 22,
226 	[RESET_AO_IR_BLASTER] = 23,
227 };
228 
229 static struct clk_hw *gxbb_ao_hw_clks[] = {
230 		[CLKID_AO_REMOTE]	= &gxbb_ao_remote.hw,
231 		[CLKID_AO_I2C_MASTER]	= &gxbb_ao_i2c_master.hw,
232 		[CLKID_AO_I2C_SLAVE]	= &gxbb_ao_i2c_slave.hw,
233 		[CLKID_AO_UART1]	= &gxbb_ao_uart1.hw,
234 		[CLKID_AO_UART2]	= &gxbb_ao_uart2.hw,
235 		[CLKID_AO_IR_BLASTER]	= &gxbb_ao_ir_blaster.hw,
236 		[CLKID_AO_CEC_32K]	= &gxbb_ao_cts_cec.hw,
237 		[CLKID_AO_CTS_OSCIN]	= &gxbb_ao_cts_oscin.hw,
238 		[CLKID_AO_32K_PRE]	= &gxbb_ao_32k_pre.hw,
239 		[CLKID_AO_32K_DIV]	= &gxbb_ao_32k_div.hw,
240 		[CLKID_AO_32K_SEL]	= &gxbb_ao_32k_sel.hw,
241 		[CLKID_AO_32K]		= &gxbb_ao_32k.hw,
242 		[CLKID_AO_CTS_RTC_OSCIN] = &gxbb_ao_cts_rtc_oscin.hw,
243 		[CLKID_AO_CLK81]	= &gxbb_ao_clk81.hw,
244 };
245 
246 static const struct meson_aoclk_data gxbb_ao_clkc_data = {
247 	.reset_reg	= AO_RTI_GEN_CNTL_REG0,
248 	.num_reset	= ARRAY_SIZE(gxbb_ao_reset),
249 	.reset		= gxbb_ao_reset,
250 	.clkc_data	= {
251 		.hw_clks = {
252 			.hws	= gxbb_ao_hw_clks,
253 			.num	= ARRAY_SIZE(gxbb_ao_hw_clks),
254 		},
255 	},
256 };
257 
258 static const struct of_device_id gxbb_ao_clkc_match_table[] = {
259 	{
260 		.compatible	= "amlogic,meson-gx-aoclkc",
261 		.data		= &gxbb_ao_clkc_data.clkc_data,
262 	},
263 	{ }
264 };
265 MODULE_DEVICE_TABLE(of, gxbb_ao_clkc_match_table);
266 
267 static struct platform_driver gxbb_ao_clkc_driver = {
268 	.probe		= meson_aoclkc_probe,
269 	.driver		= {
270 		.name	= "gxbb-aoclkc",
271 		.of_match_table = gxbb_ao_clkc_match_table,
272 	},
273 };
274 module_platform_driver(gxbb_ao_clkc_driver);
275 
276 MODULE_DESCRIPTION("Amlogic GXBB Always-ON Clock Controller driver");
277 MODULE_LICENSE("GPL");
278 MODULE_IMPORT_NS("CLK_MESON");
279