346 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) 2018 Amlogic, inc.9 * Author: Qiufang Dai <qiufang.dai@amlogic.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/axg-aoclkc.h>22#include <dt-bindings/reset/axg-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_PWR_CNTL_REG1 0x0C29#define AO_RTI_PWR_CNTL_REG0 0x1030#define AO_RTI_GEN_CNTL_REG0 0x4031#define AO_OSCIN_CNTL 0x5832#define AO_CRT_CLK_CNTL1 0x6833#define AO_SAR_CLK 0x9034#define AO_RTC_ALT_CLK_CNTL0 0x9435#define AO_RTC_ALT_CLK_CNTL1 0x9836 37#define AXG_AO_GATE(_name, _bit) \38static struct clk_regmap axg_aoclk_##_name = { \39 .data = &(struct clk_regmap_gate_data) { \40 .offset = (AO_RTI_GEN_CNTL_REG0), \41 .bit_idx = (_bit), \42 }, \43 .hw.init = &(struct clk_init_data) { \44 .name = "axg_ao_" #_name, \45 .ops = &clk_regmap_gate_ops, \46 .parent_data = &(const struct clk_parent_data) { \47 .fw_name = "mpeg-clk", \48 }, \49 .num_parents = 1, \50 .flags = CLK_IGNORE_UNUSED, \51 }, \52}53 54AXG_AO_GATE(remote, 0);55AXG_AO_GATE(i2c_master, 1);56AXG_AO_GATE(i2c_slave, 2);57AXG_AO_GATE(uart1, 3);58AXG_AO_GATE(uart2, 5);59AXG_AO_GATE(ir_blaster, 6);60AXG_AO_GATE(saradc, 7);61 62static struct clk_regmap axg_aoclk_cts_oscin = {63 .data = &(struct clk_regmap_gate_data){64 .offset = AO_RTI_PWR_CNTL_REG0,65 .bit_idx = 14,66 },67 .hw.init = &(struct clk_init_data){68 .name = "cts_oscin",69 .ops = &clk_regmap_gate_ro_ops,70 .parent_data = &(const struct clk_parent_data) {71 .fw_name = "xtal",72 },73 .num_parents = 1,74 },75};76 77static struct clk_regmap axg_aoclk_32k_pre = {78 .data = &(struct clk_regmap_gate_data){79 .offset = AO_RTC_ALT_CLK_CNTL0,80 .bit_idx = 31,81 },82 .hw.init = &(struct clk_init_data){83 .name = "axg_ao_32k_pre",84 .ops = &clk_regmap_gate_ops,85 .parent_hws = (const struct clk_hw *[]) {86 &axg_aoclk_cts_oscin.hw87 },88 .num_parents = 1,89 },90};91 92static const struct meson_clk_dualdiv_param axg_32k_div_table[] = {93 {94 .dual = 1,95 .n1 = 733,96 .m1 = 8,97 .n2 = 732,98 .m2 = 11,99 }, {}100};101 102static struct clk_regmap axg_aoclk_32k_div = {103 .data = &(struct meson_clk_dualdiv_data){104 .n1 = {105 .reg_off = AO_RTC_ALT_CLK_CNTL0,106 .shift = 0,107 .width = 12,108 },109 .n2 = {110 .reg_off = AO_RTC_ALT_CLK_CNTL0,111 .shift = 12,112 .width = 12,113 },114 .m1 = {115 .reg_off = AO_RTC_ALT_CLK_CNTL1,116 .shift = 0,117 .width = 12,118 },119 .m2 = {120 .reg_off = AO_RTC_ALT_CLK_CNTL1,121 .shift = 12,122 .width = 12,123 },124 .dual = {125 .reg_off = AO_RTC_ALT_CLK_CNTL0,126 .shift = 28,127 .width = 1,128 },129 .table = axg_32k_div_table,130 },131 .hw.init = &(struct clk_init_data){132 .name = "axg_ao_32k_div",133 .ops = &meson_clk_dualdiv_ops,134 .parent_hws = (const struct clk_hw *[]) {135 &axg_aoclk_32k_pre.hw136 },137 .num_parents = 1,138 },139};140 141static struct clk_regmap axg_aoclk_32k_sel = {142 .data = &(struct clk_regmap_mux_data) {143 .offset = AO_RTC_ALT_CLK_CNTL1,144 .mask = 0x1,145 .shift = 24,146 .flags = CLK_MUX_ROUND_CLOSEST,147 },148 .hw.init = &(struct clk_init_data){149 .name = "axg_ao_32k_sel",150 .ops = &clk_regmap_mux_ops,151 .parent_hws = (const struct clk_hw *[]) {152 &axg_aoclk_32k_div.hw,153 &axg_aoclk_32k_pre.hw,154 },155 .num_parents = 2,156 .flags = CLK_SET_RATE_PARENT,157 },158};159 160static struct clk_regmap axg_aoclk_32k = {161 .data = &(struct clk_regmap_gate_data){162 .offset = AO_RTC_ALT_CLK_CNTL0,163 .bit_idx = 30,164 },165 .hw.init = &(struct clk_init_data){166 .name = "axg_ao_32k",167 .ops = &clk_regmap_gate_ops,168 .parent_hws = (const struct clk_hw *[]) {169 &axg_aoclk_32k_sel.hw170 },171 .num_parents = 1,172 .flags = CLK_SET_RATE_PARENT,173 },174};175 176static struct clk_regmap axg_aoclk_cts_rtc_oscin = {177 .data = &(struct clk_regmap_mux_data) {178 .offset = AO_RTI_PWR_CNTL_REG0,179 .mask = 0x1,180 .shift = 10,181 .flags = CLK_MUX_ROUND_CLOSEST,182 },183 .hw.init = &(struct clk_init_data){184 .name = "axg_ao_cts_rtc_oscin",185 .ops = &clk_regmap_mux_ops,186 .parent_data = (const struct clk_parent_data []) {187 { .hw = &axg_aoclk_32k.hw },188 { .fw_name = "ext_32k-0", },189 },190 .num_parents = 2,191 .flags = CLK_SET_RATE_PARENT,192 },193};194 195static struct clk_regmap axg_aoclk_clk81 = {196 .data = &(struct clk_regmap_mux_data) {197 .offset = AO_RTI_PWR_CNTL_REG0,198 .mask = 0x1,199 .shift = 8,200 .flags = CLK_MUX_ROUND_CLOSEST,201 },202 .hw.init = &(struct clk_init_data){203 .name = "axg_ao_clk81",204 .ops = &clk_regmap_mux_ro_ops,205 .parent_data = (const struct clk_parent_data []) {206 { .fw_name = "mpeg-clk", },207 { .hw = &axg_aoclk_cts_rtc_oscin.hw },208 },209 .num_parents = 2,210 .flags = CLK_SET_RATE_PARENT,211 },212};213 214static struct clk_regmap axg_aoclk_saradc_mux = {215 .data = &(struct clk_regmap_mux_data) {216 .offset = AO_SAR_CLK,217 .mask = 0x3,218 .shift = 9,219 },220 .hw.init = &(struct clk_init_data){221 .name = "axg_ao_saradc_mux",222 .ops = &clk_regmap_mux_ops,223 .parent_data = (const struct clk_parent_data []) {224 { .fw_name = "xtal", },225 { .hw = &axg_aoclk_clk81.hw },226 },227 .num_parents = 2,228 },229};230 231static struct clk_regmap axg_aoclk_saradc_div = {232 .data = &(struct clk_regmap_div_data) {233 .offset = AO_SAR_CLK,234 .shift = 0,235 .width = 8,236 },237 .hw.init = &(struct clk_init_data){238 .name = "axg_ao_saradc_div",239 .ops = &clk_regmap_divider_ops,240 .parent_hws = (const struct clk_hw *[]) {241 &axg_aoclk_saradc_mux.hw242 },243 .num_parents = 1,244 .flags = CLK_SET_RATE_PARENT,245 },246};247 248static struct clk_regmap axg_aoclk_saradc_gate = {249 .data = &(struct clk_regmap_gate_data) {250 .offset = AO_SAR_CLK,251 .bit_idx = 8,252 },253 .hw.init = &(struct clk_init_data){254 .name = "axg_ao_saradc_gate",255 .ops = &clk_regmap_gate_ops,256 .parent_hws = (const struct clk_hw *[]) {257 &axg_aoclk_saradc_div.hw258 },259 .num_parents = 1,260 .flags = CLK_SET_RATE_PARENT,261 },262};263 264static const unsigned int axg_aoclk_reset[] = {265 [RESET_AO_REMOTE] = 16,266 [RESET_AO_I2C_MASTER] = 18,267 [RESET_AO_I2C_SLAVE] = 19,268 [RESET_AO_UART1] = 17,269 [RESET_AO_UART2] = 22,270 [RESET_AO_IR_BLASTER] = 23,271};272 273static struct clk_regmap *axg_aoclk_regmap[] = {274 &axg_aoclk_remote,275 &axg_aoclk_i2c_master,276 &axg_aoclk_i2c_slave,277 &axg_aoclk_uart1,278 &axg_aoclk_uart2,279 &axg_aoclk_ir_blaster,280 &axg_aoclk_saradc,281 &axg_aoclk_cts_oscin,282 &axg_aoclk_32k_pre,283 &axg_aoclk_32k_div,284 &axg_aoclk_32k_sel,285 &axg_aoclk_32k,286 &axg_aoclk_cts_rtc_oscin,287 &axg_aoclk_clk81,288 &axg_aoclk_saradc_mux,289 &axg_aoclk_saradc_div,290 &axg_aoclk_saradc_gate,291};292 293static struct clk_hw *axg_aoclk_hw_clks[] = {294 [CLKID_AO_REMOTE] = &axg_aoclk_remote.hw,295 [CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master.hw,296 [CLKID_AO_I2C_SLAVE] = &axg_aoclk_i2c_slave.hw,297 [CLKID_AO_UART1] = &axg_aoclk_uart1.hw,298 [CLKID_AO_UART2] = &axg_aoclk_uart2.hw,299 [CLKID_AO_IR_BLASTER] = &axg_aoclk_ir_blaster.hw,300 [CLKID_AO_SAR_ADC] = &axg_aoclk_saradc.hw,301 [CLKID_AO_CLK81] = &axg_aoclk_clk81.hw,302 [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux.hw,303 [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div.hw,304 [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate.hw,305 [CLKID_AO_CTS_OSCIN] = &axg_aoclk_cts_oscin.hw,306 [CLKID_AO_32K_PRE] = &axg_aoclk_32k_pre.hw,307 [CLKID_AO_32K_DIV] = &axg_aoclk_32k_div.hw,308 [CLKID_AO_32K_SEL] = &axg_aoclk_32k_sel.hw,309 [CLKID_AO_32K] = &axg_aoclk_32k.hw,310 [CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw,311};312 313static const struct meson_aoclk_data axg_aoclkc_data = {314 .reset_reg = AO_RTI_GEN_CNTL_REG0,315 .num_reset = ARRAY_SIZE(axg_aoclk_reset),316 .reset = axg_aoclk_reset,317 .num_clks = ARRAY_SIZE(axg_aoclk_regmap),318 .clks = axg_aoclk_regmap,319 .hw_clks = {320 .hws = axg_aoclk_hw_clks,321 .num = ARRAY_SIZE(axg_aoclk_hw_clks),322 },323};324 325static const struct of_device_id axg_aoclkc_match_table[] = {326 {327 .compatible = "amlogic,meson-axg-aoclkc",328 .data = &axg_aoclkc_data,329 },330 { }331};332MODULE_DEVICE_TABLE(of, axg_aoclkc_match_table);333 334static struct platform_driver axg_aoclkc_driver = {335 .probe = meson_aoclkc_probe,336 .driver = {337 .name = "axg-aoclkc",338 .of_match_table = axg_aoclkc_match_table,339 },340};341module_platform_driver(axg_aoclkc_driver);342 343MODULE_DESCRIPTION("Amlogic AXG Always-ON Clock Controller driver");344MODULE_LICENSE("GPL");345MODULE_IMPORT_NS(CLK_MESON);346