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 #define AXG_AO_GATE(_name, _bit) \ 38 static 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 54 AXG_AO_GATE(remote, 0); 55 AXG_AO_GATE(i2c_master, 1); 56 AXG_AO_GATE(i2c_slave, 2); 57 AXG_AO_GATE(uart1, 3); 58 AXG_AO_GATE(uart2, 5); 59 AXG_AO_GATE(ir_blaster, 6); 60 AXG_AO_GATE(saradc, 7); 61 62 static 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 77 static 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.hw 87 }, 88 .num_parents = 1, 89 }, 90 }; 91 92 static 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 102 static 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.hw 136 }, 137 .num_parents = 1, 138 }, 139 }; 140 141 static 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 160 static 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.hw 170 }, 171 .num_parents = 1, 172 .flags = CLK_SET_RATE_PARENT, 173 }, 174 }; 175 176 static 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 195 static 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 214 static 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 231 static 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.hw 242 }, 243 .num_parents = 1, 244 .flags = CLK_SET_RATE_PARENT, 245 }, 246 }; 247 248 static 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.hw 258 }, 259 .num_parents = 1, 260 .flags = CLK_SET_RATE_PARENT, 261 }, 262 }; 263 264 static 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 273 static 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 293 static 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 313 static 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 325 static const struct of_device_id axg_aoclkc_match_table[] = { 326 { 327 .compatible = "amlogic,meson-axg-aoclkc", 328 .data = &axg_aoclkc_data, 329 }, 330 { } 331 }; 332 MODULE_DEVICE_TABLE(of, axg_aoclkc_match_table); 333 334 static 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 }; 341 342 module_platform_driver(axg_aoclkc_driver); 343 MODULE_LICENSE("GPL v2"); 344