Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: meson: c3: add support for the C3 SoC PLL clock

Add the C3 PLL clock controller driver for the Amlogic C3 SoC family.

[jbrunet: fixed probe function name]
Co-developed-by: Chuan Liu <chuan.liu@amlogic.com>
Signed-off-by: Chuan Liu <chuan.liu@amlogic.com>
Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
Link: https://lore.kernel.org/r/20240522082727.3029656-5-xianwei.zhao@amlogic.com
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>

authored by

Xianwei Zhao and committed by
Jerome Brunet
8a9a129d d7583cde

+760
+13
drivers/clk/meson/Kconfig
··· 132 132 device, A1 SoC Family. Say Y if you want A1 Peripherals clock 133 133 controller to work. 134 134 135 + config COMMON_CLK_C3_PLL 136 + tristate "Amlogic C3 PLL clock controller" 137 + depends on ARM64 138 + default y 139 + select COMMON_CLK_MESON_REGMAP 140 + select COMMON_CLK_MESON_PLL 141 + select COMMON_CLK_MESON_CLKC_UTILS 142 + imply COMMON_CLK_SCMI 143 + help 144 + Support for the PLL clock controller on Amlogic C302X and C308L devices, 145 + AKA C3. Say Y if you want the board to work, because PLLs are the parent 146 + of most peripherals. 147 + 135 148 config COMMON_CLK_G12A 136 149 tristate "G12 and SM1 SoC clock controllers support" 137 150 depends on ARM64
+1
drivers/clk/meson/Makefile
··· 20 20 obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o 21 21 obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o 22 22 obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o 23 + obj-$(CONFIG_COMMON_CLK_C3_PLL) += c3-pll.o 23 24 obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o 24 25 obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o 25 26 obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
+746
drivers/clk/meson/c3-pll.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Amlogic C3 PLL Controller Driver 4 + * 5 + * Copyright (c) 2023 Amlogic, inc. 6 + * Author: Chuan Liu <chuan.liu@amlogic.com> 7 + */ 8 + 9 + #include <linux/clk-provider.h> 10 + #include <linux/platform_device.h> 11 + #include "clk-regmap.h" 12 + #include "clk-pll.h" 13 + #include "meson-clkc-utils.h" 14 + #include <dt-bindings/clock/amlogic,c3-pll-clkc.h> 15 + 16 + #define ANACTRL_FIXPLL_CTRL4 0x50 17 + #define ANACTRL_GP0PLL_CTRL0 0x80 18 + #define ANACTRL_GP0PLL_CTRL1 0x84 19 + #define ANACTRL_GP0PLL_CTRL2 0x88 20 + #define ANACTRL_GP0PLL_CTRL3 0x8c 21 + #define ANACTRL_GP0PLL_CTRL4 0x90 22 + #define ANACTRL_GP0PLL_CTRL5 0x94 23 + #define ANACTRL_GP0PLL_CTRL6 0x98 24 + #define ANACTRL_HIFIPLL_CTRL0 0x100 25 + #define ANACTRL_HIFIPLL_CTRL1 0x104 26 + #define ANACTRL_HIFIPLL_CTRL2 0x108 27 + #define ANACTRL_HIFIPLL_CTRL3 0x10c 28 + #define ANACTRL_HIFIPLL_CTRL4 0x110 29 + #define ANACTRL_HIFIPLL_CTRL5 0x114 30 + #define ANACTRL_HIFIPLL_CTRL6 0x118 31 + #define ANACTRL_MPLL_CTRL0 0x180 32 + #define ANACTRL_MPLL_CTRL1 0x184 33 + #define ANACTRL_MPLL_CTRL2 0x188 34 + #define ANACTRL_MPLL_CTRL3 0x18c 35 + #define ANACTRL_MPLL_CTRL4 0x190 36 + 37 + static struct clk_regmap fclk_50m_en = { 38 + .data = &(struct clk_regmap_gate_data) { 39 + .offset = ANACTRL_FIXPLL_CTRL4, 40 + .bit_idx = 0, 41 + }, 42 + .hw.init = &(struct clk_init_data) { 43 + .name = "fclk_50m_en", 44 + .ops = &clk_regmap_gate_ro_ops, 45 + .parent_data = &(const struct clk_parent_data) { 46 + .fw_name = "fix" 47 + }, 48 + .num_parents = 1, 49 + }, 50 + }; 51 + 52 + static struct clk_fixed_factor fclk_50m = { 53 + .mult = 1, 54 + .div = 40, 55 + .hw.init = &(struct clk_init_data) { 56 + .name = "fclk_50m", 57 + .ops = &clk_fixed_factor_ops, 58 + .parent_hws = (const struct clk_hw *[]) { 59 + &fclk_50m_en.hw 60 + }, 61 + .num_parents = 1, 62 + }, 63 + }; 64 + 65 + static struct clk_fixed_factor fclk_div2_div = { 66 + .mult = 1, 67 + .div = 2, 68 + .hw.init = &(struct clk_init_data) { 69 + .name = "fclk_div2_div", 70 + .ops = &clk_fixed_factor_ops, 71 + .parent_data = &(const struct clk_parent_data) { 72 + .fw_name = "fix" 73 + }, 74 + .num_parents = 1, 75 + }, 76 + }; 77 + 78 + static struct clk_regmap fclk_div2 = { 79 + .data = &(struct clk_regmap_gate_data) { 80 + .offset = ANACTRL_FIXPLL_CTRL4, 81 + .bit_idx = 24, 82 + }, 83 + .hw.init = &(struct clk_init_data) { 84 + .name = "fclk_div2", 85 + .ops = &clk_regmap_gate_ro_ops, 86 + .parent_hws = (const struct clk_hw *[]) { 87 + &fclk_div2_div.hw 88 + }, 89 + .num_parents = 1, 90 + }, 91 + }; 92 + 93 + static struct clk_fixed_factor fclk_div2p5_div = { 94 + .mult = 2, 95 + .div = 5, 96 + .hw.init = &(struct clk_init_data) { 97 + .name = "fclk_div2p5_div", 98 + .ops = &clk_fixed_factor_ops, 99 + .parent_data = &(const struct clk_parent_data) { 100 + .fw_name = "fix" 101 + }, 102 + .num_parents = 1, 103 + }, 104 + }; 105 + 106 + static struct clk_regmap fclk_div2p5 = { 107 + .data = &(struct clk_regmap_gate_data) { 108 + .offset = ANACTRL_FIXPLL_CTRL4, 109 + .bit_idx = 4, 110 + }, 111 + .hw.init = &(struct clk_init_data) { 112 + .name = "fclk_div2p5", 113 + .ops = &clk_regmap_gate_ro_ops, 114 + .parent_hws = (const struct clk_hw *[]) { 115 + &fclk_div2p5_div.hw 116 + }, 117 + .num_parents = 1, 118 + }, 119 + }; 120 + 121 + static struct clk_fixed_factor fclk_div3_div = { 122 + .mult = 1, 123 + .div = 3, 124 + .hw.init = &(struct clk_init_data) { 125 + .name = "fclk_div3_div", 126 + .ops = &clk_fixed_factor_ops, 127 + .parent_data = &(const struct clk_parent_data) { 128 + .fw_name = "fix" 129 + }, 130 + .num_parents = 1, 131 + }, 132 + }; 133 + 134 + static struct clk_regmap fclk_div3 = { 135 + .data = &(struct clk_regmap_gate_data) { 136 + .offset = ANACTRL_FIXPLL_CTRL4, 137 + .bit_idx = 20, 138 + }, 139 + .hw.init = &(struct clk_init_data) { 140 + .name = "fclk_div3", 141 + .ops = &clk_regmap_gate_ro_ops, 142 + .parent_hws = (const struct clk_hw *[]) { 143 + &fclk_div3_div.hw 144 + }, 145 + .num_parents = 1, 146 + }, 147 + }; 148 + 149 + static struct clk_fixed_factor fclk_div4_div = { 150 + .mult = 1, 151 + .div = 4, 152 + .hw.init = &(struct clk_init_data) { 153 + .name = "fclk_div4_div", 154 + .ops = &clk_fixed_factor_ops, 155 + .parent_data = &(const struct clk_parent_data) { 156 + .fw_name = "fix" 157 + }, 158 + .num_parents = 1, 159 + }, 160 + }; 161 + 162 + static struct clk_regmap fclk_div4 = { 163 + .data = &(struct clk_regmap_gate_data) { 164 + .offset = ANACTRL_FIXPLL_CTRL4, 165 + .bit_idx = 21, 166 + }, 167 + .hw.init = &(struct clk_init_data) { 168 + .name = "fclk_div4", 169 + .ops = &clk_regmap_gate_ro_ops, 170 + .parent_hws = (const struct clk_hw *[]) { 171 + &fclk_div4_div.hw 172 + }, 173 + .num_parents = 1, 174 + }, 175 + }; 176 + 177 + static struct clk_fixed_factor fclk_div5_div = { 178 + .mult = 1, 179 + .div = 5, 180 + .hw.init = &(struct clk_init_data) { 181 + .name = "fclk_div5_div", 182 + .ops = &clk_fixed_factor_ops, 183 + .parent_data = &(const struct clk_parent_data) { 184 + .fw_name = "fix" 185 + }, 186 + .num_parents = 1, 187 + }, 188 + }; 189 + 190 + static struct clk_regmap fclk_div5 = { 191 + .data = &(struct clk_regmap_gate_data) { 192 + .offset = ANACTRL_FIXPLL_CTRL4, 193 + .bit_idx = 22, 194 + }, 195 + .hw.init = &(struct clk_init_data) { 196 + .name = "fclk_div5", 197 + .ops = &clk_regmap_gate_ro_ops, 198 + .parent_hws = (const struct clk_hw *[]) { 199 + &fclk_div5_div.hw 200 + }, 201 + .num_parents = 1, 202 + }, 203 + }; 204 + 205 + static struct clk_fixed_factor fclk_div7_div = { 206 + .mult = 1, 207 + .div = 7, 208 + .hw.init = &(struct clk_init_data) { 209 + .name = "fclk_div7_div", 210 + .ops = &clk_fixed_factor_ops, 211 + .parent_data = &(const struct clk_parent_data) { 212 + .fw_name = "fix" 213 + }, 214 + .num_parents = 1, 215 + }, 216 + }; 217 + 218 + static struct clk_regmap fclk_div7 = { 219 + .data = &(struct clk_regmap_gate_data) { 220 + .offset = ANACTRL_FIXPLL_CTRL4, 221 + .bit_idx = 23, 222 + }, 223 + .hw.init = &(struct clk_init_data) { 224 + .name = "fclk_div7", 225 + .ops = &clk_regmap_gate_ro_ops, 226 + .parent_hws = (const struct clk_hw *[]) { 227 + &fclk_div7_div.hw 228 + }, 229 + .num_parents = 1, 230 + }, 231 + }; 232 + 233 + static const struct reg_sequence c3_gp0_init_regs[] = { 234 + { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x0 }, 235 + { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 }, 236 + { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 }, 237 + { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x3927200a }, 238 + { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 }, 239 + }; 240 + 241 + static const struct pll_mult_range c3_gp0_pll_mult_range = { 242 + .min = 125, 243 + .max = 250, 244 + }; 245 + 246 + static struct clk_regmap gp0_pll_dco = { 247 + .data = &(struct meson_clk_pll_data) { 248 + .en = { 249 + .reg_off = ANACTRL_GP0PLL_CTRL0, 250 + .shift = 28, 251 + .width = 1, 252 + }, 253 + .m = { 254 + .reg_off = ANACTRL_GP0PLL_CTRL0, 255 + .shift = 0, 256 + .width = 9, 257 + }, 258 + .frac = { 259 + .reg_off = ANACTRL_GP0PLL_CTRL1, 260 + .shift = 0, 261 + .width = 19, 262 + }, 263 + .n = { 264 + .reg_off = ANACTRL_GP0PLL_CTRL0, 265 + .shift = 10, 266 + .width = 5, 267 + }, 268 + .l = { 269 + .reg_off = ANACTRL_GP0PLL_CTRL0, 270 + .shift = 31, 271 + .width = 1, 272 + }, 273 + .rst = { 274 + .reg_off = ANACTRL_GP0PLL_CTRL0, 275 + .shift = 29, 276 + .width = 1, 277 + }, 278 + .range = &c3_gp0_pll_mult_range, 279 + .init_regs = c3_gp0_init_regs, 280 + .init_count = ARRAY_SIZE(c3_gp0_init_regs), 281 + }, 282 + .hw.init = &(struct clk_init_data) { 283 + .name = "gp0_pll_dco", 284 + .ops = &meson_clk_pll_ops, 285 + .parent_data = &(const struct clk_parent_data) { 286 + .fw_name = "top", 287 + }, 288 + .num_parents = 1, 289 + }, 290 + }; 291 + 292 + /* The maximum frequency divider supports is 32, not 128(2^7) */ 293 + static const struct clk_div_table c3_gp0_pll_od_table[] = { 294 + { 0, 1 }, 295 + { 1, 2 }, 296 + { 2, 4 }, 297 + { 3, 8 }, 298 + { 4, 16 }, 299 + { 5, 32 }, 300 + { /* sentinel */ } 301 + }; 302 + 303 + static struct clk_regmap gp0_pll = { 304 + .data = &(struct clk_regmap_div_data) { 305 + .offset = ANACTRL_GP0PLL_CTRL0, 306 + .shift = 16, 307 + .width = 3, 308 + .table = c3_gp0_pll_od_table, 309 + }, 310 + .hw.init = &(struct clk_init_data) { 311 + .name = "gp0_pll", 312 + .ops = &clk_regmap_divider_ops, 313 + .parent_hws = (const struct clk_hw *[]) { 314 + &gp0_pll_dco.hw 315 + }, 316 + .num_parents = 1, 317 + .flags = CLK_SET_RATE_PARENT, 318 + }, 319 + }; 320 + 321 + static const struct reg_sequence c3_hifi_init_regs[] = { 322 + { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x0 }, 323 + { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 }, 324 + { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 }, 325 + { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x3927200a }, 326 + { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 }, 327 + }; 328 + 329 + static struct clk_regmap hifi_pll_dco = { 330 + .data = &(struct meson_clk_pll_data) { 331 + .en = { 332 + .reg_off = ANACTRL_HIFIPLL_CTRL0, 333 + .shift = 28, 334 + .width = 1, 335 + }, 336 + .m = { 337 + .reg_off = ANACTRL_HIFIPLL_CTRL0, 338 + .shift = 0, 339 + .width = 8, 340 + }, 341 + .frac = { 342 + .reg_off = ANACTRL_HIFIPLL_CTRL1, 343 + .shift = 0, 344 + .width = 19, 345 + }, 346 + .n = { 347 + .reg_off = ANACTRL_HIFIPLL_CTRL0, 348 + .shift = 10, 349 + .width = 5, 350 + }, 351 + .l = { 352 + .reg_off = ANACTRL_HIFIPLL_CTRL0, 353 + .shift = 31, 354 + .width = 1, 355 + }, 356 + .rst = { 357 + .reg_off = ANACTRL_HIFIPLL_CTRL0, 358 + .shift = 29, 359 + .width = 1, 360 + }, 361 + .range = &c3_gp0_pll_mult_range, 362 + .init_regs = c3_hifi_init_regs, 363 + .init_count = ARRAY_SIZE(c3_hifi_init_regs), 364 + }, 365 + .hw.init = &(struct clk_init_data) { 366 + .name = "hifi_pll_dco", 367 + .ops = &meson_clk_pll_ops, 368 + .parent_data = &(const struct clk_parent_data) { 369 + .fw_name = "top", 370 + }, 371 + .num_parents = 1, 372 + }, 373 + }; 374 + 375 + static struct clk_regmap hifi_pll = { 376 + .data = &(struct clk_regmap_div_data) { 377 + .offset = ANACTRL_HIFIPLL_CTRL0, 378 + .shift = 16, 379 + .width = 2, 380 + .flags = CLK_DIVIDER_POWER_OF_TWO, 381 + }, 382 + .hw.init = &(struct clk_init_data) { 383 + .name = "hifi_pll", 384 + .ops = &clk_regmap_divider_ops, 385 + .parent_hws = (const struct clk_hw *[]) { 386 + &hifi_pll_dco.hw 387 + }, 388 + .num_parents = 1, 389 + .flags = CLK_SET_RATE_PARENT, 390 + }, 391 + }; 392 + 393 + static const struct reg_sequence c3_mclk_init_regs[] = { 394 + { .reg = ANACTRL_MPLL_CTRL1, .def = 0x1420500f }, 395 + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x00023041 }, 396 + { .reg = ANACTRL_MPLL_CTRL3, .def = 0x18180000 }, 397 + { .reg = ANACTRL_MPLL_CTRL2, .def = 0x00023001 } 398 + }; 399 + 400 + static const struct pll_mult_range c3_mclk_pll_mult_range = { 401 + .min = 67, 402 + .max = 133, 403 + }; 404 + 405 + static struct clk_regmap mclk_pll_dco = { 406 + .data = &(struct meson_clk_pll_data) { 407 + .en = { 408 + .reg_off = ANACTRL_MPLL_CTRL0, 409 + .shift = 28, 410 + .width = 1, 411 + }, 412 + .m = { 413 + .reg_off = ANACTRL_MPLL_CTRL0, 414 + .shift = 0, 415 + .width = 8, 416 + }, 417 + .n = { 418 + .reg_off = ANACTRL_MPLL_CTRL0, 419 + .shift = 16, 420 + .width = 5, 421 + }, 422 + .l = { 423 + .reg_off = ANACTRL_MPLL_CTRL0, 424 + .shift = 31, 425 + .width = 1, 426 + }, 427 + .rst = { 428 + .reg_off = ANACTRL_MPLL_CTRL0, 429 + .shift = 29, 430 + .width = 1, 431 + }, 432 + .range = &c3_mclk_pll_mult_range, 433 + .init_regs = c3_mclk_init_regs, 434 + .init_count = ARRAY_SIZE(c3_mclk_init_regs), 435 + }, 436 + .hw.init = &(struct clk_init_data) { 437 + .name = "mclk_pll_dco", 438 + .ops = &meson_clk_pll_ops, 439 + .parent_data = &(const struct clk_parent_data) { 440 + .fw_name = "mclk", 441 + }, 442 + .num_parents = 1, 443 + }, 444 + }; 445 + 446 + static const struct clk_div_table c3_mpll_od_table[] = { 447 + { 0, 1 }, 448 + { 1, 2 }, 449 + { 2, 4 }, 450 + { 3, 8 }, 451 + { 4, 16 }, 452 + { /* sentinel */ } 453 + }; 454 + 455 + static struct clk_regmap mclk_pll_od = { 456 + .data = &(struct clk_regmap_div_data) { 457 + .offset = ANACTRL_MPLL_CTRL0, 458 + .shift = 12, 459 + .width = 3, 460 + .table = c3_mpll_od_table, 461 + }, 462 + .hw.init = &(struct clk_init_data) { 463 + .name = "mclk_pll_od", 464 + .ops = &clk_regmap_divider_ops, 465 + .parent_hws = (const struct clk_hw *[]) { 466 + &mclk_pll_dco.hw }, 467 + .num_parents = 1, 468 + .flags = CLK_SET_RATE_PARENT, 469 + }, 470 + }; 471 + 472 + /* both value 0 and 1 gives divide the input rate by one */ 473 + static struct clk_regmap mclk_pll = { 474 + .data = &(struct clk_regmap_div_data) { 475 + .offset = ANACTRL_MPLL_CTRL4, 476 + .shift = 16, 477 + .width = 5, 478 + .flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 479 + }, 480 + .hw.init = &(struct clk_init_data) { 481 + .name = "mclk_pll", 482 + .ops = &clk_regmap_divider_ops, 483 + .parent_hws = (const struct clk_hw *[]) { 484 + &mclk_pll_od.hw 485 + }, 486 + .num_parents = 1, 487 + .flags = CLK_SET_RATE_PARENT, 488 + }, 489 + }; 490 + 491 + static const struct clk_parent_data mclk_parent[] = { 492 + { .hw = &mclk_pll.hw }, 493 + { .fw_name = "mclk" }, 494 + { .hw = &fclk_50m.hw } 495 + }; 496 + 497 + static struct clk_regmap mclk0_sel = { 498 + .data = &(struct clk_regmap_mux_data) { 499 + .offset = ANACTRL_MPLL_CTRL4, 500 + .mask = 0x3, 501 + .shift = 4, 502 + }, 503 + .hw.init = &(struct clk_init_data) { 504 + .name = "mclk0_sel", 505 + .ops = &clk_regmap_mux_ops, 506 + .parent_data = mclk_parent, 507 + .num_parents = ARRAY_SIZE(mclk_parent), 508 + }, 509 + }; 510 + 511 + static struct clk_regmap mclk0_div_en = { 512 + .data = &(struct clk_regmap_gate_data) { 513 + .offset = ANACTRL_MPLL_CTRL4, 514 + .bit_idx = 1, 515 + }, 516 + .hw.init = &(struct clk_init_data) { 517 + .name = "mclk0_div_en", 518 + .ops = &clk_regmap_gate_ops, 519 + .parent_hws = (const struct clk_hw *[]) { 520 + &mclk0_sel.hw 521 + }, 522 + .num_parents = 1, 523 + .flags = CLK_SET_RATE_PARENT, 524 + }, 525 + }; 526 + 527 + static struct clk_regmap mclk0_div = { 528 + .data = &(struct clk_regmap_div_data) { 529 + .offset = ANACTRL_MPLL_CTRL4, 530 + .shift = 2, 531 + .width = 1, 532 + }, 533 + .hw.init = &(struct clk_init_data) { 534 + .name = "mclk0_div", 535 + .ops = &clk_regmap_divider_ops, 536 + .parent_hws = (const struct clk_hw *[]) { 537 + &mclk0_div_en.hw 538 + }, 539 + .num_parents = 1, 540 + .flags = CLK_SET_RATE_PARENT, 541 + }, 542 + }; 543 + 544 + static struct clk_regmap mclk0 = { 545 + .data = &(struct clk_regmap_gate_data) { 546 + .offset = ANACTRL_MPLL_CTRL4, 547 + .bit_idx = 0, 548 + }, 549 + .hw.init = &(struct clk_init_data) { 550 + .name = "mclk0", 551 + .ops = &clk_regmap_gate_ops, 552 + .parent_hws = (const struct clk_hw *[]) { 553 + &mclk0_div.hw 554 + }, 555 + .num_parents = 1, 556 + .flags = CLK_SET_RATE_PARENT, 557 + }, 558 + }; 559 + 560 + static struct clk_regmap mclk1_sel = { 561 + .data = &(struct clk_regmap_mux_data) { 562 + .offset = ANACTRL_MPLL_CTRL4, 563 + .mask = 0x3, 564 + .shift = 12, 565 + }, 566 + .hw.init = &(struct clk_init_data) { 567 + .name = "mclk1_sel", 568 + .ops = &clk_regmap_mux_ops, 569 + .parent_data = mclk_parent, 570 + .num_parents = ARRAY_SIZE(mclk_parent), 571 + }, 572 + }; 573 + 574 + static struct clk_regmap mclk1_div_en = { 575 + .data = &(struct clk_regmap_gate_data) { 576 + .offset = ANACTRL_MPLL_CTRL4, 577 + .bit_idx = 9, 578 + }, 579 + .hw.init = &(struct clk_init_data) { 580 + .name = "mclk1_div_en", 581 + .ops = &clk_regmap_gate_ops, 582 + .parent_hws = (const struct clk_hw *[]) { 583 + &mclk1_sel.hw 584 + }, 585 + .num_parents = 1, 586 + .flags = CLK_SET_RATE_PARENT, 587 + }, 588 + }; 589 + 590 + static struct clk_regmap mclk1_div = { 591 + .data = &(struct clk_regmap_div_data) { 592 + .offset = ANACTRL_MPLL_CTRL4, 593 + .shift = 10, 594 + .width = 1, 595 + }, 596 + .hw.init = &(struct clk_init_data) { 597 + .name = "mclk1_div", 598 + .ops = &clk_regmap_divider_ops, 599 + .parent_hws = (const struct clk_hw *[]) { 600 + &mclk1_div_en.hw 601 + }, 602 + .num_parents = 1, 603 + .flags = CLK_SET_RATE_PARENT, 604 + }, 605 + }; 606 + 607 + static struct clk_regmap mclk1 = { 608 + .data = &(struct clk_regmap_gate_data) { 609 + .offset = ANACTRL_MPLL_CTRL4, 610 + .bit_idx = 8, 611 + }, 612 + .hw.init = &(struct clk_init_data) { 613 + .name = "mclk1", 614 + .ops = &clk_regmap_gate_ops, 615 + .parent_hws = (const struct clk_hw *[]) { 616 + &mclk1_div.hw 617 + }, 618 + .num_parents = 1, 619 + .flags = CLK_SET_RATE_PARENT, 620 + }, 621 + }; 622 + 623 + static struct clk_hw *c3_pll_hw_clks[] = { 624 + [CLKID_FCLK_50M_EN] = &fclk_50m_en.hw, 625 + [CLKID_FCLK_50M] = &fclk_50m.hw, 626 + [CLKID_FCLK_DIV2_DIV] = &fclk_div2_div.hw, 627 + [CLKID_FCLK_DIV2] = &fclk_div2.hw, 628 + [CLKID_FCLK_DIV2P5_DIV] = &fclk_div2p5_div.hw, 629 + [CLKID_FCLK_DIV2P5] = &fclk_div2p5.hw, 630 + [CLKID_FCLK_DIV3_DIV] = &fclk_div3_div.hw, 631 + [CLKID_FCLK_DIV3] = &fclk_div3.hw, 632 + [CLKID_FCLK_DIV4_DIV] = &fclk_div4_div.hw, 633 + [CLKID_FCLK_DIV4] = &fclk_div4.hw, 634 + [CLKID_FCLK_DIV5_DIV] = &fclk_div5_div.hw, 635 + [CLKID_FCLK_DIV5] = &fclk_div5.hw, 636 + [CLKID_FCLK_DIV7_DIV] = &fclk_div7_div.hw, 637 + [CLKID_FCLK_DIV7] = &fclk_div7.hw, 638 + [CLKID_GP0_PLL_DCO] = &gp0_pll_dco.hw, 639 + [CLKID_GP0_PLL] = &gp0_pll.hw, 640 + [CLKID_HIFI_PLL_DCO] = &hifi_pll_dco.hw, 641 + [CLKID_HIFI_PLL] = &hifi_pll.hw, 642 + [CLKID_MCLK_PLL_DCO] = &mclk_pll_dco.hw, 643 + [CLKID_MCLK_PLL_OD] = &mclk_pll_od.hw, 644 + [CLKID_MCLK_PLL] = &mclk_pll.hw, 645 + [CLKID_MCLK0_SEL] = &mclk0_sel.hw, 646 + [CLKID_MCLK0_SEL_EN] = &mclk0_div_en.hw, 647 + [CLKID_MCLK0_DIV] = &mclk0_div.hw, 648 + [CLKID_MCLK0] = &mclk0.hw, 649 + [CLKID_MCLK1_SEL] = &mclk1_sel.hw, 650 + [CLKID_MCLK1_SEL_EN] = &mclk1_div_en.hw, 651 + [CLKID_MCLK1_DIV] = &mclk1_div.hw, 652 + [CLKID_MCLK1] = &mclk1.hw 653 + }; 654 + 655 + /* Convenience table to populate regmap in .probe */ 656 + static struct clk_regmap *const c3_pll_clk_regmaps[] = { 657 + &fclk_50m_en, 658 + &fclk_div2, 659 + &fclk_div2p5, 660 + &fclk_div3, 661 + &fclk_div4, 662 + &fclk_div5, 663 + &fclk_div7, 664 + &gp0_pll_dco, 665 + &gp0_pll, 666 + &hifi_pll_dco, 667 + &hifi_pll, 668 + &mclk_pll_dco, 669 + &mclk_pll_od, 670 + &mclk_pll, 671 + &mclk0_sel, 672 + &mclk0_div_en, 673 + &mclk0_div, 674 + &mclk0, 675 + &mclk1_sel, 676 + &mclk1_div_en, 677 + &mclk1_div, 678 + &mclk1, 679 + }; 680 + 681 + static struct regmap_config clkc_regmap_config = { 682 + .reg_bits = 32, 683 + .val_bits = 32, 684 + .reg_stride = 4, 685 + .max_register = ANACTRL_MPLL_CTRL4, 686 + }; 687 + 688 + static struct meson_clk_hw_data c3_pll_clks = { 689 + .hws = c3_pll_hw_clks, 690 + .num = ARRAY_SIZE(c3_pll_hw_clks), 691 + }; 692 + 693 + static int c3_pll_probe(struct platform_device *pdev) 694 + { 695 + struct device *dev = &pdev->dev; 696 + struct regmap *regmap; 697 + void __iomem *base; 698 + int clkid, ret, i; 699 + 700 + base = devm_platform_ioremap_resource(pdev, 0); 701 + if (IS_ERR(base)) 702 + return PTR_ERR(base); 703 + 704 + regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config); 705 + if (IS_ERR(regmap)) 706 + return PTR_ERR(regmap); 707 + 708 + /* Populate regmap for the regmap backed clocks */ 709 + for (i = 0; i < ARRAY_SIZE(c3_pll_clk_regmaps); i++) 710 + c3_pll_clk_regmaps[i]->map = regmap; 711 + 712 + for (clkid = 0; clkid < c3_pll_clks.num; clkid++) { 713 + /* array might be sparse */ 714 + if (!c3_pll_clks.hws[clkid]) 715 + continue; 716 + 717 + ret = devm_clk_hw_register(dev, c3_pll_clks.hws[clkid]); 718 + if (ret) { 719 + dev_err(dev, "Clock registration failed\n"); 720 + return ret; 721 + } 722 + } 723 + 724 + return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get, 725 + &c3_pll_clks); 726 + } 727 + 728 + static const struct of_device_id c3_pll_clkc_match_table[] = { 729 + { 730 + .compatible = "amlogic,c3-pll-clkc", 731 + }, 732 + {} 733 + }; 734 + MODULE_DEVICE_TABLE(of, c3_pll_clkc_match_table); 735 + 736 + static struct platform_driver c3_pll_driver = { 737 + .probe = c3_pll_probe, 738 + .driver = { 739 + .name = "c3-pll-clkc", 740 + .of_match_table = c3_pll_clkc_match_table, 741 + }, 742 + }; 743 + 744 + module_platform_driver(c3_pll_driver); 745 + MODULE_AUTHOR("Chuan Liu <chuan.liu@amlogic.com>"); 746 + MODULE_LICENSE("GPL");