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