1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2025 Chen-Yu Tsai <wens@csie.org>
4 *
5 * Based on the A523 CCU driver:
6 * Copyright (C) 2023-2024 Arm Ltd.
7 */
8
9 #include <linux/clk-provider.h>
10 #include <linux/io.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13
14 #include <dt-bindings/clock/sun55i-a523-mcu-ccu.h>
15 #include <dt-bindings/reset/sun55i-a523-mcu-ccu.h>
16
17 #include "ccu_common.h"
18 #include "ccu_reset.h"
19
20 #include "ccu_div.h"
21 #include "ccu_gate.h"
22 #include "ccu_mp.h"
23 #include "ccu_mult.h"
24 #include "ccu_nm.h"
25
26 static const struct clk_parent_data osc24M[] = {
27 { .fw_name = "hosc" }
28 };
29
30 static const struct clk_parent_data ahb[] = {
31 { .fw_name = "r-ahb" }
32 };
33
34 static const struct clk_parent_data apb[] = {
35 { .fw_name = "r-apb0" }
36 };
37
38 #define SUN55I_A523_PLL_AUDIO1_REG 0x00c
39 static struct ccu_sdm_setting pll_audio1_sdm_table[] = {
40 { .rate = 2167603200, .pattern = 0xa000a234, .m = 1, .n = 90 }, /* div2->22.5792 */
41 { .rate = 2359296000, .pattern = 0xa0009ba6, .m = 1, .n = 98 }, /* div2->24.576 */
42 { .rate = 1806336000, .pattern = 0xa000872b, .m = 1, .n = 75 }, /* div5->22.576 */
43 };
44
45 static struct ccu_nm pll_audio1_clk = {
46 .enable = BIT(27),
47 .lock = BIT(28),
48 .n = _SUNXI_CCU_MULT_MIN(8, 8, 11),
49 .m = _SUNXI_CCU_DIV(1, 1),
50 .sdm = _SUNXI_CCU_SDM(pll_audio1_sdm_table, BIT(24),
51 0x010, BIT(31)),
52 .min_rate = 180000000U,
53 .max_rate = 3500000000U,
54 .common = {
55 .reg = 0x00c,
56 .features = CCU_FEATURE_SIGMA_DELTA_MOD,
57 .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-audio1",
58 osc24M, &ccu_nm_ops,
59 CLK_SET_RATE_GATE),
60 },
61 };
62
63 /*
64 * /2 and /5 dividers are actually programmable, but we just use the
65 * values from the BSP, since the audio PLL only needs to provide a
66 * couple clock rates. This also matches the names given in the manual.
67 */
68 static const struct clk_hw *pll_audio1_div_parents[] = { &pll_audio1_clk.common.hw };
69 static CLK_FIXED_FACTOR_HWS(pll_audio1_div2_clk, "pll-audio1-div2",
70 pll_audio1_div_parents, 2, 1,
71 CLK_SET_RATE_PARENT);
72 static CLK_FIXED_FACTOR_HWS(pll_audio1_div5_clk, "pll-audio1-div5",
73 pll_audio1_div_parents, 5, 1,
74 CLK_SET_RATE_PARENT);
75
76 static SUNXI_CCU_M_WITH_GATE(audio_out_clk, "audio-out",
77 "pll-audio1-div2", 0x01c,
78 0, 5, BIT(31), CLK_SET_RATE_PARENT);
79
80 static const struct clk_parent_data dsp_parents[] = {
81 { .fw_name = "hosc" },
82 { .fw_name = "losc" },
83 { .fw_name = "iosc" },
84 /*
85 * The order of the following two parent is from the BSP code. It is
86 * the opposite in the manual. Testing with the DSP is required to
87 * figure out the real order.
88 */
89 { .hw = &pll_audio1_div5_clk.hw },
90 { .hw = &pll_audio1_div2_clk.hw },
91 { .fw_name = "dsp" },
92 };
93 static SUNXI_CCU_M_DATA_WITH_MUX_GATE(dsp_clk, "mcu-dsp", dsp_parents, 0x0020,
94 0, 5, /* M */
95 24, 3, /* mux */
96 BIT(31), /* gate */
97 0);
98
99 static const struct clk_parent_data i2s_parents[] = {
100 { .fw_name = "pll-audio0-4x" },
101 { .hw = &pll_audio1_div2_clk.hw },
102 { .hw = &pll_audio1_div5_clk.hw },
103 };
104
105 static SUNXI_CCU_DUALDIV_MUX_GATE(i2s0_clk, "i2s0", i2s_parents, 0x02c,
106 0, 5, /* M */
107 5, 5, /* P */
108 24, 3, /* mux */
109 BIT(31), /* gate */
110 CLK_SET_RATE_PARENT);
111 static SUNXI_CCU_DUALDIV_MUX_GATE(i2s1_clk, "i2s1", i2s_parents, 0x030,
112 0, 5, /* M */
113 5, 5, /* P */
114 24, 3, /* mux */
115 BIT(31), /* gate */
116 CLK_SET_RATE_PARENT);
117 static SUNXI_CCU_DUALDIV_MUX_GATE(i2s2_clk, "i2s2", i2s_parents, 0x034,
118 0, 5, /* M */
119 5, 5, /* P */
120 24, 3, /* mux */
121 BIT(31), /* gate */
122 CLK_SET_RATE_PARENT);
123 static SUNXI_CCU_DUALDIV_MUX_GATE(i2s3_clk, "i2s3", i2s_parents, 0x038,
124 0, 5, /* M */
125 5, 5, /* P */
126 24, 3, /* mux */
127 BIT(31), /* gate */
128 CLK_SET_RATE_PARENT);
129
130 static const struct clk_parent_data i2s3_asrc_parents[] = {
131 { .fw_name = "pll-periph0-300m" },
132 { .hw = &pll_audio1_div2_clk.hw },
133 { .hw = &pll_audio1_div5_clk.hw },
134 };
135 static SUNXI_CCU_DUALDIV_MUX_GATE(i2s3_asrc_clk, "i2s3-asrc",
136 i2s3_asrc_parents, 0x03c,
137 0, 5, /* M */
138 5, 5, /* P */
139 24, 3, /* mux */
140 BIT(31), /* gate */
141 CLK_SET_RATE_PARENT);
142
143 static SUNXI_CCU_GATE_DATA(bus_i2s0_clk, "bus-i2s0", apb, 0x040, BIT(0), 0);
144 static SUNXI_CCU_GATE_DATA(bus_i2s1_clk, "bus-i2s1", apb, 0x040, BIT(1), 0);
145 static SUNXI_CCU_GATE_DATA(bus_i2s2_clk, "bus-i2s2", apb, 0x040, BIT(2), 0);
146 static SUNXI_CCU_GATE_DATA(bus_i2s3_clk, "bus-i2s3", apb, 0x040, BIT(3), 0);
147
148 static const struct clk_parent_data audio_parents[] = {
149 { .fw_name = "pll-audio0-4x" },
150 { .hw = &pll_audio1_div2_clk.hw },
151 { .hw = &pll_audio1_div5_clk.hw },
152 };
153 static SUNXI_CCU_DUALDIV_MUX_GATE(spdif_tx_clk, "spdif-tx",
154 audio_parents, 0x044,
155 0, 5, /* M */
156 5, 5, /* P */
157 24, 3, /* mux */
158 BIT(31), /* gate */
159 CLK_SET_RATE_PARENT);
160 static SUNXI_CCU_DUALDIV_MUX_GATE(spdif_rx_clk, "spdif-rx",
161 i2s3_asrc_parents, 0x048,
162 0, 5, /* M */
163 5, 5, /* P */
164 24, 3, /* mux */
165 BIT(31), /* gate */
166 CLK_SET_RATE_PARENT);
167
168 static SUNXI_CCU_GATE_DATA(bus_spdif_clk, "bus-spdif", apb, 0x04c, BIT(0), 0);
169
170 static SUNXI_CCU_DUALDIV_MUX_GATE(dmic_clk, "dmic", audio_parents, 0x050,
171 0, 5, /* M */
172 5, 5, /* P */
173 24, 3, /* mux */
174 BIT(31), /* gate */
175 CLK_SET_RATE_PARENT);
176
177 static SUNXI_CCU_GATE_DATA(bus_dmic_clk, "bus-dmic", apb, 0x054, BIT(0), 0);
178
179 static SUNXI_CCU_DUALDIV_MUX_GATE(audio_dac_clk, "audio-dac",
180 audio_parents, 0x058,
181 0, 5, /* M */
182 5, 5, /* P */
183 24, 3, /* mux */
184 BIT(31), /* gate */
185 CLK_SET_RATE_PARENT);
186 static SUNXI_CCU_DUALDIV_MUX_GATE(audio_adc_clk, "audio-adc",
187 audio_parents, 0x05c,
188 0, 5, /* M */
189 5, 5, /* P */
190 24, 3, /* mux */
191 BIT(31), /* gate */
192 CLK_SET_RATE_PARENT);
193
194 static SUNXI_CCU_GATE_DATA(bus_audio_codec_clk, "bus-audio-codec",
195 apb, 0x060, BIT(0), 0);
196
197 static SUNXI_CCU_GATE_DATA(bus_dsp_msgbox_clk, "bus-dsp-msgbox",
198 ahb, 0x068, BIT(0), 0);
199 static SUNXI_CCU_GATE_DATA(bus_dsp_cfg_clk, "bus-dsp-cfg",
200 apb, 0x06c, BIT(0), 0);
201
202 static SUNXI_CCU_GATE_DATA(bus_npu_hclk, "bus-npu-hclk", ahb, 0x070, BIT(1), 0);
203 static SUNXI_CCU_GATE_DATA(bus_npu_aclk, "bus-npu-aclk", ahb, 0x070, BIT(2), 0);
204
205 static const struct clk_parent_data timer_parents[] = {
206 { .fw_name = "hosc" },
207 { .fw_name = "losc" },
208 { .fw_name = "iosc" },
209 { .fw_name = "r-ahb" }
210 };
211 static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer0_clk, "mcu-timer0", timer_parents,
212 0x074,
213 1, 3, /* P */
214 4, 2, /* mux */
215 BIT(0), /* gate */
216 0);
217 static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer1_clk, "mcu-timer1", timer_parents,
218 0x078,
219 1, 3, /* P */
220 4, 2, /* mux */
221 BIT(0), /* gate */
222 0);
223 static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer2_clk, "mcu-timer2", timer_parents,
224 0x07c,
225 1, 3, /* P */
226 4, 2, /* mux */
227 BIT(0), /* gate */
228 0);
229 static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer3_clk, "mcu-timer3", timer_parents,
230 0x080,
231 1, 3, /* P */
232 4, 2, /* mux */
233 BIT(0), /* gate */
234 0);
235 static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer4_clk, "mcu-timer4", timer_parents,
236 0x084,
237 1, 3, /* P */
238 4, 2, /* mux */
239 BIT(0), /* gate */
240 0);
241 static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer5_clk, "mcu-timer5", timer_parents,
242 0x088,
243 1, 3, /* P */
244 4, 2, /* mux */
245 BIT(0), /* gate */
246 0);
247 static SUNXI_CCU_GATE_DATA(bus_mcu_timer_clk, "bus-mcu-timer", ahb, 0x08c, BIT(0), 0);
248 static SUNXI_CCU_GATE_DATA(bus_mcu_dma_clk, "bus-mcu-dma", ahb, 0x104, BIT(0), 0);
249 /* tzma* only found in BSP code. */
250 static SUNXI_CCU_GATE_DATA(tzma0_clk, "tzma0", ahb, 0x108, BIT(0), 0);
251 static SUNXI_CCU_GATE_DATA(tzma1_clk, "tzma1", ahb, 0x10c, BIT(0), 0);
252 /* parent is a guess as this block is not shown in the system bus tree diagram */
253 static SUNXI_CCU_GATE_DATA(bus_pubsram_clk, "bus-pubsram", ahb, 0x114, BIT(0), 0);
254
255 /*
256 * user manual has "mbus" clock as parent of both clocks below,
257 * but this makes more sense, since BSP MCU DMA controller has
258 * reference to both of them, likely needing both enabled.
259 */
260 static SUNXI_CCU_GATE_FW(mbus_mcu_clk, "mbus-mcu", "mbus", 0x11c, BIT(1), 0);
261 static SUNXI_CCU_GATE_HW(mbus_mcu_dma_clk, "mbus-mcu-dma",
262 &mbus_mcu_clk.common.hw, 0x11c, BIT(0), 0);
263
264 static const struct clk_parent_data riscv_pwm_parents[] = {
265 { .fw_name = "hosc" },
266 { .fw_name = "losc" },
267 { .fw_name = "iosc" },
268 };
269
270 static SUNXI_CCU_MUX_DATA_WITH_GATE(riscv_clk, "riscv",
271 riscv_pwm_parents, 0x120,
272 27, 3, BIT(31), 0);
273 /* Parents are guesses as these two blocks are not shown in the system bus tree diagram */
274 static SUNXI_CCU_GATE_DATA(bus_riscv_cfg_clk, "bus-riscv-cfg", ahb,
275 0x124, BIT(0), 0);
276 static SUNXI_CCU_GATE_DATA(bus_riscv_msgbox_clk, "bus-riscv-msgbox", ahb,
277 0x128, BIT(0), 0);
278
279 static SUNXI_CCU_MUX_DATA_WITH_GATE(mcu_pwm0_clk, "mcu-pwm0",
280 riscv_pwm_parents, 0x130,
281 24, 3, BIT(31), 0);
282 static SUNXI_CCU_GATE_DATA(bus_mcu_pwm0_clk, "bus-mcu-pwm0", apb,
283 0x134, BIT(0), 0);
284
285 /*
286 * Contains all clocks that are controlled by a hardware register. They
287 * have a (sunxi) .common member, which needs to be initialised by the common
288 * sunxi CCU code, to be filled with the MMIO base address and the shared lock.
289 */
290 static struct ccu_common *sun55i_a523_mcu_ccu_clks[] = {
291 &pll_audio1_clk.common,
292 &audio_out_clk.common,
293 &dsp_clk.common,
294 &i2s0_clk.common,
295 &i2s1_clk.common,
296 &i2s2_clk.common,
297 &i2s3_clk.common,
298 &i2s3_asrc_clk.common,
299 &bus_i2s0_clk.common,
300 &bus_i2s1_clk.common,
301 &bus_i2s2_clk.common,
302 &bus_i2s3_clk.common,
303 &spdif_tx_clk.common,
304 &spdif_rx_clk.common,
305 &bus_spdif_clk.common,
306 &dmic_clk.common,
307 &bus_dmic_clk.common,
308 &audio_dac_clk.common,
309 &audio_adc_clk.common,
310 &bus_audio_codec_clk.common,
311 &bus_dsp_msgbox_clk.common,
312 &bus_dsp_cfg_clk.common,
313 &bus_npu_aclk.common,
314 &bus_npu_hclk.common,
315 &mcu_timer0_clk.common,
316 &mcu_timer1_clk.common,
317 &mcu_timer2_clk.common,
318 &mcu_timer3_clk.common,
319 &mcu_timer4_clk.common,
320 &mcu_timer5_clk.common,
321 &bus_mcu_timer_clk.common,
322 &bus_mcu_dma_clk.common,
323 &tzma0_clk.common,
324 &tzma1_clk.common,
325 &bus_pubsram_clk.common,
326 &mbus_mcu_dma_clk.common,
327 &mbus_mcu_clk.common,
328 &riscv_clk.common,
329 &bus_riscv_cfg_clk.common,
330 &bus_riscv_msgbox_clk.common,
331 &mcu_pwm0_clk.common,
332 &bus_mcu_pwm0_clk.common,
333 };
334
335 static struct clk_hw_onecell_data sun55i_a523_mcu_hw_clks = {
336 .hws = {
337 [CLK_MCU_PLL_AUDIO1] = &pll_audio1_clk.common.hw,
338 [CLK_MCU_PLL_AUDIO1_DIV2] = &pll_audio1_div2_clk.hw,
339 [CLK_MCU_PLL_AUDIO1_DIV5] = &pll_audio1_div5_clk.hw,
340 [CLK_MCU_AUDIO_OUT] = &audio_out_clk.common.hw,
341 [CLK_MCU_DSP] = &dsp_clk.common.hw,
342 [CLK_MCU_I2S0] = &i2s0_clk.common.hw,
343 [CLK_MCU_I2S1] = &i2s1_clk.common.hw,
344 [CLK_MCU_I2S2] = &i2s2_clk.common.hw,
345 [CLK_MCU_I2S3] = &i2s3_clk.common.hw,
346 [CLK_MCU_I2S3_ASRC] = &i2s3_asrc_clk.common.hw,
347 [CLK_BUS_MCU_I2S0] = &bus_i2s0_clk.common.hw,
348 [CLK_BUS_MCU_I2S1] = &bus_i2s1_clk.common.hw,
349 [CLK_BUS_MCU_I2S2] = &bus_i2s2_clk.common.hw,
350 [CLK_BUS_MCU_I2S3] = &bus_i2s3_clk.common.hw,
351 [CLK_MCU_SPDIF_TX] = &spdif_tx_clk.common.hw,
352 [CLK_MCU_SPDIF_RX] = &spdif_rx_clk.common.hw,
353 [CLK_BUS_MCU_SPDIF] = &bus_spdif_clk.common.hw,
354 [CLK_MCU_DMIC] = &dmic_clk.common.hw,
355 [CLK_BUS_MCU_DMIC] = &bus_dmic_clk.common.hw,
356 [CLK_MCU_AUDIO_CODEC_DAC] = &audio_dac_clk.common.hw,
357 [CLK_MCU_AUDIO_CODEC_ADC] = &audio_adc_clk.common.hw,
358 [CLK_BUS_MCU_AUDIO_CODEC] = &bus_audio_codec_clk.common.hw,
359 [CLK_BUS_MCU_DSP_MSGBOX] = &bus_dsp_msgbox_clk.common.hw,
360 [CLK_BUS_MCU_DSP_CFG] = &bus_dsp_cfg_clk.common.hw,
361 [CLK_BUS_MCU_NPU_HCLK] = &bus_npu_hclk.common.hw,
362 [CLK_BUS_MCU_NPU_ACLK] = &bus_npu_aclk.common.hw,
363 [CLK_MCU_TIMER0] = &mcu_timer0_clk.common.hw,
364 [CLK_MCU_TIMER1] = &mcu_timer1_clk.common.hw,
365 [CLK_MCU_TIMER2] = &mcu_timer2_clk.common.hw,
366 [CLK_MCU_TIMER3] = &mcu_timer3_clk.common.hw,
367 [CLK_MCU_TIMER4] = &mcu_timer4_clk.common.hw,
368 [CLK_MCU_TIMER5] = &mcu_timer5_clk.common.hw,
369 [CLK_BUS_MCU_TIMER] = &bus_mcu_timer_clk.common.hw,
370 [CLK_BUS_MCU_DMA] = &bus_mcu_dma_clk.common.hw,
371 [CLK_MCU_TZMA0] = &tzma0_clk.common.hw,
372 [CLK_MCU_TZMA1] = &tzma1_clk.common.hw,
373 [CLK_BUS_MCU_PUBSRAM] = &bus_pubsram_clk.common.hw,
374 [CLK_MCU_MBUS_DMA] = &mbus_mcu_dma_clk.common.hw,
375 [CLK_MCU_MBUS] = &mbus_mcu_clk.common.hw,
376 [CLK_MCU_RISCV] = &riscv_clk.common.hw,
377 [CLK_BUS_MCU_RISCV_CFG] = &bus_riscv_cfg_clk.common.hw,
378 [CLK_BUS_MCU_RISCV_MSGBOX] = &bus_riscv_msgbox_clk.common.hw,
379 [CLK_MCU_PWM0] = &mcu_pwm0_clk.common.hw,
380 [CLK_BUS_MCU_PWM0] = &bus_mcu_pwm0_clk.common.hw,
381 },
382 .num = CLK_BUS_MCU_PWM0 + 1,
383 };
384
385 static struct ccu_reset_map sun55i_a523_mcu_ccu_resets[] = {
386 [RST_BUS_MCU_I2S0] = { 0x0040, BIT(16) },
387 [RST_BUS_MCU_I2S1] = { 0x0040, BIT(17) },
388 [RST_BUS_MCU_I2S2] = { 0x0040, BIT(18) },
389 [RST_BUS_MCU_I2S3] = { 0x0040, BIT(19) },
390 [RST_BUS_MCU_SPDIF] = { 0x004c, BIT(16) },
391 [RST_BUS_MCU_DMIC] = { 0x0054, BIT(16) },
392 [RST_BUS_MCU_AUDIO_CODEC] = { 0x0060, BIT(16) },
393 [RST_BUS_MCU_DSP_MSGBOX] = { 0x0068, BIT(16) },
394 [RST_BUS_MCU_DSP_CFG] = { 0x006c, BIT(16) },
395 [RST_BUS_MCU_NPU] = { 0x0070, BIT(16) },
396 [RST_BUS_MCU_TIMER] = { 0x008c, BIT(16) },
397 /* dsp and dsp_debug resets only found in BSP code. */
398 [RST_BUS_MCU_DSP_DEBUG] = { 0x0100, BIT(16) },
399 [RST_BUS_MCU_DSP] = { 0x0100, BIT(17) },
400 [RST_BUS_MCU_DMA] = { 0x0104, BIT(16) },
401 [RST_BUS_MCU_PUBSRAM] = { 0x0114, BIT(16) },
402 [RST_BUS_MCU_RISCV_CFG] = { 0x0124, BIT(16) },
403 [RST_BUS_MCU_RISCV_DEBUG] = { 0x0124, BIT(17) },
404 [RST_BUS_MCU_RISCV_CORE] = { 0x0124, BIT(18) },
405 [RST_BUS_MCU_RISCV_MSGBOX] = { 0x0128, BIT(16) },
406 [RST_BUS_MCU_PWM0] = { 0x0134, BIT(16) },
407 };
408
409 static const struct sunxi_ccu_desc sun55i_a523_mcu_ccu_desc = {
410 .ccu_clks = sun55i_a523_mcu_ccu_clks,
411 .num_ccu_clks = ARRAY_SIZE(sun55i_a523_mcu_ccu_clks),
412
413 .hw_clks = &sun55i_a523_mcu_hw_clks,
414
415 .resets = sun55i_a523_mcu_ccu_resets,
416 .num_resets = ARRAY_SIZE(sun55i_a523_mcu_ccu_resets),
417 };
418
sun55i_a523_mcu_ccu_probe(struct platform_device * pdev)419 static int sun55i_a523_mcu_ccu_probe(struct platform_device *pdev)
420 {
421 void __iomem *reg;
422 u32 val;
423 int ret;
424
425 reg = devm_platform_ioremap_resource(pdev, 0);
426 if (IS_ERR(reg))
427 return PTR_ERR(reg);
428
429 val = readl(reg + SUN55I_A523_PLL_AUDIO1_REG);
430
431 /*
432 * The PLL clock code does not model all bits, for instance it does
433 * not support a separate enable and gate bit. We present the
434 * gate bit(27) as the enable bit, but then have to set the
435 * PLL Enable, LDO Enable, and Lock Enable bits on all PLLs here.
436 */
437 val |= BIT(31) | BIT(30) | BIT(29);
438
439 /* Enforce p1 = 5, p0 = 2 (the default) for PLL_AUDIO1 */
440 val &= ~(GENMASK(22, 20) | GENMASK(18, 16));
441 val |= (4 << 20) | (1 << 16);
442
443 writel(val, reg + SUN55I_A523_PLL_AUDIO1_REG);
444
445 ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun55i_a523_mcu_ccu_desc);
446 if (ret)
447 return ret;
448
449 return 0;
450 }
451
452 static const struct of_device_id sun55i_a523_mcu_ccu_ids[] = {
453 { .compatible = "allwinner,sun55i-a523-mcu-ccu" },
454 { }
455 };
456
457 static struct platform_driver sun55i_a523_mcu_ccu_driver = {
458 .probe = sun55i_a523_mcu_ccu_probe,
459 .driver = {
460 .name = "sun55i-a523-mcu-ccu",
461 .suppress_bind_attrs = true,
462 .of_match_table = sun55i_a523_mcu_ccu_ids,
463 },
464 };
465 module_platform_driver(sun55i_a523_mcu_ccu_driver);
466
467 MODULE_IMPORT_NS("SUNXI_CCU");
468 MODULE_DESCRIPTION("Support for the Allwinner A523 MCU CCU");
469 MODULE_LICENSE("GPL");
470