Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Bitmain BM1880 SoC clock driver |
| 4 | * |
| 5 | * Copyright (c) 2019 Linaro Ltd. |
| 6 | * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> |
| 7 | */ |
| 8 | |
| 9 | #include <linux/clk-provider.h> |
| 10 | #include <linux/kernel.h> |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/of_address.h> |
| 13 | #include <linux/of_device.h> |
| 14 | #include <linux/platform_device.h> |
| 15 | #include <linux/slab.h> |
| 16 | |
| 17 | #include <dt-bindings/clock/bm1880-clock.h> |
| 18 | |
| 19 | #define BM1880_CLK_MPLL_CTL 0x00 |
| 20 | #define BM1880_CLK_SPLL_CTL 0x04 |
| 21 | #define BM1880_CLK_FPLL_CTL 0x08 |
| 22 | #define BM1880_CLK_DDRPLL_CTL 0x0c |
| 23 | |
| 24 | #define BM1880_CLK_ENABLE0 0x00 |
| 25 | #define BM1880_CLK_ENABLE1 0x04 |
| 26 | #define BM1880_CLK_SELECT 0x20 |
| 27 | #define BM1880_CLK_DIV0 0x40 |
| 28 | #define BM1880_CLK_DIV1 0x44 |
| 29 | #define BM1880_CLK_DIV2 0x48 |
| 30 | #define BM1880_CLK_DIV3 0x4c |
| 31 | #define BM1880_CLK_DIV4 0x50 |
| 32 | #define BM1880_CLK_DIV5 0x54 |
| 33 | #define BM1880_CLK_DIV6 0x58 |
| 34 | #define BM1880_CLK_DIV7 0x5c |
| 35 | #define BM1880_CLK_DIV8 0x60 |
| 36 | #define BM1880_CLK_DIV9 0x64 |
| 37 | #define BM1880_CLK_DIV10 0x68 |
| 38 | #define BM1880_CLK_DIV11 0x6c |
| 39 | #define BM1880_CLK_DIV12 0x70 |
| 40 | #define BM1880_CLK_DIV13 0x74 |
| 41 | #define BM1880_CLK_DIV14 0x78 |
| 42 | #define BM1880_CLK_DIV15 0x7c |
| 43 | #define BM1880_CLK_DIV16 0x80 |
| 44 | #define BM1880_CLK_DIV17 0x84 |
| 45 | #define BM1880_CLK_DIV18 0x88 |
| 46 | #define BM1880_CLK_DIV19 0x8c |
| 47 | #define BM1880_CLK_DIV20 0x90 |
| 48 | #define BM1880_CLK_DIV21 0x94 |
| 49 | #define BM1880_CLK_DIV22 0x98 |
| 50 | #define BM1880_CLK_DIV23 0x9c |
| 51 | #define BM1880_CLK_DIV24 0xa0 |
| 52 | #define BM1880_CLK_DIV25 0xa4 |
| 53 | #define BM1880_CLK_DIV26 0xa8 |
| 54 | #define BM1880_CLK_DIV27 0xac |
| 55 | #define BM1880_CLK_DIV28 0xb0 |
| 56 | |
| 57 | #define to_bm1880_pll_clk(_hw) container_of(_hw, struct bm1880_pll_hw_clock, hw) |
| 58 | #define to_bm1880_div_clk(_hw) container_of(_hw, struct bm1880_div_hw_clock, hw) |
| 59 | |
| 60 | static DEFINE_SPINLOCK(bm1880_clk_lock); |
| 61 | |
| 62 | struct bm1880_clock_data { |
| 63 | void __iomem *pll_base; |
| 64 | void __iomem *sys_base; |
| 65 | struct clk_hw_onecell_data hw_data; |
| 66 | }; |
| 67 | |
| 68 | struct bm1880_gate_clock { |
| 69 | unsigned int id; |
| 70 | const char *name; |
| 71 | const char *parent; |
| 72 | u32 gate_reg; |
| 73 | s8 gate_shift; |
| 74 | unsigned long flags; |
| 75 | }; |
| 76 | |
| 77 | struct bm1880_mux_clock { |
| 78 | unsigned int id; |
| 79 | const char *name; |
| 80 | const char * const *parents; |
| 81 | s8 num_parents; |
| 82 | u32 reg; |
| 83 | s8 shift; |
| 84 | unsigned long flags; |
| 85 | }; |
| 86 | |
| 87 | struct bm1880_div_clock { |
| 88 | unsigned int id; |
| 89 | const char *name; |
| 90 | u32 reg; |
| 91 | u8 shift; |
| 92 | u8 width; |
| 93 | u32 initval; |
| 94 | const struct clk_div_table *table; |
| 95 | unsigned long flags; |
| 96 | }; |
| 97 | |
| 98 | struct bm1880_div_hw_clock { |
| 99 | struct bm1880_div_clock div; |
| 100 | void __iomem *base; |
| 101 | spinlock_t *lock; |
| 102 | struct clk_hw hw; |
| 103 | struct clk_init_data init; |
| 104 | }; |
| 105 | |
| 106 | struct bm1880_composite_clock { |
| 107 | unsigned int id; |
| 108 | const char *name; |
| 109 | const char *parent; |
| 110 | const char * const *parents; |
| 111 | unsigned int num_parents; |
| 112 | unsigned long flags; |
| 113 | |
| 114 | u32 gate_reg; |
| 115 | u32 mux_reg; |
| 116 | u32 div_reg; |
| 117 | |
| 118 | s8 gate_shift; |
| 119 | s8 mux_shift; |
| 120 | s8 div_shift; |
| 121 | s8 div_width; |
| 122 | s16 div_initval; |
| 123 | const struct clk_div_table *table; |
| 124 | }; |
| 125 | |
| 126 | struct bm1880_pll_clock { |
| 127 | unsigned int id; |
| 128 | const char *name; |
| 129 | u32 reg; |
| 130 | unsigned long flags; |
| 131 | }; |
| 132 | |
| 133 | struct bm1880_pll_hw_clock { |
| 134 | struct bm1880_pll_clock pll; |
| 135 | void __iomem *base; |
| 136 | struct clk_hw hw; |
| 137 | struct clk_init_data init; |
| 138 | }; |
| 139 | |
| 140 | static const struct clk_ops bm1880_pll_ops; |
| 141 | static const struct clk_ops bm1880_clk_div_ops; |
| 142 | |
| 143 | #define GATE_DIV(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \ |
| 144 | _div_shift, _div_width, _div_initval, _table, \ |
| 145 | _flags) { \ |
| 146 | .id = _id, \ |
| 147 | .parent = _parent, \ |
| 148 | .name = _name, \ |
| 149 | .gate_reg = _gate_reg, \ |
| 150 | .gate_shift = _gate_shift, \ |
| 151 | .div_reg = _div_reg, \ |
| 152 | .div_shift = _div_shift, \ |
| 153 | .div_width = _div_width, \ |
| 154 | .div_initval = _div_initval, \ |
| 155 | .table = _table, \ |
| 156 | .mux_shift = -1, \ |
| 157 | .flags = _flags, \ |
| 158 | } |
| 159 | |
| 160 | #define GATE_MUX(_id, _name, _parents, _gate_reg, _gate_shift, \ |
| 161 | _mux_reg, _mux_shift, _flags) { \ |
| 162 | .id = _id, \ |
| 163 | .parents = _parents, \ |
| 164 | .num_parents = ARRAY_SIZE(_parents), \ |
| 165 | .name = _name, \ |
| 166 | .gate_reg = _gate_reg, \ |
| 167 | .gate_shift = _gate_shift, \ |
| 168 | .div_shift = -1, \ |
| 169 | .mux_reg = _mux_reg, \ |
| 170 | .mux_shift = _mux_shift, \ |
| 171 | .flags = _flags, \ |
| 172 | } |
| 173 | |
| 174 | #define CLK_PLL(_id, _name, _parent, _reg, _flags) { \ |
| 175 | .pll.id = _id, \ |
| 176 | .pll.name = _name, \ |
| 177 | .pll.reg = _reg, \ |
| 178 | .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, \ |
| 179 | &bm1880_pll_ops, \ |
| 180 | _flags), \ |
| 181 | } |
| 182 | |
| 183 | #define CLK_DIV(_id, _name, _parent, _reg, _shift, _width, _initval, \ |
| 184 | _table, _flags) { \ |
| 185 | .div.id = _id, \ |
| 186 | .div.name = _name, \ |
| 187 | .div.reg = _reg, \ |
| 188 | .div.shift = _shift, \ |
| 189 | .div.width = _width, \ |
| 190 | .div.initval = _initval, \ |
| 191 | .div.table = _table, \ |
| 192 | .hw.init = CLK_HW_INIT_HW(_name, _parent, \ |
| 193 | &bm1880_clk_div_ops, \ |
| 194 | _flags), \ |
| 195 | } |
| 196 | |
| 197 | static struct clk_parent_data bm1880_pll_parent[] = { |
| 198 | { .fw_name = "osc", .name = "osc" }, |
| 199 | }; |
| 200 | |
| 201 | /* |
| 202 | * All PLL clocks are marked as CRITICAL, hence they are very crucial |
| 203 | * for the functioning of the SoC |
| 204 | */ |
| 205 | static struct bm1880_pll_hw_clock bm1880_pll_clks[] = { |
| 206 | CLK_PLL(BM1880_CLK_MPLL, "clk_mpll", bm1880_pll_parent, |
| 207 | BM1880_CLK_MPLL_CTL, 0), |
| 208 | CLK_PLL(BM1880_CLK_SPLL, "clk_spll", bm1880_pll_parent, |
| 209 | BM1880_CLK_SPLL_CTL, 0), |
| 210 | CLK_PLL(BM1880_CLK_FPLL, "clk_fpll", bm1880_pll_parent, |
| 211 | BM1880_CLK_FPLL_CTL, 0), |
| 212 | CLK_PLL(BM1880_CLK_DDRPLL, "clk_ddrpll", bm1880_pll_parent, |
| 213 | BM1880_CLK_DDRPLL_CTL, 0), |
| 214 | }; |
| 215 | |
| 216 | /* |
| 217 | * Clocks marked as CRITICAL are needed for the proper functioning |
| 218 | * of the SoC. |
| 219 | */ |
| 220 | static const struct bm1880_gate_clock bm1880_gate_clks[] = { |
| 221 | { BM1880_CLK_AHB_ROM, "clk_ahb_rom", "clk_mux_axi6", |
| 222 | BM1880_CLK_ENABLE0, 2, 0 }, |
| 223 | { BM1880_CLK_AXI_SRAM, "clk_axi_sram", "clk_axi1", |
| 224 | BM1880_CLK_ENABLE0, 3, 0 }, |
| 225 | /* |
| 226 | * Since this clock is sourcing the DDR memory, let's mark it as |
| 227 | * critical to avoid gating. |
| 228 | */ |
| 229 | { BM1880_CLK_DDR_AXI, "clk_ddr_axi", "clk_mux_axi6", |
| 230 | BM1880_CLK_ENABLE0, 4, CLK_IS_CRITICAL }, |
| 231 | { BM1880_CLK_APB_EFUSE, "clk_apb_efuse", "clk_mux_axi6", |
| 232 | BM1880_CLK_ENABLE0, 6, 0 }, |
| 233 | { BM1880_CLK_AXI5_EMMC, "clk_axi5_emmc", "clk_axi5", |
| 234 | BM1880_CLK_ENABLE0, 7, 0 }, |
| 235 | { BM1880_CLK_AXI5_SD, "clk_axi5_sd", "clk_axi5", |
| 236 | BM1880_CLK_ENABLE0, 10, 0 }, |
| 237 | { BM1880_CLK_AXI4_ETH0, "clk_axi4_eth0", "clk_axi4", |
| 238 | BM1880_CLK_ENABLE0, 14, 0 }, |
| 239 | { BM1880_CLK_AXI4_ETH1, "clk_axi4_eth1", "clk_axi4", |
| 240 | BM1880_CLK_ENABLE0, 16, 0 }, |
| 241 | { BM1880_CLK_AXI1_GDMA, "clk_axi1_gdma", "clk_axi1", |
| 242 | BM1880_CLK_ENABLE0, 17, 0 }, |
| 243 | /* Don't gate GPIO clocks as it is not owned by the GPIO driver */ |
| 244 | { BM1880_CLK_APB_GPIO, "clk_apb_gpio", "clk_mux_axi6", |
| 245 | BM1880_CLK_ENABLE0, 18, CLK_IGNORE_UNUSED }, |
| 246 | { BM1880_CLK_APB_GPIO_INTR, "clk_apb_gpio_intr", "clk_mux_axi6", |
| 247 | BM1880_CLK_ENABLE0, 19, CLK_IGNORE_UNUSED }, |
| 248 | { BM1880_CLK_AXI1_MINER, "clk_axi1_miner", "clk_axi1", |
| 249 | BM1880_CLK_ENABLE0, 21, 0 }, |
| 250 | { BM1880_CLK_AHB_SF, "clk_ahb_sf", "clk_mux_axi6", |
| 251 | BM1880_CLK_ENABLE0, 22, 0 }, |
| 252 | /* |
| 253 | * Not sure which module this clock is sourcing but gating this clock |
| 254 | * prevents the system from booting. So, let's mark it as critical. |
| 255 | */ |
| 256 | { BM1880_CLK_SDMA_AXI, "clk_sdma_axi", "clk_axi5", |
| 257 | BM1880_CLK_ENABLE0, 23, CLK_IS_CRITICAL }, |
| 258 | { BM1880_CLK_APB_I2C, "clk_apb_i2c", "clk_mux_axi6", |
| 259 | BM1880_CLK_ENABLE0, 25, 0 }, |
| 260 | { BM1880_CLK_APB_WDT, "clk_apb_wdt", "clk_mux_axi6", |
| 261 | BM1880_CLK_ENABLE0, 26, 0 }, |
| 262 | { BM1880_CLK_APB_JPEG, "clk_apb_jpeg", "clk_axi6", |
| 263 | BM1880_CLK_ENABLE0, 27, 0 }, |
| 264 | { BM1880_CLK_AXI5_NF, "clk_axi5_nf", "clk_axi5", |
| 265 | BM1880_CLK_ENABLE0, 29, 0 }, |
| 266 | { BM1880_CLK_APB_NF, "clk_apb_nf", "clk_axi6", |
| 267 | BM1880_CLK_ENABLE0, 30, 0 }, |
| 268 | { BM1880_CLK_APB_PWM, "clk_apb_pwm", "clk_mux_axi6", |
| 269 | BM1880_CLK_ENABLE1, 0, 0 }, |
| 270 | { BM1880_CLK_RV, "clk_rv", "clk_mux_rv", |
| 271 | BM1880_CLK_ENABLE1, 1, 0 }, |
| 272 | { BM1880_CLK_APB_SPI, "clk_apb_spi", "clk_mux_axi6", |
| 273 | BM1880_CLK_ENABLE1, 2, 0 }, |
| 274 | { BM1880_CLK_UART_500M, "clk_uart_500m", "clk_div_uart_500m", |
| 275 | BM1880_CLK_ENABLE1, 4, 0 }, |
| 276 | { BM1880_CLK_APB_UART, "clk_apb_uart", "clk_axi6", |
| 277 | BM1880_CLK_ENABLE1, 5, 0 }, |
| 278 | { BM1880_CLK_APB_I2S, "clk_apb_i2s", "clk_axi6", |
| 279 | BM1880_CLK_ENABLE1, 6, 0 }, |
| 280 | { BM1880_CLK_AXI4_USB, "clk_axi4_usb", "clk_axi4", |
| 281 | BM1880_CLK_ENABLE1, 7, 0 }, |
| 282 | { BM1880_CLK_APB_USB, "clk_apb_usb", "clk_axi6", |
| 283 | BM1880_CLK_ENABLE1, 8, 0 }, |
| 284 | { BM1880_CLK_12M_USB, "clk_12m_usb", "clk_div_12m_usb", |
| 285 | BM1880_CLK_ENABLE1, 11, 0 }, |
| 286 | { BM1880_CLK_APB_VIDEO, "clk_apb_video", "clk_axi6", |
| 287 | BM1880_CLK_ENABLE1, 12, 0 }, |
| 288 | { BM1880_CLK_APB_VPP, "clk_apb_vpp", "clk_axi6", |
| 289 | BM1880_CLK_ENABLE1, 15, 0 }, |
| 290 | { BM1880_CLK_AXI6, "clk_axi6", "clk_mux_axi6", |
| 291 | BM1880_CLK_ENABLE1, 21, 0 }, |
| 292 | }; |
| 293 | |
| 294 | static const char * const clk_a53_parents[] = { "clk_spll", "clk_mpll" }; |
| 295 | static const char * const clk_rv_parents[] = { "clk_div_1_rv", "clk_div_0_rv" }; |
| 296 | static const char * const clk_axi1_parents[] = { "clk_div_1_axi1", "clk_div_0_axi1" }; |
| 297 | static const char * const clk_axi6_parents[] = { "clk_div_1_axi6", "clk_div_0_axi6" }; |
| 298 | |
| 299 | static const struct bm1880_mux_clock bm1880_mux_clks[] = { |
| 300 | { BM1880_CLK_MUX_RV, "clk_mux_rv", clk_rv_parents, 2, |
| 301 | BM1880_CLK_SELECT, 1, 0 }, |
| 302 | { BM1880_CLK_MUX_AXI6, "clk_mux_axi6", clk_axi6_parents, 2, |
| 303 | BM1880_CLK_SELECT, 3, 0 }, |
| 304 | }; |
| 305 | |
| 306 | static const struct clk_div_table bm1880_div_table_0[] = { |
| 307 | { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, |
| 308 | { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, |
| 309 | { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, |
| 310 | { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, |
| 311 | { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, |
| 312 | { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, |
| 313 | { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, |
| 314 | { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, |
| 315 | { 0, 0 } |
| 316 | }; |
| 317 | |
| 318 | static const struct clk_div_table bm1880_div_table_1[] = { |
| 319 | { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, |
| 320 | { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, |
| 321 | { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, |
| 322 | { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, |
| 323 | { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, |
| 324 | { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, |
| 325 | { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, |
| 326 | { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, |
| 327 | { 127, 128 }, { 0, 0 } |
| 328 | }; |
| 329 | |
| 330 | static const struct clk_div_table bm1880_div_table_2[] = { |
| 331 | { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, |
| 332 | { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, |
| 333 | { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, |
| 334 | { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, |
| 335 | { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, |
| 336 | { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, |
| 337 | { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, |
| 338 | { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, |
| 339 | { 127, 128 }, { 255, 256 }, { 0, 0 } |
| 340 | }; |
| 341 | |
| 342 | static const struct clk_div_table bm1880_div_table_3[] = { |
| 343 | { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, |
| 344 | { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, |
| 345 | { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, |
| 346 | { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, |
| 347 | { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, |
| 348 | { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, |
| 349 | { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, |
| 350 | { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, |
| 351 | { 127, 128 }, { 255, 256 }, { 511, 512 }, { 0, 0 } |
| 352 | }; |
| 353 | |
| 354 | static const struct clk_div_table bm1880_div_table_4[] = { |
| 355 | { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, |
| 356 | { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, |
| 357 | { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, |
| 358 | { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, |
| 359 | { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, |
| 360 | { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, |
| 361 | { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, |
| 362 | { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, |
| 363 | { 127, 128 }, { 255, 256 }, { 511, 512 }, { 65535, 65536 }, |
| 364 | { 0, 0 } |
| 365 | }; |
| 366 | |
| 367 | /* |
| 368 | * Clocks marked as CRITICAL are needed for the proper functioning |
| 369 | * of the SoC. |
| 370 | */ |
| 371 | static struct bm1880_div_hw_clock bm1880_div_clks[] = { |
| 372 | CLK_DIV(BM1880_CLK_DIV_0_RV, "clk_div_0_rv", &bm1880_pll_clks[1].hw, |
| 373 | BM1880_CLK_DIV12, 16, 5, 1, bm1880_div_table_0, 0), |
| 374 | CLK_DIV(BM1880_CLK_DIV_1_RV, "clk_div_1_rv", &bm1880_pll_clks[2].hw, |
| 375 | BM1880_CLK_DIV13, 16, 5, 1, bm1880_div_table_0, 0), |
| 376 | CLK_DIV(BM1880_CLK_DIV_UART_500M, "clk_div_uart_500m", &bm1880_pll_clks[2].hw, |
| 377 | BM1880_CLK_DIV15, 16, 7, 3, bm1880_div_table_1, 0), |
| 378 | CLK_DIV(BM1880_CLK_DIV_0_AXI1, "clk_div_0_axi1", &bm1880_pll_clks[0].hw, |
| 379 | BM1880_CLK_DIV21, 16, 5, 2, bm1880_div_table_0, |
| 380 | 0), |
| 381 | CLK_DIV(BM1880_CLK_DIV_1_AXI1, "clk_div_1_axi1", &bm1880_pll_clks[2].hw, |
| 382 | BM1880_CLK_DIV22, 16, 5, 3, bm1880_div_table_0, |
| 383 | 0), |
| 384 | CLK_DIV(BM1880_CLK_DIV_0_AXI6, "clk_div_0_axi6", &bm1880_pll_clks[2].hw, |
| 385 | BM1880_CLK_DIV27, 16, 5, 15, bm1880_div_table_0, |
| 386 | 0), |
| 387 | CLK_DIV(BM1880_CLK_DIV_1_AXI6, "clk_div_1_axi6", &bm1880_pll_clks[0].hw, |
| 388 | BM1880_CLK_DIV28, 16, 5, 11, bm1880_div_table_0, |
| 389 | 0), |
| 390 | CLK_DIV(BM1880_CLK_DIV_12M_USB, "clk_div_12m_usb", &bm1880_pll_clks[2].hw, |
| 391 | BM1880_CLK_DIV18, 16, 7, 125, bm1880_div_table_1, 0), |
| 392 | }; |
| 393 | |
| 394 | /* |
| 395 | * Clocks marked as CRITICAL are all needed for the proper functioning |
| 396 | * of the SoC. |
| 397 | */ |
| 398 | static struct bm1880_composite_clock bm1880_composite_clks[] = { |
| 399 | /* |
| 400 | * Since clk_a53 and clk_50m_a53 clocks are sourcing the CPU core, |
| 401 | * let's mark them as critical to avoid gating. |
| 402 | */ |
| 403 | GATE_MUX(BM1880_CLK_A53, "clk_a53", clk_a53_parents, |
| 404 | BM1880_CLK_ENABLE0, 0, BM1880_CLK_SELECT, 0, |
| 405 | CLK_IS_CRITICAL), |
| 406 | GATE_DIV(BM1880_CLK_50M_A53, "clk_50m_a53", "clk_fpll", |
| 407 | BM1880_CLK_ENABLE0, 1, BM1880_CLK_DIV0, 16, 5, 30, |
| 408 | bm1880_div_table_0, CLK_IS_CRITICAL), |
| 409 | GATE_DIV(BM1880_CLK_EFUSE, "clk_efuse", "clk_fpll", |
| 410 | BM1880_CLK_ENABLE0, 5, BM1880_CLK_DIV1, 16, 7, 60, |
| 411 | bm1880_div_table_1, 0), |
| 412 | GATE_DIV(BM1880_CLK_EMMC, "clk_emmc", "clk_fpll", |
| 413 | BM1880_CLK_ENABLE0, 8, BM1880_CLK_DIV2, 16, 5, 15, |
| 414 | bm1880_div_table_0, 0), |
| 415 | GATE_DIV(BM1880_CLK_100K_EMMC, "clk_100k_emmc", "clk_div_12m_usb", |
| 416 | BM1880_CLK_ENABLE0, 9, BM1880_CLK_DIV3, 16, 8, 120, |
| 417 | bm1880_div_table_2, 0), |
| 418 | GATE_DIV(BM1880_CLK_SD, "clk_sd", "clk_fpll", |
| 419 | BM1880_CLK_ENABLE0, 11, BM1880_CLK_DIV4, 16, 5, 15, |
| 420 | bm1880_div_table_0, 0), |
| 421 | GATE_DIV(BM1880_CLK_100K_SD, "clk_100k_sd", "clk_div_12m_usb", |
| 422 | BM1880_CLK_ENABLE0, 12, BM1880_CLK_DIV5, 16, 8, 120, |
| 423 | bm1880_div_table_2, 0), |
| 424 | GATE_DIV(BM1880_CLK_500M_ETH0, "clk_500m_eth0", "clk_fpll", |
| 425 | BM1880_CLK_ENABLE0, 13, BM1880_CLK_DIV6, 16, 5, 3, |
| 426 | bm1880_div_table_0, 0), |
| 427 | GATE_DIV(BM1880_CLK_500M_ETH1, "clk_500m_eth1", "clk_fpll", |
| 428 | BM1880_CLK_ENABLE0, 15, BM1880_CLK_DIV7, 16, 5, 3, |
| 429 | bm1880_div_table_0, 0), |
| 430 | /* Don't gate GPIO clocks as it is not owned by the GPIO driver */ |
| 431 | GATE_DIV(BM1880_CLK_GPIO_DB, "clk_gpio_db", "clk_div_12m_usb", |
| 432 | BM1880_CLK_ENABLE0, 20, BM1880_CLK_DIV8, 16, 16, 120, |
| 433 | bm1880_div_table_4, CLK_IGNORE_UNUSED), |
| 434 | GATE_DIV(BM1880_CLK_SDMA_AUD, "clk_sdma_aud", "clk_fpll", |
| 435 | BM1880_CLK_ENABLE0, 24, BM1880_CLK_DIV9, 16, 7, 61, |
| 436 | bm1880_div_table_1, 0), |
| 437 | GATE_DIV(BM1880_CLK_JPEG_AXI, "clk_jpeg_axi", "clk_fpll", |
| 438 | BM1880_CLK_ENABLE0, 28, BM1880_CLK_DIV10, 16, 5, 4, |
| 439 | bm1880_div_table_0, 0), |
| 440 | GATE_DIV(BM1880_CLK_NF, "clk_nf", "clk_fpll", |
| 441 | BM1880_CLK_ENABLE0, 31, BM1880_CLK_DIV11, 16, 5, 30, |
| 442 | bm1880_div_table_0, 0), |
| 443 | GATE_DIV(BM1880_CLK_TPU_AXI, "clk_tpu_axi", "clk_spll", |
| 444 | BM1880_CLK_ENABLE1, 3, BM1880_CLK_DIV14, 16, 5, 1, |
| 445 | bm1880_div_table_0, 0), |
| 446 | GATE_DIV(BM1880_CLK_125M_USB, "clk_125m_usb", "clk_fpll", |
| 447 | BM1880_CLK_ENABLE1, 9, BM1880_CLK_DIV16, 16, 5, 12, |
| 448 | bm1880_div_table_0, 0), |
| 449 | GATE_DIV(BM1880_CLK_33K_USB, "clk_33k_usb", "clk_div_12m_usb", |
| 450 | BM1880_CLK_ENABLE1, 10, BM1880_CLK_DIV17, 16, 9, 363, |
| 451 | bm1880_div_table_3, 0), |
| 452 | GATE_DIV(BM1880_CLK_VIDEO_AXI, "clk_video_axi", "clk_fpll", |
| 453 | BM1880_CLK_ENABLE1, 13, BM1880_CLK_DIV19, 16, 5, 4, |
| 454 | bm1880_div_table_0, 0), |
| 455 | GATE_DIV(BM1880_CLK_VPP_AXI, "clk_vpp_axi", "clk_fpll", |
| 456 | BM1880_CLK_ENABLE1, 14, BM1880_CLK_DIV20, 16, 5, 4, |
| 457 | bm1880_div_table_0, 0), |
| 458 | GATE_MUX(BM1880_CLK_AXI1, "clk_axi1", clk_axi1_parents, |
| 459 | BM1880_CLK_ENABLE1, 15, BM1880_CLK_SELECT, 2, 0), |
| 460 | GATE_DIV(BM1880_CLK_AXI2, "clk_axi2", "clk_fpll", |
| 461 | BM1880_CLK_ENABLE1, 17, BM1880_CLK_DIV23, 16, 5, 3, |
| 462 | bm1880_div_table_0, 0), |
| 463 | GATE_DIV(BM1880_CLK_AXI3, "clk_axi3", "clk_mux_rv", |
| 464 | BM1880_CLK_ENABLE1, 18, BM1880_CLK_DIV24, 16, 5, 2, |
| 465 | bm1880_div_table_0, 0), |
| 466 | GATE_DIV(BM1880_CLK_AXI4, "clk_axi4", "clk_fpll", |
| 467 | BM1880_CLK_ENABLE1, 19, BM1880_CLK_DIV25, 16, 5, 6, |
| 468 | bm1880_div_table_0, 0), |
| 469 | GATE_DIV(BM1880_CLK_AXI5, "clk_axi5", "clk_fpll", |
| 470 | BM1880_CLK_ENABLE1, 20, BM1880_CLK_DIV26, 16, 5, 15, |
| 471 | bm1880_div_table_0, 0), |
| 472 | }; |
| 473 | |
| 474 | static unsigned long bm1880_pll_rate_calc(u32 regval, unsigned long parent_rate) |
| 475 | { |
| 476 | u64 numerator; |
YueHaibing | 59ef4da | 2019-11-29 03:35:34 +0000 | [diff] [blame] | 477 | u32 fbdiv, refdiv; |
Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 478 | u32 postdiv1, postdiv2, denominator; |
| 479 | |
| 480 | fbdiv = (regval >> 16) & 0xfff; |
Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 481 | refdiv = regval & 0x1f; |
| 482 | postdiv1 = (regval >> 8) & 0x7; |
| 483 | postdiv2 = (regval >> 12) & 0x7; |
| 484 | |
| 485 | numerator = parent_rate * fbdiv; |
| 486 | denominator = refdiv * postdiv1 * postdiv2; |
| 487 | do_div(numerator, denominator); |
| 488 | |
| 489 | return (unsigned long)numerator; |
| 490 | } |
| 491 | |
| 492 | static unsigned long bm1880_pll_recalc_rate(struct clk_hw *hw, |
| 493 | unsigned long parent_rate) |
| 494 | { |
| 495 | struct bm1880_pll_hw_clock *pll_hw = to_bm1880_pll_clk(hw); |
| 496 | unsigned long rate; |
| 497 | u32 regval; |
| 498 | |
| 499 | regval = readl(pll_hw->base + pll_hw->pll.reg); |
| 500 | rate = bm1880_pll_rate_calc(regval, parent_rate); |
| 501 | |
| 502 | return rate; |
| 503 | } |
| 504 | |
| 505 | static const struct clk_ops bm1880_pll_ops = { |
| 506 | .recalc_rate = bm1880_pll_recalc_rate, |
| 507 | }; |
| 508 | |
| 509 | static struct clk_hw *bm1880_clk_register_pll(struct bm1880_pll_hw_clock *pll_clk, |
| 510 | void __iomem *sys_base) |
| 511 | { |
| 512 | struct clk_hw *hw; |
| 513 | int err; |
| 514 | |
| 515 | pll_clk->base = sys_base; |
| 516 | hw = &pll_clk->hw; |
| 517 | |
| 518 | err = clk_hw_register(NULL, hw); |
| 519 | if (err) |
| 520 | return ERR_PTR(err); |
| 521 | |
| 522 | return hw; |
| 523 | } |
| 524 | |
Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 525 | static int bm1880_clk_register_plls(struct bm1880_pll_hw_clock *clks, |
| 526 | int num_clks, |
| 527 | struct bm1880_clock_data *data) |
| 528 | { |
| 529 | struct clk_hw *hw; |
| 530 | void __iomem *pll_base = data->pll_base; |
| 531 | int i; |
| 532 | |
| 533 | for (i = 0; i < num_clks; i++) { |
| 534 | struct bm1880_pll_hw_clock *bm1880_clk = &clks[i]; |
| 535 | |
| 536 | hw = bm1880_clk_register_pll(bm1880_clk, pll_base); |
| 537 | if (IS_ERR(hw)) { |
| 538 | pr_err("%s: failed to register clock %s\n", |
| 539 | __func__, bm1880_clk->pll.name); |
| 540 | goto err_clk; |
| 541 | } |
| 542 | |
| 543 | data->hw_data.hws[clks[i].pll.id] = hw; |
| 544 | } |
| 545 | |
| 546 | return 0; |
| 547 | |
| 548 | err_clk: |
| 549 | while (i--) |
Conor Dooley | c861c1b | 2021-12-23 15:42:44 +0000 | [diff] [blame] | 550 | clk_hw_unregister(data->hw_data.hws[clks[i].pll.id]); |
Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 551 | |
| 552 | return PTR_ERR(hw); |
| 553 | } |
| 554 | |
| 555 | static int bm1880_clk_register_mux(const struct bm1880_mux_clock *clks, |
| 556 | int num_clks, |
| 557 | struct bm1880_clock_data *data) |
| 558 | { |
| 559 | struct clk_hw *hw; |
| 560 | void __iomem *sys_base = data->sys_base; |
| 561 | int i; |
| 562 | |
| 563 | for (i = 0; i < num_clks; i++) { |
| 564 | hw = clk_hw_register_mux(NULL, clks[i].name, |
| 565 | clks[i].parents, |
| 566 | clks[i].num_parents, |
| 567 | clks[i].flags, |
| 568 | sys_base + clks[i].reg, |
| 569 | clks[i].shift, 1, 0, |
| 570 | &bm1880_clk_lock); |
| 571 | if (IS_ERR(hw)) { |
| 572 | pr_err("%s: failed to register clock %s\n", |
| 573 | __func__, clks[i].name); |
| 574 | goto err_clk; |
| 575 | } |
| 576 | |
| 577 | data->hw_data.hws[clks[i].id] = hw; |
| 578 | } |
| 579 | |
| 580 | return 0; |
| 581 | |
| 582 | err_clk: |
| 583 | while (i--) |
| 584 | clk_hw_unregister_mux(data->hw_data.hws[clks[i].id]); |
| 585 | |
| 586 | return PTR_ERR(hw); |
| 587 | } |
| 588 | |
| 589 | static unsigned long bm1880_clk_div_recalc_rate(struct clk_hw *hw, |
| 590 | unsigned long parent_rate) |
| 591 | { |
| 592 | struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); |
| 593 | struct bm1880_div_clock *div = &div_hw->div; |
| 594 | void __iomem *reg_addr = div_hw->base + div->reg; |
| 595 | unsigned int val; |
| 596 | unsigned long rate; |
| 597 | |
| 598 | if (!(readl(reg_addr) & BIT(3))) { |
| 599 | val = div->initval; |
| 600 | } else { |
| 601 | val = readl(reg_addr) >> div->shift; |
| 602 | val &= clk_div_mask(div->width); |
| 603 | } |
| 604 | |
| 605 | rate = divider_recalc_rate(hw, parent_rate, val, div->table, |
| 606 | div->flags, div->width); |
| 607 | |
| 608 | return rate; |
| 609 | } |
| 610 | |
| 611 | static long bm1880_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, |
| 612 | unsigned long *prate) |
| 613 | { |
| 614 | struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); |
| 615 | struct bm1880_div_clock *div = &div_hw->div; |
| 616 | void __iomem *reg_addr = div_hw->base + div->reg; |
| 617 | |
| 618 | if (div->flags & CLK_DIVIDER_READ_ONLY) { |
| 619 | u32 val; |
| 620 | |
| 621 | val = readl(reg_addr) >> div->shift; |
| 622 | val &= clk_div_mask(div->width); |
| 623 | |
| 624 | return divider_ro_round_rate(hw, rate, prate, div->table, |
| 625 | div->width, div->flags, |
| 626 | val); |
| 627 | } |
| 628 | |
| 629 | return divider_round_rate(hw, rate, prate, div->table, |
| 630 | div->width, div->flags); |
| 631 | } |
| 632 | |
| 633 | static int bm1880_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, |
| 634 | unsigned long parent_rate) |
| 635 | { |
| 636 | struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); |
| 637 | struct bm1880_div_clock *div = &div_hw->div; |
| 638 | void __iomem *reg_addr = div_hw->base + div->reg; |
| 639 | unsigned long flags = 0; |
| 640 | int value; |
| 641 | u32 val; |
| 642 | |
| 643 | value = divider_get_val(rate, parent_rate, div->table, |
| 644 | div->width, div_hw->div.flags); |
| 645 | if (value < 0) |
| 646 | return value; |
| 647 | |
| 648 | if (div_hw->lock) |
| 649 | spin_lock_irqsave(div_hw->lock, flags); |
| 650 | else |
| 651 | __acquire(div_hw->lock); |
| 652 | |
| 653 | val = readl(reg_addr); |
| 654 | val &= ~(clk_div_mask(div->width) << div_hw->div.shift); |
| 655 | val |= (u32)value << div->shift; |
| 656 | writel(val, reg_addr); |
| 657 | |
| 658 | if (div_hw->lock) |
| 659 | spin_unlock_irqrestore(div_hw->lock, flags); |
| 660 | else |
| 661 | __release(div_hw->lock); |
| 662 | |
| 663 | return 0; |
| 664 | } |
| 665 | |
| 666 | static const struct clk_ops bm1880_clk_div_ops = { |
| 667 | .recalc_rate = bm1880_clk_div_recalc_rate, |
| 668 | .round_rate = bm1880_clk_div_round_rate, |
| 669 | .set_rate = bm1880_clk_div_set_rate, |
| 670 | }; |
| 671 | |
| 672 | static struct clk_hw *bm1880_clk_register_div(struct bm1880_div_hw_clock *div_clk, |
| 673 | void __iomem *sys_base) |
| 674 | { |
| 675 | struct clk_hw *hw; |
| 676 | int err; |
| 677 | |
| 678 | div_clk->div.flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO; |
| 679 | div_clk->base = sys_base; |
| 680 | div_clk->lock = &bm1880_clk_lock; |
| 681 | |
| 682 | hw = &div_clk->hw; |
| 683 | err = clk_hw_register(NULL, hw); |
| 684 | if (err) |
| 685 | return ERR_PTR(err); |
| 686 | |
| 687 | return hw; |
| 688 | } |
| 689 | |
Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 690 | static int bm1880_clk_register_divs(struct bm1880_div_hw_clock *clks, |
| 691 | int num_clks, |
| 692 | struct bm1880_clock_data *data) |
| 693 | { |
| 694 | struct clk_hw *hw; |
| 695 | void __iomem *sys_base = data->sys_base; |
| 696 | unsigned int i, id; |
| 697 | |
| 698 | for (i = 0; i < num_clks; i++) { |
| 699 | struct bm1880_div_hw_clock *bm1880_clk = &clks[i]; |
| 700 | |
| 701 | hw = bm1880_clk_register_div(bm1880_clk, sys_base); |
| 702 | if (IS_ERR(hw)) { |
| 703 | pr_err("%s: failed to register clock %s\n", |
| 704 | __func__, bm1880_clk->div.name); |
| 705 | goto err_clk; |
| 706 | } |
| 707 | |
| 708 | id = clks[i].div.id; |
| 709 | data->hw_data.hws[id] = hw; |
| 710 | } |
| 711 | |
| 712 | return 0; |
| 713 | |
| 714 | err_clk: |
| 715 | while (i--) |
Conor Dooley | c861c1b | 2021-12-23 15:42:44 +0000 | [diff] [blame] | 716 | clk_hw_unregister(data->hw_data.hws[clks[i].div.id]); |
Manivannan Sadhasivam | 1ab4601 | 2019-11-15 21:59:00 +0530 | [diff] [blame] | 717 | |
| 718 | return PTR_ERR(hw); |
| 719 | } |
| 720 | |
| 721 | static int bm1880_clk_register_gate(const struct bm1880_gate_clock *clks, |
| 722 | int num_clks, |
| 723 | struct bm1880_clock_data *data) |
| 724 | { |
| 725 | struct clk_hw *hw; |
| 726 | void __iomem *sys_base = data->sys_base; |
| 727 | int i; |
| 728 | |
| 729 | for (i = 0; i < num_clks; i++) { |
| 730 | hw = clk_hw_register_gate(NULL, clks[i].name, |
| 731 | clks[i].parent, |
| 732 | clks[i].flags, |
| 733 | sys_base + clks[i].gate_reg, |
| 734 | clks[i].gate_shift, 0, |
| 735 | &bm1880_clk_lock); |
| 736 | if (IS_ERR(hw)) { |
| 737 | pr_err("%s: failed to register clock %s\n", |
| 738 | __func__, clks[i].name); |
| 739 | goto err_clk; |
| 740 | } |
| 741 | |
| 742 | data->hw_data.hws[clks[i].id] = hw; |
| 743 | } |
| 744 | |
| 745 | return 0; |
| 746 | |
| 747 | err_clk: |
| 748 | while (i--) |
| 749 | clk_hw_unregister_gate(data->hw_data.hws[clks[i].id]); |
| 750 | |
| 751 | return PTR_ERR(hw); |
| 752 | } |
| 753 | |
| 754 | static struct clk_hw *bm1880_clk_register_composite(struct bm1880_composite_clock *clks, |
| 755 | void __iomem *sys_base) |
| 756 | { |
| 757 | struct clk_hw *hw; |
| 758 | struct clk_mux *mux = NULL; |
| 759 | struct clk_gate *gate = NULL; |
| 760 | struct bm1880_div_hw_clock *div_hws = NULL; |
| 761 | struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; |
| 762 | const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; |
| 763 | const char * const *parent_names; |
| 764 | const char *parent; |
| 765 | int num_parents; |
| 766 | int ret; |
| 767 | |
| 768 | if (clks->mux_shift >= 0) { |
| 769 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); |
| 770 | if (!mux) |
| 771 | return ERR_PTR(-ENOMEM); |
| 772 | |
| 773 | mux->reg = sys_base + clks->mux_reg; |
| 774 | mux->mask = 1; |
| 775 | mux->shift = clks->mux_shift; |
| 776 | mux_hw = &mux->hw; |
| 777 | mux_ops = &clk_mux_ops; |
| 778 | mux->lock = &bm1880_clk_lock; |
| 779 | |
| 780 | parent_names = clks->parents; |
| 781 | num_parents = clks->num_parents; |
| 782 | } else { |
| 783 | parent = clks->parent; |
| 784 | parent_names = &parent; |
| 785 | num_parents = 1; |
| 786 | } |
| 787 | |
| 788 | if (clks->gate_shift >= 0) { |
| 789 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); |
| 790 | if (!gate) { |
| 791 | ret = -ENOMEM; |
| 792 | goto err_out; |
| 793 | } |
| 794 | |
| 795 | gate->reg = sys_base + clks->gate_reg; |
| 796 | gate->bit_idx = clks->gate_shift; |
| 797 | gate->lock = &bm1880_clk_lock; |
| 798 | |
| 799 | gate_hw = &gate->hw; |
| 800 | gate_ops = &clk_gate_ops; |
| 801 | } |
| 802 | |
| 803 | if (clks->div_shift >= 0) { |
| 804 | div_hws = kzalloc(sizeof(*div_hws), GFP_KERNEL); |
| 805 | if (!div_hws) { |
| 806 | ret = -ENOMEM; |
| 807 | goto err_out; |
| 808 | } |
| 809 | |
| 810 | div_hws->base = sys_base; |
| 811 | div_hws->div.reg = clks->div_reg; |
| 812 | div_hws->div.shift = clks->div_shift; |
| 813 | div_hws->div.width = clks->div_width; |
| 814 | div_hws->div.table = clks->table; |
| 815 | div_hws->div.initval = clks->div_initval; |
| 816 | div_hws->lock = &bm1880_clk_lock; |
| 817 | div_hws->div.flags = CLK_DIVIDER_ONE_BASED | |
| 818 | CLK_DIVIDER_ALLOW_ZERO; |
| 819 | |
| 820 | div_hw = &div_hws->hw; |
| 821 | div_ops = &bm1880_clk_div_ops; |
| 822 | } |
| 823 | |
| 824 | hw = clk_hw_register_composite(NULL, clks->name, parent_names, |
| 825 | num_parents, mux_hw, mux_ops, div_hw, |
| 826 | div_ops, gate_hw, gate_ops, |
| 827 | clks->flags); |
| 828 | |
| 829 | if (IS_ERR(hw)) { |
| 830 | ret = PTR_ERR(hw); |
| 831 | goto err_out; |
| 832 | } |
| 833 | |
| 834 | return hw; |
| 835 | |
| 836 | err_out: |
| 837 | kfree(div_hws); |
| 838 | kfree(gate); |
| 839 | kfree(mux); |
| 840 | |
| 841 | return ERR_PTR(ret); |
| 842 | } |
| 843 | |
| 844 | static int bm1880_clk_register_composites(struct bm1880_composite_clock *clks, |
| 845 | int num_clks, |
| 846 | struct bm1880_clock_data *data) |
| 847 | { |
| 848 | struct clk_hw *hw; |
| 849 | void __iomem *sys_base = data->sys_base; |
| 850 | int i; |
| 851 | |
| 852 | for (i = 0; i < num_clks; i++) { |
| 853 | struct bm1880_composite_clock *bm1880_clk = &clks[i]; |
| 854 | |
| 855 | hw = bm1880_clk_register_composite(bm1880_clk, sys_base); |
| 856 | if (IS_ERR(hw)) { |
| 857 | pr_err("%s: failed to register clock %s\n", |
| 858 | __func__, bm1880_clk->name); |
| 859 | goto err_clk; |
| 860 | } |
| 861 | |
| 862 | data->hw_data.hws[clks[i].id] = hw; |
| 863 | } |
| 864 | |
| 865 | return 0; |
| 866 | |
| 867 | err_clk: |
| 868 | while (i--) |
| 869 | clk_hw_unregister_composite(data->hw_data.hws[clks[i].id]); |
| 870 | |
| 871 | return PTR_ERR(hw); |
| 872 | } |
| 873 | |
| 874 | static int bm1880_clk_probe(struct platform_device *pdev) |
| 875 | { |
| 876 | struct bm1880_clock_data *clk_data; |
| 877 | void __iomem *pll_base, *sys_base; |
| 878 | struct device *dev = &pdev->dev; |
| 879 | struct resource *res; |
| 880 | int num_clks, i; |
| 881 | |
| 882 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 883 | pll_base = devm_ioremap_resource(&pdev->dev, res); |
| 884 | if (IS_ERR(pll_base)) |
| 885 | return PTR_ERR(pll_base); |
| 886 | |
| 887 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| 888 | sys_base = devm_ioremap_resource(&pdev->dev, res); |
| 889 | if (IS_ERR(sys_base)) |
| 890 | return PTR_ERR(sys_base); |
| 891 | |
| 892 | num_clks = ARRAY_SIZE(bm1880_pll_clks) + |
| 893 | ARRAY_SIZE(bm1880_div_clks) + |
| 894 | ARRAY_SIZE(bm1880_mux_clks) + |
| 895 | ARRAY_SIZE(bm1880_composite_clks) + |
| 896 | ARRAY_SIZE(bm1880_gate_clks); |
| 897 | |
| 898 | clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, |
| 899 | num_clks), GFP_KERNEL); |
| 900 | if (!clk_data) |
| 901 | return -ENOMEM; |
| 902 | |
| 903 | clk_data->pll_base = pll_base; |
| 904 | clk_data->sys_base = sys_base; |
| 905 | |
| 906 | for (i = 0; i < num_clks; i++) |
| 907 | clk_data->hw_data.hws[i] = ERR_PTR(-ENOENT); |
| 908 | |
| 909 | clk_data->hw_data.num = num_clks; |
| 910 | |
| 911 | bm1880_clk_register_plls(bm1880_pll_clks, |
| 912 | ARRAY_SIZE(bm1880_pll_clks), |
| 913 | clk_data); |
| 914 | |
| 915 | bm1880_clk_register_divs(bm1880_div_clks, |
| 916 | ARRAY_SIZE(bm1880_div_clks), |
| 917 | clk_data); |
| 918 | |
| 919 | bm1880_clk_register_mux(bm1880_mux_clks, |
| 920 | ARRAY_SIZE(bm1880_mux_clks), |
| 921 | clk_data); |
| 922 | |
| 923 | bm1880_clk_register_composites(bm1880_composite_clks, |
| 924 | ARRAY_SIZE(bm1880_composite_clks), |
| 925 | clk_data); |
| 926 | |
| 927 | bm1880_clk_register_gate(bm1880_gate_clks, |
| 928 | ARRAY_SIZE(bm1880_gate_clks), |
| 929 | clk_data); |
| 930 | |
| 931 | return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, |
| 932 | &clk_data->hw_data); |
| 933 | } |
| 934 | |
| 935 | static const struct of_device_id bm1880_of_match[] = { |
| 936 | { .compatible = "bitmain,bm1880-clk", }, |
| 937 | {} |
| 938 | }; |
| 939 | MODULE_DEVICE_TABLE(of, bm1880_of_match); |
| 940 | |
| 941 | static struct platform_driver bm1880_clk_driver = { |
| 942 | .driver = { |
| 943 | .name = "bm1880-clk", |
| 944 | .of_match_table = bm1880_of_match, |
| 945 | }, |
| 946 | .probe = bm1880_clk_probe, |
| 947 | }; |
| 948 | module_platform_driver(bm1880_clk_driver); |
| 949 | |
| 950 | MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); |
| 951 | MODULE_DESCRIPTION("Clock driver for Bitmain BM1880 SoC"); |
| 952 | MODULE_LICENSE("GPL v2"); |