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