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 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