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

source code of linux/drivers/clk/meson/axg-aoclk.c