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) 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 offsets 26 * Register offsets from the data sheet must be multiplied by 4. 27 */ 28 #define AO_RTI_PWR_CNTL_REG1 0x0C 29 #define AO_RTI_PWR_CNTL_REG0 0x10 30 #define AO_RTI_GEN_CNTL_REG0 0x40 31 #define AO_OSCIN_CNTL 0x58 32 #define AO_CRT_CLK_CNTL1 0x68 33 #define AO_SAR_CLK 0x90 34 #define AO_RTC_ALT_CLK_CNTL0 0x94 35 #define AO_RTC_ALT_CLK_CNTL1 0x98 36 37 static const struct clk_parent_data axg_ao_pclk_parents = { .fw_name = "mpeg-clk" }; 38 39 #define AXG_AO_GATE(_name, _bit, _flags) \ 40 MESON_PCLK(axg_ao_##_name, AO_RTI_GEN_CNTL_REG0, _bit, \ 41 &axg_ao_pclk_parents, _flags) 42 43 static AXG_AO_GATE(remote, 0, CLK_IGNORE_UNUSED); 44 static AXG_AO_GATE(i2c_master, 1, CLK_IGNORE_UNUSED); 45 static AXG_AO_GATE(i2c_slave, 2, CLK_IGNORE_UNUSED); 46 static AXG_AO_GATE(uart1, 3, CLK_IGNORE_UNUSED); 47 static AXG_AO_GATE(uart2, 5, CLK_IGNORE_UNUSED); 48 static AXG_AO_GATE(ir_blaster, 6, CLK_IGNORE_UNUSED); 49 static AXG_AO_GATE(saradc, 7, CLK_IGNORE_UNUSED); 50 51 static struct clk_regmap axg_ao_cts_oscin = { 52 .data = &(struct clk_regmap_gate_data){ 53 .offset = AO_RTI_PWR_CNTL_REG0, 54 .bit_idx = 14, 55 }, 56 .hw.init = &(struct clk_init_data){ 57 .name = "cts_oscin", 58 .ops = &clk_regmap_gate_ro_ops, 59 .parent_data = &(const struct clk_parent_data) { 60 .fw_name = "xtal", 61 }, 62 .num_parents = 1, 63 }, 64 }; 65 66 static struct clk_regmap axg_ao_32k_pre = { 67 .data = &(struct clk_regmap_gate_data){ 68 .offset = AO_RTC_ALT_CLK_CNTL0, 69 .bit_idx = 31, 70 }, 71 .hw.init = &(struct clk_init_data){ 72 .name = "axg_ao_32k_pre", 73 .ops = &clk_regmap_gate_ops, 74 .parent_hws = (const struct clk_hw *[]) { 75 &axg_ao_cts_oscin.hw 76 }, 77 .num_parents = 1, 78 }, 79 }; 80 81 static const struct meson_clk_dualdiv_param axg_32k_div_table[] = { 82 { 83 .dual = 1, 84 .n1 = 733, 85 .m1 = 8, 86 .n2 = 732, 87 .m2 = 11, 88 }, {} 89 }; 90 91 static struct clk_regmap axg_ao_32k_div = { 92 .data = &(struct meson_clk_dualdiv_data){ 93 .n1 = { 94 .reg_off = AO_RTC_ALT_CLK_CNTL0, 95 .shift = 0, 96 .width = 12, 97 }, 98 .n2 = { 99 .reg_off = AO_RTC_ALT_CLK_CNTL0, 100 .shift = 12, 101 .width = 12, 102 }, 103 .m1 = { 104 .reg_off = AO_RTC_ALT_CLK_CNTL1, 105 .shift = 0, 106 .width = 12, 107 }, 108 .m2 = { 109 .reg_off = AO_RTC_ALT_CLK_CNTL1, 110 .shift = 12, 111 .width = 12, 112 }, 113 .dual = { 114 .reg_off = AO_RTC_ALT_CLK_CNTL0, 115 .shift = 28, 116 .width = 1, 117 }, 118 .table = axg_32k_div_table, 119 }, 120 .hw.init = &(struct clk_init_data){ 121 .name = "axg_ao_32k_div", 122 .ops = &meson_clk_dualdiv_ops, 123 .parent_hws = (const struct clk_hw *[]) { 124 &axg_ao_32k_pre.hw 125 }, 126 .num_parents = 1, 127 }, 128 }; 129 130 static struct clk_regmap axg_ao_32k_sel = { 131 .data = &(struct clk_regmap_mux_data) { 132 .offset = AO_RTC_ALT_CLK_CNTL1, 133 .mask = 0x1, 134 .shift = 24, 135 .flags = CLK_MUX_ROUND_CLOSEST, 136 }, 137 .hw.init = &(struct clk_init_data){ 138 .name = "axg_ao_32k_sel", 139 .ops = &clk_regmap_mux_ops, 140 .parent_hws = (const struct clk_hw *[]) { 141 &axg_ao_32k_div.hw, 142 &axg_ao_32k_pre.hw, 143 }, 144 .num_parents = 2, 145 .flags = CLK_SET_RATE_PARENT, 146 }, 147 }; 148 149 static struct clk_regmap axg_ao_32k = { 150 .data = &(struct clk_regmap_gate_data){ 151 .offset = AO_RTC_ALT_CLK_CNTL0, 152 .bit_idx = 30, 153 }, 154 .hw.init = &(struct clk_init_data){ 155 .name = "axg_ao_32k", 156 .ops = &clk_regmap_gate_ops, 157 .parent_hws = (const struct clk_hw *[]) { 158 &axg_ao_32k_sel.hw 159 }, 160 .num_parents = 1, 161 .flags = CLK_SET_RATE_PARENT, 162 }, 163 }; 164 165 static struct clk_regmap axg_ao_cts_rtc_oscin = { 166 .data = &(struct clk_regmap_mux_data) { 167 .offset = AO_RTI_PWR_CNTL_REG0, 168 .mask = 0x1, 169 .shift = 10, 170 .flags = CLK_MUX_ROUND_CLOSEST, 171 }, 172 .hw.init = &(struct clk_init_data){ 173 .name = "axg_ao_cts_rtc_oscin", 174 .ops = &clk_regmap_mux_ops, 175 .parent_data = (const struct clk_parent_data []) { 176 { .hw = &axg_ao_32k.hw }, 177 { .fw_name = "ext_32k-0", }, 178 }, 179 .num_parents = 2, 180 .flags = CLK_SET_RATE_PARENT, 181 }, 182 }; 183 184 static struct clk_regmap axg_ao_clk81 = { 185 .data = &(struct clk_regmap_mux_data) { 186 .offset = AO_RTI_PWR_CNTL_REG0, 187 .mask = 0x1, 188 .shift = 8, 189 .flags = CLK_MUX_ROUND_CLOSEST, 190 }, 191 .hw.init = &(struct clk_init_data){ 192 /* 193 * NOTE: this is one of the infamous clock the pwm driver 194 * can request directly by its global name. It's wrong but 195 * there is not much we can do about it until the support 196 * for the old pwm bindings is dropped 197 */ 198 .name = "axg_ao_clk81", 199 .ops = &clk_regmap_mux_ro_ops, 200 .parent_data = (const struct clk_parent_data []) { 201 { .fw_name = "mpeg-clk", }, 202 { .hw = &axg_ao_cts_rtc_oscin.hw }, 203 }, 204 .num_parents = 2, 205 .flags = CLK_SET_RATE_PARENT, 206 }, 207 }; 208 209 static struct clk_regmap axg_ao_saradc_mux = { 210 .data = &(struct clk_regmap_mux_data) { 211 .offset = AO_SAR_CLK, 212 .mask = 0x3, 213 .shift = 9, 214 }, 215 .hw.init = &(struct clk_init_data){ 216 .name = "ao_saradc_mux", 217 .ops = &clk_regmap_mux_ops, 218 .parent_data = (const struct clk_parent_data []) { 219 { .fw_name = "xtal", }, 220 { .hw = &axg_ao_clk81.hw }, 221 }, 222 .num_parents = 2, 223 }, 224 }; 225 226 static struct clk_regmap axg_ao_saradc_div = { 227 .data = &(struct clk_regmap_div_data) { 228 .offset = AO_SAR_CLK, 229 .shift = 0, 230 .width = 8, 231 }, 232 .hw.init = &(struct clk_init_data){ 233 .name = "ao_saradc_div", 234 .ops = &clk_regmap_divider_ops, 235 .parent_hws = (const struct clk_hw *[]) { 236 &axg_ao_saradc_mux.hw 237 }, 238 .num_parents = 1, 239 .flags = CLK_SET_RATE_PARENT, 240 }, 241 }; 242 243 static struct clk_regmap axg_ao_saradc_gate = { 244 .data = &(struct clk_regmap_gate_data) { 245 .offset = AO_SAR_CLK, 246 .bit_idx = 8, 247 }, 248 .hw.init = &(struct clk_init_data){ 249 .name = "ao_saradc_gate", 250 .ops = &clk_regmap_gate_ops, 251 .parent_hws = (const struct clk_hw *[]) { 252 &axg_ao_saradc_div.hw 253 }, 254 .num_parents = 1, 255 .flags = CLK_SET_RATE_PARENT, 256 }, 257 }; 258 259 static const unsigned int axg_ao_reset[] = { 260 [RESET_AO_REMOTE] = 16, 261 [RESET_AO_I2C_MASTER] = 18, 262 [RESET_AO_I2C_SLAVE] = 19, 263 [RESET_AO_UART1] = 17, 264 [RESET_AO_UART2] = 22, 265 [RESET_AO_IR_BLASTER] = 23, 266 }; 267 268 static struct clk_hw *axg_ao_hw_clks[] = { 269 [CLKID_AO_REMOTE] = &axg_ao_remote.hw, 270 [CLKID_AO_I2C_MASTER] = &axg_ao_i2c_master.hw, 271 [CLKID_AO_I2C_SLAVE] = &axg_ao_i2c_slave.hw, 272 [CLKID_AO_UART1] = &axg_ao_uart1.hw, 273 [CLKID_AO_UART2] = &axg_ao_uart2.hw, 274 [CLKID_AO_IR_BLASTER] = &axg_ao_ir_blaster.hw, 275 [CLKID_AO_SAR_ADC] = &axg_ao_saradc.hw, 276 [CLKID_AO_CLK81] = &axg_ao_clk81.hw, 277 [CLKID_AO_SAR_ADC_SEL] = &axg_ao_saradc_mux.hw, 278 [CLKID_AO_SAR_ADC_DIV] = &axg_ao_saradc_div.hw, 279 [CLKID_AO_SAR_ADC_CLK] = &axg_ao_saradc_gate.hw, 280 [CLKID_AO_CTS_OSCIN] = &axg_ao_cts_oscin.hw, 281 [CLKID_AO_32K_PRE] = &axg_ao_32k_pre.hw, 282 [CLKID_AO_32K_DIV] = &axg_ao_32k_div.hw, 283 [CLKID_AO_32K_SEL] = &axg_ao_32k_sel.hw, 284 [CLKID_AO_32K] = &axg_ao_32k.hw, 285 [CLKID_AO_CTS_RTC_OSCIN] = &axg_ao_cts_rtc_oscin.hw, 286 }; 287 288 static const struct meson_aoclk_data axg_ao_clkc_data = { 289 .reset_reg = AO_RTI_GEN_CNTL_REG0, 290 .num_reset = ARRAY_SIZE(axg_ao_reset), 291 .reset = axg_ao_reset, 292 .clkc_data = { 293 .hw_clks = { 294 .hws = axg_ao_hw_clks, 295 .num = ARRAY_SIZE(axg_ao_hw_clks), 296 }, 297 }, 298 }; 299 300 static const struct of_device_id axg_ao_clkc_match_table[] = { 301 { 302 .compatible = "amlogic,meson-axg-aoclkc", 303 .data = &axg_ao_clkc_data.clkc_data, 304 }, 305 { } 306 }; 307 MODULE_DEVICE_TABLE(of, axg_ao_clkc_match_table); 308 309 static struct platform_driver axg_ao_clkc_driver = { 310 .probe = meson_aoclkc_probe, 311 .driver = { 312 .name = "axg-ao-clkc", 313 .of_match_table = axg_ao_clkc_match_table, 314 }, 315 }; 316 module_platform_driver(axg_ao_clkc_driver); 317 318 MODULE_DESCRIPTION("Amlogic AXG Always-ON Clock Controller driver"); 319 MODULE_LICENSE("GPL"); 320 MODULE_IMPORT_NS("CLK_MESON"); 321