307 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)2/*3 * Copyright (c) 2016 BayLibre, SAS.4 * Author: Neil Armstrong <narmstrong@baylibre.com>5 */6#include <linux/platform_device.h>7#include <linux/mfd/syscon.h>8#include <linux/module.h>9#include "meson-aoclk.h"10 11#include "clk-regmap.h"12#include "clk-dualdiv.h"13 14#include <dt-bindings/clock/gxbb-aoclkc.h>15#include <dt-bindings/reset/gxbb-aoclkc.h>16 17/* AO Configuration Clock registers offsets */18#define AO_RTI_PWR_CNTL_REG1 0x0c19#define AO_RTI_PWR_CNTL_REG0 0x1020#define AO_RTI_GEN_CNTL_REG0 0x4021#define AO_OSCIN_CNTL 0x5822#define AO_CRT_CLK_CNTL1 0x6823#define AO_RTC_ALT_CLK_CNTL0 0x9424#define AO_RTC_ALT_CLK_CNTL1 0x9825 26#define GXBB_AO_GATE(_name, _bit) \27static struct clk_regmap _name##_ao = { \28 .data = &(struct clk_regmap_gate_data) { \29 .offset = AO_RTI_GEN_CNTL_REG0, \30 .bit_idx = (_bit), \31 }, \32 .hw.init = &(struct clk_init_data) { \33 .name = #_name "_ao", \34 .ops = &clk_regmap_gate_ops, \35 .parent_data = &(const struct clk_parent_data) { \36 .fw_name = "mpeg-clk", \37 }, \38 .num_parents = 1, \39 .flags = CLK_IGNORE_UNUSED, \40 }, \41}42 43GXBB_AO_GATE(remote, 0);44GXBB_AO_GATE(i2c_master, 1);45GXBB_AO_GATE(i2c_slave, 2);46GXBB_AO_GATE(uart1, 3);47GXBB_AO_GATE(uart2, 5);48GXBB_AO_GATE(ir_blaster, 6);49 50static struct clk_regmap ao_cts_oscin = {51 .data = &(struct clk_regmap_gate_data){52 .offset = AO_RTI_PWR_CNTL_REG0,53 .bit_idx = 6,54 },55 .hw.init = &(struct clk_init_data){56 .name = "ao_cts_oscin",57 .ops = &clk_regmap_gate_ro_ops,58 .parent_data = &(const struct clk_parent_data) {59 .fw_name = "xtal",60 },61 .num_parents = 1,62 },63};64 65static struct clk_regmap ao_32k_pre = {66 .data = &(struct clk_regmap_gate_data){67 .offset = AO_RTC_ALT_CLK_CNTL0,68 .bit_idx = 31,69 },70 .hw.init = &(struct clk_init_data){71 .name = "ao_32k_pre",72 .ops = &clk_regmap_gate_ops,73 .parent_hws = (const struct clk_hw *[]) { &ao_cts_oscin.hw },74 .num_parents = 1,75 },76};77 78static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = {79 {80 .dual = 1,81 .n1 = 733,82 .m1 = 8,83 .n2 = 732,84 .m2 = 11,85 }, {}86};87 88static struct clk_regmap ao_32k_div = {89 .data = &(struct meson_clk_dualdiv_data){90 .n1 = {91 .reg_off = AO_RTC_ALT_CLK_CNTL0,92 .shift = 0,93 .width = 12,94 },95 .n2 = {96 .reg_off = AO_RTC_ALT_CLK_CNTL0,97 .shift = 12,98 .width = 12,99 },100 .m1 = {101 .reg_off = AO_RTC_ALT_CLK_CNTL1,102 .shift = 0,103 .width = 12,104 },105 .m2 = {106 .reg_off = AO_RTC_ALT_CLK_CNTL1,107 .shift = 12,108 .width = 12,109 },110 .dual = {111 .reg_off = AO_RTC_ALT_CLK_CNTL0,112 .shift = 28,113 .width = 1,114 },115 .table = gxbb_32k_div_table,116 },117 .hw.init = &(struct clk_init_data){118 .name = "ao_32k_div",119 .ops = &meson_clk_dualdiv_ops,120 .parent_hws = (const struct clk_hw *[]) { &ao_32k_pre.hw },121 .num_parents = 1,122 },123};124 125static struct clk_regmap ao_32k_sel = {126 .data = &(struct clk_regmap_mux_data) {127 .offset = AO_RTC_ALT_CLK_CNTL1,128 .mask = 0x1,129 .shift = 24,130 .flags = CLK_MUX_ROUND_CLOSEST,131 },132 .hw.init = &(struct clk_init_data){133 .name = "ao_32k_sel",134 .ops = &clk_regmap_mux_ops,135 .parent_hws = (const struct clk_hw *[]) {136 &ao_32k_div.hw,137 &ao_32k_pre.hw138 },139 .num_parents = 2,140 .flags = CLK_SET_RATE_PARENT,141 },142};143 144static struct clk_regmap ao_32k = {145 .data = &(struct clk_regmap_gate_data){146 .offset = AO_RTC_ALT_CLK_CNTL0,147 .bit_idx = 30,148 },149 .hw.init = &(struct clk_init_data){150 .name = "ao_32k",151 .ops = &clk_regmap_gate_ops,152 .parent_hws = (const struct clk_hw *[]) { &ao_32k_sel.hw },153 .num_parents = 1,154 .flags = CLK_SET_RATE_PARENT,155 },156};157 158static struct clk_regmap ao_cts_rtc_oscin = {159 .data = &(struct clk_regmap_mux_data) {160 .offset = AO_RTI_PWR_CNTL_REG0,161 .mask = 0x7,162 .shift = 10,163 .table = (u32[]){ 1, 2, 3, 4 },164 .flags = CLK_MUX_ROUND_CLOSEST,165 },166 .hw.init = &(struct clk_init_data){167 .name = "ao_cts_rtc_oscin",168 .ops = &clk_regmap_mux_ops,169 .parent_data = (const struct clk_parent_data []) {170 { .fw_name = "ext-32k-0", },171 { .fw_name = "ext-32k-1", },172 { .fw_name = "ext-32k-2", },173 { .hw = &ao_32k.hw },174 },175 .num_parents = 4,176 .flags = CLK_SET_RATE_PARENT,177 },178};179 180static struct clk_regmap ao_clk81 = {181 .data = &(struct clk_regmap_mux_data) {182 .offset = AO_RTI_PWR_CNTL_REG0,183 .mask = 0x1,184 .shift = 0,185 .flags = CLK_MUX_ROUND_CLOSEST,186 },187 .hw.init = &(struct clk_init_data){188 .name = "ao_clk81",189 .ops = &clk_regmap_mux_ro_ops,190 .parent_data = (const struct clk_parent_data []) {191 { .fw_name = "mpeg-clk", },192 { .hw = &ao_cts_rtc_oscin.hw },193 },194 .num_parents = 2,195 .flags = CLK_SET_RATE_PARENT,196 },197};198 199static struct clk_regmap ao_cts_cec = {200 .data = &(struct clk_regmap_mux_data) {201 .offset = AO_CRT_CLK_CNTL1,202 .mask = 0x1,203 .shift = 27,204 .flags = CLK_MUX_ROUND_CLOSEST,205 },206 .hw.init = &(struct clk_init_data){207 .name = "ao_cts_cec",208 .ops = &clk_regmap_mux_ops,209 /*210 * FIXME: The 'fixme' parent obviously does not exist.211 *212 * ATM, CCF won't call get_parent() if num_parents is 1. It213 * does not allow NULL as a parent name either.214 *215 * On this particular mux, we only know the input #1 parent216 * but, on boot, unknown input #0 is set, so it is critical217 * to call .get_parent() on it218 *219 * Until CCF gets fixed, adding this fake parent that won't220 * ever be registered should work around the problem221 */222 .parent_data = (const struct clk_parent_data []) {223 { .name = "fixme", .index = -1, },224 { .hw = &ao_cts_rtc_oscin.hw },225 },226 .num_parents = 2,227 .flags = CLK_SET_RATE_PARENT,228 },229};230 231static const unsigned int gxbb_aoclk_reset[] = {232 [RESET_AO_REMOTE] = 16,233 [RESET_AO_I2C_MASTER] = 18,234 [RESET_AO_I2C_SLAVE] = 19,235 [RESET_AO_UART1] = 17,236 [RESET_AO_UART2] = 22,237 [RESET_AO_IR_BLASTER] = 23,238};239 240static struct clk_regmap *gxbb_aoclk[] = {241 &remote_ao,242 &i2c_master_ao,243 &i2c_slave_ao,244 &uart1_ao,245 &uart2_ao,246 &ir_blaster_ao,247 &ao_cts_oscin,248 &ao_32k_pre,249 &ao_32k_div,250 &ao_32k_sel,251 &ao_32k,252 &ao_cts_rtc_oscin,253 &ao_clk81,254 &ao_cts_cec,255};256 257static struct clk_hw *gxbb_aoclk_hw_clks[] = {258 [CLKID_AO_REMOTE] = &remote_ao.hw,259 [CLKID_AO_I2C_MASTER] = &i2c_master_ao.hw,260 [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao.hw,261 [CLKID_AO_UART1] = &uart1_ao.hw,262 [CLKID_AO_UART2] = &uart2_ao.hw,263 [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw,264 [CLKID_AO_CEC_32K] = &ao_cts_cec.hw,265 [CLKID_AO_CTS_OSCIN] = &ao_cts_oscin.hw,266 [CLKID_AO_32K_PRE] = &ao_32k_pre.hw,267 [CLKID_AO_32K_DIV] = &ao_32k_div.hw,268 [CLKID_AO_32K_SEL] = &ao_32k_sel.hw,269 [CLKID_AO_32K] = &ao_32k.hw,270 [CLKID_AO_CTS_RTC_OSCIN] = &ao_cts_rtc_oscin.hw,271 [CLKID_AO_CLK81] = &ao_clk81.hw,272};273 274static const struct meson_aoclk_data gxbb_aoclkc_data = {275 .reset_reg = AO_RTI_GEN_CNTL_REG0,276 .num_reset = ARRAY_SIZE(gxbb_aoclk_reset),277 .reset = gxbb_aoclk_reset,278 .num_clks = ARRAY_SIZE(gxbb_aoclk),279 .clks = gxbb_aoclk,280 .hw_clks = {281 .hws = gxbb_aoclk_hw_clks,282 .num = ARRAY_SIZE(gxbb_aoclk_hw_clks),283 },284};285 286static const struct of_device_id gxbb_aoclkc_match_table[] = {287 {288 .compatible = "amlogic,meson-gx-aoclkc",289 .data = &gxbb_aoclkc_data,290 },291 { }292};293MODULE_DEVICE_TABLE(of, gxbb_aoclkc_match_table);294 295static struct platform_driver gxbb_aoclkc_driver = {296 .probe = meson_aoclkc_probe,297 .driver = {298 .name = "gxbb-aoclkc",299 .of_match_table = gxbb_aoclkc_match_table,300 },301};302module_platform_driver(gxbb_aoclkc_driver);303 304MODULE_DESCRIPTION("Amlogic GXBB Always-ON Clock Controller driver");305MODULE_LICENSE("GPL");306MODULE_IMPORT_NS(CLK_MESON);307