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) 2019 Baylibre SAS. 9 * Author: Neil Armstrong <narmstrong@baylibre.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/g12a-aoclkc.h> 22 #include <dt-bindings/reset/g12a-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_STATUS_REG3 0x0C 29 #define AO_RTI_PWR_CNTL_REG0 0x10 30 #define AO_RTI_GEN_CNTL_REG0 0x40 31 #define AO_CLK_GATE0 0x4c 32 #define AO_CLK_GATE0_SP 0x50 33 #define AO_OSCIN_CNTL 0x58 34 #define AO_CEC_CLK_CNTL_REG0 0x74 35 #define AO_CEC_CLK_CNTL_REG1 0x78 36 #define AO_SAR_CLK 0x90 37 #define AO_RTC_ALT_CLK_CNTL0 0x94 38 #define AO_RTC_ALT_CLK_CNTL1 0x98 39 40 /* 41 * Like every other peripheral clock gate in Amlogic Clock drivers, 42 * we are using CLK_IGNORE_UNUSED here, so we keep the state of the 43 * bootloader. The goal is to remove this flag at some point. 44 * Actually removing it will require some extensive test to be done safely. 45 */ 46 #define AXG_AO_GATE(_name, _reg, _bit) \ 47 static struct clk_regmap g12a_aoclk_##_name = { \ 48 .data = &(struct clk_regmap_gate_data) { \ 49 .offset = (_reg), \ 50 .bit_idx = (_bit), \ 51 }, \ 52 .hw.init = &(struct clk_init_data) { \ 53 .name = "g12a_ao_" #_name, \ 54 .ops = &clk_regmap_gate_ops, \ 55 .parent_data = &(const struct clk_parent_data) { \ 56 .fw_name = "mpeg-clk", \ 57 }, \ 58 .num_parents = 1, \ 59 .flags = CLK_IGNORE_UNUSED, \ 60 }, \ 61 } 62 63 AXG_AO_GATE(ahb, AO_CLK_GATE0, 0); 64 AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1); 65 AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2); 66 AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3); 67 AXG_AO_GATE(uart, AO_CLK_GATE0, 4); 68 AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5); 69 AXG_AO_GATE(uart2, AO_CLK_GATE0, 6); 70 AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7); 71 AXG_AO_GATE(saradc, AO_CLK_GATE0, 8); 72 AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0); 73 AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1); 74 AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2); 75 AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3); 76 AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4); 77 AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5); 78 79 static struct clk_regmap g12a_aoclk_cts_oscin = { 80 .data = &(struct clk_regmap_gate_data){ 81 .offset = AO_RTI_PWR_CNTL_REG0, 82 .bit_idx = 14, 83 }, 84 .hw.init = &(struct clk_init_data){ 85 .name = "cts_oscin", 86 .ops = &clk_regmap_gate_ro_ops, 87 .parent_data = &(const struct clk_parent_data) { 88 .fw_name = "xtal", 89 }, 90 .num_parents = 1, 91 }, 92 }; 93 94 static const struct meson_clk_dualdiv_param g12a_32k_div_table[] = { 95 { 96 .dual = 1, 97 .n1 = 733, 98 .m1 = 8, 99 .n2 = 732, 100 .m2 = 11, 101 }, {} 102 }; 103 104 /* 32k_by_oscin clock */ 105 106 static struct clk_regmap g12a_aoclk_32k_by_oscin_pre = { 107 .data = &(struct clk_regmap_gate_data){ 108 .offset = AO_RTC_ALT_CLK_CNTL0, 109 .bit_idx = 31, 110 }, 111 .hw.init = &(struct clk_init_data){ 112 .name = "g12a_ao_32k_by_oscin_pre", 113 .ops = &clk_regmap_gate_ops, 114 .parent_hws = (const struct clk_hw *[]) { 115 &g12a_aoclk_cts_oscin.hw 116 }, 117 .num_parents = 1, 118 }, 119 }; 120 121 static struct clk_regmap g12a_aoclk_32k_by_oscin_div = { 122 .data = &(struct meson_clk_dualdiv_data){ 123 .n1 = { 124 .reg_off = AO_RTC_ALT_CLK_CNTL0, 125 .shift = 0, 126 .width = 12, 127 }, 128 .n2 = { 129 .reg_off = AO_RTC_ALT_CLK_CNTL0, 130 .shift = 12, 131 .width = 12, 132 }, 133 .m1 = { 134 .reg_off = AO_RTC_ALT_CLK_CNTL1, 135 .shift = 0, 136 .width = 12, 137 }, 138 .m2 = { 139 .reg_off = AO_RTC_ALT_CLK_CNTL1, 140 .shift = 12, 141 .width = 12, 142 }, 143 .dual = { 144 .reg_off = AO_RTC_ALT_CLK_CNTL0, 145 .shift = 28, 146 .width = 1, 147 }, 148 .table = g12a_32k_div_table, 149 }, 150 .hw.init = &(struct clk_init_data){ 151 .name = "g12a_ao_32k_by_oscin_div", 152 .ops = &meson_clk_dualdiv_ops, 153 .parent_hws = (const struct clk_hw *[]) { 154 &g12a_aoclk_32k_by_oscin_pre.hw 155 }, 156 .num_parents = 1, 157 }, 158 }; 159 160 static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = { 161 .data = &(struct clk_regmap_mux_data) { 162 .offset = AO_RTC_ALT_CLK_CNTL1, 163 .mask = 0x1, 164 .shift = 24, 165 .flags = CLK_MUX_ROUND_CLOSEST, 166 }, 167 .hw.init = &(struct clk_init_data){ 168 .name = "g12a_ao_32k_by_oscin_sel", 169 .ops = &clk_regmap_mux_ops, 170 .parent_hws = (const struct clk_hw *[]) { 171 &g12a_aoclk_32k_by_oscin_div.hw, 172 &g12a_aoclk_32k_by_oscin_pre.hw, 173 }, 174 .num_parents = 2, 175 .flags = CLK_SET_RATE_PARENT, 176 }, 177 }; 178 179 static struct clk_regmap g12a_aoclk_32k_by_oscin = { 180 .data = &(struct clk_regmap_gate_data){ 181 .offset = AO_RTC_ALT_CLK_CNTL0, 182 .bit_idx = 30, 183 }, 184 .hw.init = &(struct clk_init_data){ 185 .name = "g12a_ao_32k_by_oscin", 186 .ops = &clk_regmap_gate_ops, 187 .parent_hws = (const struct clk_hw *[]) { 188 &g12a_aoclk_32k_by_oscin_sel.hw 189 }, 190 .num_parents = 1, 191 .flags = CLK_SET_RATE_PARENT, 192 }, 193 }; 194 195 /* cec clock */ 196 197 static struct clk_regmap g12a_aoclk_cec_pre = { 198 .data = &(struct clk_regmap_gate_data){ 199 .offset = AO_CEC_CLK_CNTL_REG0, 200 .bit_idx = 31, 201 }, 202 .hw.init = &(struct clk_init_data){ 203 .name = "g12a_ao_cec_pre", 204 .ops = &clk_regmap_gate_ops, 205 .parent_hws = (const struct clk_hw *[]) { 206 &g12a_aoclk_cts_oscin.hw 207 }, 208 .num_parents = 1, 209 }, 210 }; 211 212 static struct clk_regmap g12a_aoclk_cec_div = { 213 .data = &(struct meson_clk_dualdiv_data){ 214 .n1 = { 215 .reg_off = AO_CEC_CLK_CNTL_REG0, 216 .shift = 0, 217 .width = 12, 218 }, 219 .n2 = { 220 .reg_off = AO_CEC_CLK_CNTL_REG0, 221 .shift = 12, 222 .width = 12, 223 }, 224 .m1 = { 225 .reg_off = AO_CEC_CLK_CNTL_REG1, 226 .shift = 0, 227 .width = 12, 228 }, 229 .m2 = { 230 .reg_off = AO_CEC_CLK_CNTL_REG1, 231 .shift = 12, 232 .width = 12, 233 }, 234 .dual = { 235 .reg_off = AO_CEC_CLK_CNTL_REG0, 236 .shift = 28, 237 .width = 1, 238 }, 239 .table = g12a_32k_div_table, 240 }, 241 .hw.init = &(struct clk_init_data){ 242 .name = "g12a_ao_cec_div", 243 .ops = &meson_clk_dualdiv_ops, 244 .parent_hws = (const struct clk_hw *[]) { 245 &g12a_aoclk_cec_pre.hw 246 }, 247 .num_parents = 1, 248 }, 249 }; 250 251 static struct clk_regmap g12a_aoclk_cec_sel = { 252 .data = &(struct clk_regmap_mux_data) { 253 .offset = AO_CEC_CLK_CNTL_REG1, 254 .mask = 0x1, 255 .shift = 24, 256 .flags = CLK_MUX_ROUND_CLOSEST, 257 }, 258 .hw.init = &(struct clk_init_data){ 259 .name = "g12a_ao_cec_sel", 260 .ops = &clk_regmap_mux_ops, 261 .parent_hws = (const struct clk_hw *[]) { 262 &g12a_aoclk_cec_div.hw, 263 &g12a_aoclk_cec_pre.hw, 264 }, 265 .num_parents = 2, 266 .flags = CLK_SET_RATE_PARENT, 267 }, 268 }; 269 270 static struct clk_regmap g12a_aoclk_cec = { 271 .data = &(struct clk_regmap_gate_data){ 272 .offset = AO_CEC_CLK_CNTL_REG0, 273 .bit_idx = 30, 274 }, 275 .hw.init = &(struct clk_init_data){ 276 .name = "g12a_ao_cec", 277 .ops = &clk_regmap_gate_ops, 278 .parent_hws = (const struct clk_hw *[]) { 279 &g12a_aoclk_cec_sel.hw 280 }, 281 .num_parents = 1, 282 .flags = CLK_SET_RATE_PARENT, 283 }, 284 }; 285 286 static struct clk_regmap g12a_aoclk_cts_rtc_oscin = { 287 .data = &(struct clk_regmap_mux_data) { 288 .offset = AO_RTI_PWR_CNTL_REG0, 289 .mask = 0x1, 290 .shift = 10, 291 .flags = CLK_MUX_ROUND_CLOSEST, 292 }, 293 .hw.init = &(struct clk_init_data){ 294 .name = "g12a_ao_cts_rtc_oscin", 295 .ops = &clk_regmap_mux_ops, 296 .parent_data = (const struct clk_parent_data []) { 297 { .hw = &g12a_aoclk_32k_by_oscin.hw }, 298 { .fw_name = "ext-32k-0", }, 299 }, 300 .num_parents = 2, 301 .flags = CLK_SET_RATE_PARENT, 302 }, 303 }; 304 305 static struct clk_regmap g12a_aoclk_clk81 = { 306 .data = &(struct clk_regmap_mux_data) { 307 .offset = AO_RTI_PWR_CNTL_REG0, 308 .mask = 0x1, 309 .shift = 8, 310 .flags = CLK_MUX_ROUND_CLOSEST, 311 }, 312 .hw.init = &(struct clk_init_data){ 313 .name = "g12a_ao_clk81", 314 .ops = &clk_regmap_mux_ro_ops, 315 .parent_data = (const struct clk_parent_data []) { 316 { .fw_name = "mpeg-clk", }, 317 { .hw = &g12a_aoclk_cts_rtc_oscin.hw }, 318 }, 319 .num_parents = 2, 320 .flags = CLK_SET_RATE_PARENT, 321 }, 322 }; 323 324 static struct clk_regmap g12a_aoclk_saradc_mux = { 325 .data = &(struct clk_regmap_mux_data) { 326 .offset = AO_SAR_CLK, 327 .mask = 0x3, 328 .shift = 9, 329 }, 330 .hw.init = &(struct clk_init_data){ 331 .name = "g12a_ao_saradc_mux", 332 .ops = &clk_regmap_mux_ops, 333 .parent_data = (const struct clk_parent_data []) { 334 { .fw_name = "xtal", }, 335 { .hw = &g12a_aoclk_clk81.hw }, 336 }, 337 .num_parents = 2, 338 }, 339 }; 340 341 static struct clk_regmap g12a_aoclk_saradc_div = { 342 .data = &(struct clk_regmap_div_data) { 343 .offset = AO_SAR_CLK, 344 .shift = 0, 345 .width = 8, 346 }, 347 .hw.init = &(struct clk_init_data){ 348 .name = "g12a_ao_saradc_div", 349 .ops = &clk_regmap_divider_ops, 350 .parent_hws = (const struct clk_hw *[]) { 351 &g12a_aoclk_saradc_mux.hw 352 }, 353 .num_parents = 1, 354 .flags = CLK_SET_RATE_PARENT, 355 }, 356 }; 357 358 static struct clk_regmap g12a_aoclk_saradc_gate = { 359 .data = &(struct clk_regmap_gate_data) { 360 .offset = AO_SAR_CLK, 361 .bit_idx = 8, 362 }, 363 .hw.init = &(struct clk_init_data){ 364 .name = "g12a_ao_saradc_gate", 365 .ops = &clk_regmap_gate_ops, 366 .parent_hws = (const struct clk_hw *[]) { 367 &g12a_aoclk_saradc_div.hw 368 }, 369 .num_parents = 1, 370 .flags = CLK_SET_RATE_PARENT, 371 }, 372 }; 373 374 static const unsigned int g12a_aoclk_reset[] = { 375 [RESET_AO_IR_IN] = 16, 376 [RESET_AO_UART] = 17, 377 [RESET_AO_I2C_M] = 18, 378 [RESET_AO_I2C_S] = 19, 379 [RESET_AO_SAR_ADC] = 20, 380 [RESET_AO_UART2] = 22, 381 [RESET_AO_IR_OUT] = 23, 382 }; 383 384 static struct clk_regmap *g12a_aoclk_regmap[] = { 385 &g12a_aoclk_ahb, 386 &g12a_aoclk_ir_in, 387 &g12a_aoclk_i2c_m0, 388 &g12a_aoclk_i2c_s0, 389 &g12a_aoclk_uart, 390 &g12a_aoclk_prod_i2c, 391 &g12a_aoclk_uart2, 392 &g12a_aoclk_ir_out, 393 &g12a_aoclk_saradc, 394 &g12a_aoclk_mailbox, 395 &g12a_aoclk_m3, 396 &g12a_aoclk_ahb_sram, 397 &g12a_aoclk_rti, 398 &g12a_aoclk_m4_fclk, 399 &g12a_aoclk_m4_hclk, 400 &g12a_aoclk_cts_oscin, 401 &g12a_aoclk_32k_by_oscin_pre, 402 &g12a_aoclk_32k_by_oscin_div, 403 &g12a_aoclk_32k_by_oscin_sel, 404 &g12a_aoclk_32k_by_oscin, 405 &g12a_aoclk_cec_pre, 406 &g12a_aoclk_cec_div, 407 &g12a_aoclk_cec_sel, 408 &g12a_aoclk_cec, 409 &g12a_aoclk_cts_rtc_oscin, 410 &g12a_aoclk_clk81, 411 &g12a_aoclk_saradc_mux, 412 &g12a_aoclk_saradc_div, 413 &g12a_aoclk_saradc_gate, 414 }; 415 416 static struct clk_hw *g12a_aoclk_hw_clks[] = { 417 [CLKID_AO_AHB] = &g12a_aoclk_ahb.hw, 418 [CLKID_AO_IR_IN] = &g12a_aoclk_ir_in.hw, 419 [CLKID_AO_I2C_M0] = &g12a_aoclk_i2c_m0.hw, 420 [CLKID_AO_I2C_S0] = &g12a_aoclk_i2c_s0.hw, 421 [CLKID_AO_UART] = &g12a_aoclk_uart.hw, 422 [CLKID_AO_PROD_I2C] = &g12a_aoclk_prod_i2c.hw, 423 [CLKID_AO_UART2] = &g12a_aoclk_uart2.hw, 424 [CLKID_AO_IR_OUT] = &g12a_aoclk_ir_out.hw, 425 [CLKID_AO_SAR_ADC] = &g12a_aoclk_saradc.hw, 426 [CLKID_AO_MAILBOX] = &g12a_aoclk_mailbox.hw, 427 [CLKID_AO_M3] = &g12a_aoclk_m3.hw, 428 [CLKID_AO_AHB_SRAM] = &g12a_aoclk_ahb_sram.hw, 429 [CLKID_AO_RTI] = &g12a_aoclk_rti.hw, 430 [CLKID_AO_M4_FCLK] = &g12a_aoclk_m4_fclk.hw, 431 [CLKID_AO_M4_HCLK] = &g12a_aoclk_m4_hclk.hw, 432 [CLKID_AO_CLK81] = &g12a_aoclk_clk81.hw, 433 [CLKID_AO_SAR_ADC_SEL] = &g12a_aoclk_saradc_mux.hw, 434 [CLKID_AO_SAR_ADC_DIV] = &g12a_aoclk_saradc_div.hw, 435 [CLKID_AO_SAR_ADC_CLK] = &g12a_aoclk_saradc_gate.hw, 436 [CLKID_AO_CTS_OSCIN] = &g12a_aoclk_cts_oscin.hw, 437 [CLKID_AO_32K_PRE] = &g12a_aoclk_32k_by_oscin_pre.hw, 438 [CLKID_AO_32K_DIV] = &g12a_aoclk_32k_by_oscin_div.hw, 439 [CLKID_AO_32K_SEL] = &g12a_aoclk_32k_by_oscin_sel.hw, 440 [CLKID_AO_32K] = &g12a_aoclk_32k_by_oscin.hw, 441 [CLKID_AO_CEC_PRE] = &g12a_aoclk_cec_pre.hw, 442 [CLKID_AO_CEC_DIV] = &g12a_aoclk_cec_div.hw, 443 [CLKID_AO_CEC_SEL] = &g12a_aoclk_cec_sel.hw, 444 [CLKID_AO_CEC] = &g12a_aoclk_cec.hw, 445 [CLKID_AO_CTS_RTC_OSCIN] = &g12a_aoclk_cts_rtc_oscin.hw, 446 }; 447 448 static const struct meson_aoclk_data g12a_aoclkc_data = { 449 .reset_reg = AO_RTI_GEN_CNTL_REG0, 450 .num_reset = ARRAY_SIZE(g12a_aoclk_reset), 451 .reset = g12a_aoclk_reset, 452 .num_clks = ARRAY_SIZE(g12a_aoclk_regmap), 453 .clks = g12a_aoclk_regmap, 454 .hw_clks = { 455 .hws = g12a_aoclk_hw_clks, 456 .num = ARRAY_SIZE(g12a_aoclk_hw_clks), 457 }, 458 }; 459 460 static const struct of_device_id g12a_aoclkc_match_table[] = { 461 { 462 .compatible = "amlogic,meson-g12a-aoclkc", 463 .data = &g12a_aoclkc_data, 464 }, 465 { } 466 }; 467 MODULE_DEVICE_TABLE(of, g12a_aoclkc_match_table); 468 469 static struct platform_driver g12a_aoclkc_driver = { 470 .probe = meson_aoclkc_probe, 471 .driver = { 472 .name = "g12a-aoclkc", 473 .of_match_table = g12a_aoclkc_match_table, 474 }, 475 }; 476 477 module_platform_driver(g12a_aoclkc_driver); 478 MODULE_LICENSE("GPL"); 479