1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Amlogic Meson-AXG Clock Controller Driver
4 *
5 * Copyright (c) 2016 Baylibre SAS.
6 * Author: Michael Turquette <mturquette@baylibre.com>
7 *
8 * Copyright (c) 2018 Amlogic, inc.
9 * Author: Qiufang Dai <qiufang.dai@amlogic.com>
10 */
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 "meson-aoclk.h"
16#include "axg-aoclk.h"
17
18#include "clk-regmap.h"
19#include "clk-dualdiv.h"
20
21/*
22 * AO Configuration Clock registers offsets
23 * Register offsets from the data sheet must be multiplied by 4.
24 */
25#define AO_RTI_PWR_CNTL_REG1	0x0C
26#define AO_RTI_PWR_CNTL_REG0	0x10
27#define AO_RTI_GEN_CNTL_REG0	0x40
28#define AO_OSCIN_CNTL		0x58
29#define AO_CRT_CLK_CNTL1	0x68
30#define AO_SAR_CLK		0x90
31#define AO_RTC_ALT_CLK_CNTL0	0x94
32#define AO_RTC_ALT_CLK_CNTL1	0x98
33
34#define AXG_AO_GATE(_name, _bit)					\
35static struct clk_regmap axg_aoclk_##_name = {				\
36	.data = &(struct clk_regmap_gate_data) {			\
37		.offset = (AO_RTI_GEN_CNTL_REG0),			\
38		.bit_idx = (_bit),					\
39	},								\
40	.hw.init = &(struct clk_init_data) {				\
41		.name =  "axg_ao_" #_name,				\
42		.ops = &clk_regmap_gate_ops,				\
43		.parent_data = &(const struct clk_parent_data) {	\
44			.fw_name = "mpeg-clk",				\
45		},							\
46		.num_parents = 1,					\
47		.flags = CLK_IGNORE_UNUSED,				\
48	},								\
49}
50
51AXG_AO_GATE(remote, 0);
52AXG_AO_GATE(i2c_master, 1);
53AXG_AO_GATE(i2c_slave, 2);
54AXG_AO_GATE(uart1, 3);
55AXG_AO_GATE(uart2, 5);
56AXG_AO_GATE(ir_blaster, 6);
57AXG_AO_GATE(saradc, 7);
58
59static struct clk_regmap axg_aoclk_cts_oscin = {
60	.data = &(struct clk_regmap_gate_data){
61		.offset = AO_RTI_PWR_CNTL_REG0,
62		.bit_idx = 14,
63	},
64	.hw.init = &(struct clk_init_data){
65		.name = "cts_oscin",
66		.ops = &clk_regmap_gate_ro_ops,
67		.parent_data = &(const struct clk_parent_data) {
68			.fw_name = "xtal",
69		},
70		.num_parents = 1,
71	},
72};
73
74static struct clk_regmap axg_aoclk_32k_pre = {
75	.data = &(struct clk_regmap_gate_data){
76		.offset = AO_RTC_ALT_CLK_CNTL0,
77		.bit_idx = 31,
78	},
79	.hw.init = &(struct clk_init_data){
80		.name = "axg_ao_32k_pre",
81		.ops = &clk_regmap_gate_ops,
82		.parent_hws = (const struct clk_hw *[]) {
83			&axg_aoclk_cts_oscin.hw
84		},
85		.num_parents = 1,
86	},
87};
88
89static const struct meson_clk_dualdiv_param axg_32k_div_table[] = {
90	{
91		.dual	= 1,
92		.n1	= 733,
93		.m1	= 8,
94		.n2	= 732,
95		.m2	= 11,
96	}, {}
97};
98
99static struct clk_regmap axg_aoclk_32k_div = {
100	.data = &(struct meson_clk_dualdiv_data){
101		.n1 = {
102			.reg_off = AO_RTC_ALT_CLK_CNTL0,
103			.shift   = 0,
104			.width   = 12,
105		},
106		.n2 = {
107			.reg_off = AO_RTC_ALT_CLK_CNTL0,
108			.shift   = 12,
109			.width   = 12,
110		},
111		.m1 = {
112			.reg_off = AO_RTC_ALT_CLK_CNTL1,
113			.shift   = 0,
114			.width   = 12,
115		},
116		.m2 = {
117			.reg_off = AO_RTC_ALT_CLK_CNTL1,
118			.shift   = 12,
119			.width   = 12,
120		},
121		.dual = {
122			.reg_off = AO_RTC_ALT_CLK_CNTL0,
123			.shift   = 28,
124			.width   = 1,
125		},
126		.table = axg_32k_div_table,
127	},
128	.hw.init = &(struct clk_init_data){
129		.name = "axg_ao_32k_div",
130		.ops = &meson_clk_dualdiv_ops,
131		.parent_hws = (const struct clk_hw *[]) {
132			&axg_aoclk_32k_pre.hw
133		},
134		.num_parents = 1,
135	},
136};
137
138static struct clk_regmap axg_aoclk_32k_sel = {
139	.data = &(struct clk_regmap_mux_data) {
140		.offset = AO_RTC_ALT_CLK_CNTL1,
141		.mask = 0x1,
142		.shift = 24,
143		.flags = CLK_MUX_ROUND_CLOSEST,
144	},
145	.hw.init = &(struct clk_init_data){
146		.name = "axg_ao_32k_sel",
147		.ops = &clk_regmap_mux_ops,
148		.parent_hws = (const struct clk_hw *[]) {
149			&axg_aoclk_32k_div.hw,
150			&axg_aoclk_32k_pre.hw,
151		},
152		.num_parents = 2,
153		.flags = CLK_SET_RATE_PARENT,
154	},
155};
156
157static struct clk_regmap axg_aoclk_32k = {
158	.data = &(struct clk_regmap_gate_data){
159		.offset = AO_RTC_ALT_CLK_CNTL0,
160		.bit_idx = 30,
161	},
162	.hw.init = &(struct clk_init_data){
163		.name = "axg_ao_32k",
164		.ops = &clk_regmap_gate_ops,
165		.parent_hws = (const struct clk_hw *[]) {
166			&axg_aoclk_32k_sel.hw
167		},
168		.num_parents = 1,
169		.flags = CLK_SET_RATE_PARENT,
170	},
171};
172
173static struct clk_regmap axg_aoclk_cts_rtc_oscin = {
174	.data = &(struct clk_regmap_mux_data) {
175		.offset = AO_RTI_PWR_CNTL_REG0,
176		.mask = 0x1,
177		.shift = 10,
178		.flags = CLK_MUX_ROUND_CLOSEST,
179	},
180	.hw.init = &(struct clk_init_data){
181		.name = "axg_ao_cts_rtc_oscin",
182		.ops = &clk_regmap_mux_ops,
183		.parent_data = (const struct clk_parent_data []) {
184			{ .hw = &axg_aoclk_32k.hw },
185			{ .fw_name = "ext_32k-0", },
186		},
187		.num_parents = 2,
188		.flags = CLK_SET_RATE_PARENT,
189	},
190};
191
192static struct clk_regmap axg_aoclk_clk81 = {
193	.data = &(struct clk_regmap_mux_data) {
194		.offset = AO_RTI_PWR_CNTL_REG0,
195		.mask = 0x1,
196		.shift = 8,
197		.flags = CLK_MUX_ROUND_CLOSEST,
198	},
199	.hw.init = &(struct clk_init_data){
200		.name = "axg_ao_clk81",
201		.ops = &clk_regmap_mux_ro_ops,
202		.parent_data = (const struct clk_parent_data []) {
203			{ .fw_name = "mpeg-clk", },
204			{ .hw = &axg_aoclk_cts_rtc_oscin.hw },
205		},
206		.num_parents = 2,
207		.flags = CLK_SET_RATE_PARENT,
208	},
209};
210
211static struct clk_regmap axg_aoclk_saradc_mux = {
212	.data = &(struct clk_regmap_mux_data) {
213		.offset = AO_SAR_CLK,
214		.mask = 0x3,
215		.shift = 9,
216	},
217	.hw.init = &(struct clk_init_data){
218		.name = "axg_ao_saradc_mux",
219		.ops = &clk_regmap_mux_ops,
220		.parent_data = (const struct clk_parent_data []) {
221			{ .fw_name = "xtal", },
222			{ .hw = &axg_aoclk_clk81.hw },
223		},
224		.num_parents = 2,
225	},
226};
227
228static struct clk_regmap axg_aoclk_saradc_div = {
229	.data = &(struct clk_regmap_div_data) {
230		.offset = AO_SAR_CLK,
231		.shift = 0,
232		.width = 8,
233	},
234	.hw.init = &(struct clk_init_data){
235		.name = "axg_ao_saradc_div",
236		.ops = &clk_regmap_divider_ops,
237		.parent_hws = (const struct clk_hw *[]) {
238			&axg_aoclk_saradc_mux.hw
239		},
240		.num_parents = 1,
241		.flags = CLK_SET_RATE_PARENT,
242	},
243};
244
245static struct clk_regmap axg_aoclk_saradc_gate = {
246	.data = &(struct clk_regmap_gate_data) {
247		.offset = AO_SAR_CLK,
248		.bit_idx = 8,
249	},
250	.hw.init = &(struct clk_init_data){
251		.name = "axg_ao_saradc_gate",
252		.ops = &clk_regmap_gate_ops,
253		.parent_hws = (const struct clk_hw *[]) {
254			&axg_aoclk_saradc_div.hw
255		},
256		.num_parents = 1,
257		.flags = CLK_SET_RATE_PARENT,
258	},
259};
260
261static const unsigned int axg_aoclk_reset[] = {
262	[RESET_AO_REMOTE]	= 16,
263	[RESET_AO_I2C_MASTER]	= 18,
264	[RESET_AO_I2C_SLAVE]	= 19,
265	[RESET_AO_UART1]	= 17,
266	[RESET_AO_UART2]	= 22,
267	[RESET_AO_IR_BLASTER]	= 23,
268};
269
270static struct clk_regmap *axg_aoclk_regmap[] = {
271	&axg_aoclk_remote,
272	&axg_aoclk_i2c_master,
273	&axg_aoclk_i2c_slave,
274	&axg_aoclk_uart1,
275	&axg_aoclk_uart2,
276	&axg_aoclk_ir_blaster,
277	&axg_aoclk_saradc,
278	&axg_aoclk_cts_oscin,
279	&axg_aoclk_32k_pre,
280	&axg_aoclk_32k_div,
281	&axg_aoclk_32k_sel,
282	&axg_aoclk_32k,
283	&axg_aoclk_cts_rtc_oscin,
284	&axg_aoclk_clk81,
285	&axg_aoclk_saradc_mux,
286	&axg_aoclk_saradc_div,
287	&axg_aoclk_saradc_gate,
288};
289
290static const struct clk_hw_onecell_data axg_aoclk_onecell_data = {
291	.hws = {
292		[CLKID_AO_REMOTE]	= &axg_aoclk_remote.hw,
293		[CLKID_AO_I2C_MASTER]	= &axg_aoclk_i2c_master.hw,
294		[CLKID_AO_I2C_SLAVE]	= &axg_aoclk_i2c_slave.hw,
295		[CLKID_AO_UART1]	= &axg_aoclk_uart1.hw,
296		[CLKID_AO_UART2]	= &axg_aoclk_uart2.hw,
297		[CLKID_AO_IR_BLASTER]	= &axg_aoclk_ir_blaster.hw,
298		[CLKID_AO_SAR_ADC]	= &axg_aoclk_saradc.hw,
299		[CLKID_AO_CLK81]	= &axg_aoclk_clk81.hw,
300		[CLKID_AO_SAR_ADC_SEL]	= &axg_aoclk_saradc_mux.hw,
301		[CLKID_AO_SAR_ADC_DIV]	= &axg_aoclk_saradc_div.hw,
302		[CLKID_AO_SAR_ADC_CLK]	= &axg_aoclk_saradc_gate.hw,
303		[CLKID_AO_CTS_OSCIN]	= &axg_aoclk_cts_oscin.hw,
304		[CLKID_AO_32K_PRE]	= &axg_aoclk_32k_pre.hw,
305		[CLKID_AO_32K_DIV]	= &axg_aoclk_32k_div.hw,
306		[CLKID_AO_32K_SEL]	= &axg_aoclk_32k_sel.hw,
307		[CLKID_AO_32K]		= &axg_aoclk_32k.hw,
308		[CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw,
309	},
310	.num = NR_CLKS,
311};
312
313static const struct meson_aoclk_data axg_aoclkc_data = {
314	.reset_reg	= AO_RTI_GEN_CNTL_REG0,
315	.num_reset	= ARRAY_SIZE(axg_aoclk_reset),
316	.reset		= axg_aoclk_reset,
317	.num_clks	= ARRAY_SIZE(axg_aoclk_regmap),
318	.clks		= axg_aoclk_regmap,
319	.hw_data	= &axg_aoclk_onecell_data,
320};
321
322static const struct of_device_id axg_aoclkc_match_table[] = {
323	{
324		.compatible	= "amlogic,meson-axg-aoclkc",
325		.data		= &axg_aoclkc_data,
326	},
327	{ }
328};
329
330static struct platform_driver axg_aoclkc_driver = {
331	.probe		= meson_aoclkc_probe,
332	.driver		= {
333		.name	= "axg-aoclkc",
334		.of_match_table = axg_aoclkc_match_table,
335	},
336};
337
338builtin_platform_driver(axg_aoclkc_driver);
339