481 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Amlogic Meson-AXG Clock Controller Driver4 *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 offsets26 * Register offsets from the data sheet must be multiplied by 4.27 */28#define AO_RTI_STATUS_REG3 0x0C29#define AO_RTI_PWR_CNTL_REG0 0x1030#define AO_RTI_GEN_CNTL_REG0 0x4031#define AO_CLK_GATE0 0x4c32#define AO_CLK_GATE0_SP 0x5033#define AO_OSCIN_CNTL 0x5834#define AO_CEC_CLK_CNTL_REG0 0x7435#define AO_CEC_CLK_CNTL_REG1 0x7836#define AO_SAR_CLK 0x9037#define AO_RTC_ALT_CLK_CNTL0 0x9438#define AO_RTC_ALT_CLK_CNTL1 0x9839 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 the43 * 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) \47static 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 63AXG_AO_GATE(ahb, AO_CLK_GATE0, 0);64AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1);65AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2);66AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3);67AXG_AO_GATE(uart, AO_CLK_GATE0, 4);68AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5);69AXG_AO_GATE(uart2, AO_CLK_GATE0, 6);70AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7);71AXG_AO_GATE(saradc, AO_CLK_GATE0, 8);72AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0);73AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1);74AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2);75AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3);76AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4);77AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5);78 79static 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 94static 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 106static 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.hw116 },117 .num_parents = 1,118 },119};120 121static 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.hw155 },156 .num_parents = 1,157 },158};159 160static 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 179static 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.hw189 },190 .num_parents = 1,191 .flags = CLK_SET_RATE_PARENT,192 },193};194 195/* cec clock */196 197static 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.hw207 },208 .num_parents = 1,209 },210};211 212static 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.hw246 },247 .num_parents = 1,248 },249};250 251static 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 270static 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.hw280 },281 .num_parents = 1,282 .flags = CLK_SET_RATE_PARENT,283 },284};285 286static 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 305static 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 324static 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 341static 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.hw352 },353 .num_parents = 1,354 .flags = CLK_SET_RATE_PARENT,355 },356};357 358static 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.hw368 },369 .num_parents = 1,370 .flags = CLK_SET_RATE_PARENT,371 },372};373 374static 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 384static 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 416static 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 448static 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 460static const struct of_device_id g12a_aoclkc_match_table[] = {461 {462 .compatible = "amlogic,meson-g12a-aoclkc",463 .data = &g12a_aoclkc_data,464 },465 { }466};467MODULE_DEVICE_TABLE(of, g12a_aoclkc_match_table);468 469static 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};476module_platform_driver(g12a_aoclkc_driver);477 478MODULE_DESCRIPTION("Amlogic G12A Always-ON Clock Controller driver");479MODULE_LICENSE("GPL");480MODULE_IMPORT_NS(CLK_MESON);481