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