Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com> |
| 4 | */ |
| 5 | #include <kendryte/clk.h> |
| 6 | |
| 7 | #include <asm/io.h> |
| 8 | #include <dt-bindings/clock/k210-sysctl.h> |
| 9 | #include <dt-bindings/mfd/k210-sysctl.h> |
| 10 | #include <dm.h> |
| 11 | #include <log.h> |
| 12 | #include <mapmem.h> |
| 13 | |
| 14 | #include <kendryte/bypass.h> |
| 15 | #include <kendryte/pll.h> |
| 16 | |
| 17 | /* All methods are delegated to CCF clocks */ |
| 18 | |
| 19 | static ulong k210_clk_get_rate(struct clk *clk) |
| 20 | { |
| 21 | struct clk *c; |
| 22 | int err = clk_get_by_id(clk->id, &c); |
| 23 | |
| 24 | if (err) |
| 25 | return err; |
| 26 | return clk_get_rate(c); |
| 27 | } |
| 28 | |
| 29 | static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) |
| 30 | { |
| 31 | struct clk *c; |
| 32 | int err = clk_get_by_id(clk->id, &c); |
| 33 | |
| 34 | if (err) |
| 35 | return err; |
| 36 | return clk_set_rate(c, rate); |
| 37 | } |
| 38 | |
| 39 | static int k210_clk_set_parent(struct clk *clk, struct clk *parent) |
| 40 | { |
| 41 | struct clk *c, *p; |
| 42 | int err = clk_get_by_id(clk->id, &c); |
| 43 | |
| 44 | if (err) |
| 45 | return err; |
| 46 | |
| 47 | err = clk_get_by_id(parent->id, &p); |
| 48 | if (err) |
| 49 | return err; |
| 50 | |
| 51 | return clk_set_parent(c, p); |
| 52 | } |
| 53 | |
| 54 | static int k210_clk_endisable(struct clk *clk, bool enable) |
| 55 | { |
| 56 | struct clk *c; |
| 57 | int err = clk_get_by_id(clk->id, &c); |
| 58 | |
| 59 | if (err) |
| 60 | return err; |
| 61 | return enable ? clk_enable(c) : clk_disable(c); |
| 62 | } |
| 63 | |
| 64 | static int k210_clk_enable(struct clk *clk) |
| 65 | { |
| 66 | return k210_clk_endisable(clk, true); |
| 67 | } |
| 68 | |
| 69 | static int k210_clk_disable(struct clk *clk) |
| 70 | { |
| 71 | return k210_clk_endisable(clk, false); |
| 72 | } |
| 73 | |
| 74 | static const struct clk_ops k210_clk_ops = { |
| 75 | .set_rate = k210_clk_set_rate, |
| 76 | .get_rate = k210_clk_get_rate, |
| 77 | .set_parent = k210_clk_set_parent, |
| 78 | .enable = k210_clk_enable, |
| 79 | .disable = k210_clk_disable, |
| 80 | }; |
| 81 | |
| 82 | /* Parents for muxed clocks */ |
| 83 | static const char * const generic_sels[] = { "in0_half", "pll0_half" }; |
| 84 | /* The first clock is in0, which is filled in by k210_clk_probe */ |
| 85 | static const char *aclk_sels[] = { NULL, "pll0_half" }; |
| 86 | static const char *pll2_sels[] = { NULL, "pll0", "pll1" }; |
| 87 | |
| 88 | /* |
| 89 | * All parameters for different sub-clocks are collected into parameter arrays. |
| 90 | * These parameters are then initialized by the clock which uses them during |
| 91 | * probe. To save space, ids are automatically generated for each sub-clock by |
| 92 | * using an enum. Instead of storing a parameter struct for each clock, even for |
| 93 | * those clocks which don't use a particular type of sub-clock, we can just |
| 94 | * store the parameters for the clocks which need them. |
| 95 | * |
| 96 | * So why do it like this? Arranging all the sub-clocks together makes it very |
| 97 | * easy to find bugs in the code. |
| 98 | */ |
| 99 | |
| 100 | #define DIV(id, off, shift, width) DIV_FLAGS(id, off, shift, width, 0) |
| 101 | #define DIV_LIST \ |
| 102 | DIV_FLAGS(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, \ |
| 103 | CLK_DIVIDER_POWER_OF_TWO) \ |
| 104 | DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3) \ |
| 105 | DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3) \ |
| 106 | DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3) \ |
| 107 | DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4) \ |
| 108 | DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4) \ |
| 109 | DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4) \ |
| 110 | DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4) \ |
| 111 | DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4) \ |
| 112 | DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8) \ |
| 113 | DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8) \ |
| 114 | DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8) \ |
| 115 | DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8) \ |
| 116 | DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8) \ |
| 117 | DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8) \ |
| 118 | DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8) \ |
| 119 | DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16) \ |
| 120 | DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16) \ |
| 121 | DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16) \ |
| 122 | DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8) \ |
| 123 | DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8) \ |
| 124 | DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8) \ |
| 125 | DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8) \ |
| 126 | DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8) \ |
| 127 | DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8) \ |
| 128 | DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8) \ |
| 129 | DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8) |
| 130 | |
| 131 | #define _DIVIFY(id) K210_CLK_DIV_##id |
| 132 | #define DIVIFY(id) _DIVIFY(id) |
| 133 | |
| 134 | enum k210_div_ids { |
| 135 | #define DIV_FLAGS(id, ...) DIVIFY(id), |
| 136 | DIV_LIST |
| 137 | #undef DIV_FLAGS |
| 138 | }; |
| 139 | |
| 140 | struct k210_div_params { |
| 141 | u8 off; |
| 142 | u8 shift; |
| 143 | u8 width; |
| 144 | u8 flags; |
| 145 | }; |
| 146 | |
| 147 | static const struct k210_div_params k210_divs[] = { |
| 148 | #define DIV_FLAGS(id, _off, _shift, _width, _flags) \ |
| 149 | [DIVIFY(id)] = { \ |
| 150 | .off = (_off), \ |
| 151 | .shift = (_shift), \ |
| 152 | .width = (_width), \ |
| 153 | .flags = (_flags), \ |
| 154 | }, |
| 155 | DIV_LIST |
| 156 | #undef DIV_FLAGS |
| 157 | }; |
| 158 | |
| 159 | #undef DIV |
| 160 | #undef DIV_LIST |
| 161 | |
| 162 | #define GATE_LIST \ |
| 163 | GATE(K210_CLK_CPU, K210_SYSCTL_EN_CENT, 0) \ |
| 164 | GATE(K210_CLK_SRAM0, K210_SYSCTL_EN_CENT, 1) \ |
| 165 | GATE(K210_CLK_SRAM1, K210_SYSCTL_EN_CENT, 2) \ |
| 166 | GATE(K210_CLK_APB0, K210_SYSCTL_EN_CENT, 3) \ |
| 167 | GATE(K210_CLK_APB1, K210_SYSCTL_EN_CENT, 4) \ |
| 168 | GATE(K210_CLK_APB2, K210_SYSCTL_EN_CENT, 5) \ |
| 169 | GATE(K210_CLK_ROM, K210_SYSCTL_EN_PERI, 0) \ |
| 170 | GATE(K210_CLK_DMA, K210_SYSCTL_EN_PERI, 1) \ |
| 171 | GATE(K210_CLK_AI, K210_SYSCTL_EN_PERI, 2) \ |
| 172 | GATE(K210_CLK_DVP, K210_SYSCTL_EN_PERI, 3) \ |
| 173 | GATE(K210_CLK_FFT, K210_SYSCTL_EN_PERI, 4) \ |
| 174 | GATE(K210_CLK_GPIO, K210_SYSCTL_EN_PERI, 5) \ |
| 175 | GATE(K210_CLK_SPI0, K210_SYSCTL_EN_PERI, 6) \ |
| 176 | GATE(K210_CLK_SPI1, K210_SYSCTL_EN_PERI, 7) \ |
| 177 | GATE(K210_CLK_SPI2, K210_SYSCTL_EN_PERI, 8) \ |
| 178 | GATE(K210_CLK_SPI3, K210_SYSCTL_EN_PERI, 9) \ |
| 179 | GATE(K210_CLK_I2S0, K210_SYSCTL_EN_PERI, 10) \ |
| 180 | GATE(K210_CLK_I2S1, K210_SYSCTL_EN_PERI, 11) \ |
| 181 | GATE(K210_CLK_I2S2, K210_SYSCTL_EN_PERI, 12) \ |
| 182 | GATE(K210_CLK_I2C0, K210_SYSCTL_EN_PERI, 13) \ |
| 183 | GATE(K210_CLK_I2C1, K210_SYSCTL_EN_PERI, 14) \ |
| 184 | GATE(K210_CLK_I2C2, K210_SYSCTL_EN_PERI, 15) \ |
| 185 | GATE(K210_CLK_UART1, K210_SYSCTL_EN_PERI, 16) \ |
| 186 | GATE(K210_CLK_UART2, K210_SYSCTL_EN_PERI, 17) \ |
| 187 | GATE(K210_CLK_UART3, K210_SYSCTL_EN_PERI, 18) \ |
| 188 | GATE(K210_CLK_AES, K210_SYSCTL_EN_PERI, 19) \ |
| 189 | GATE(K210_CLK_FPIOA, K210_SYSCTL_EN_PERI, 20) \ |
| 190 | GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \ |
| 191 | GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \ |
| 192 | GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \ |
| 193 | GATE(K210_CLK_WDT0, K210_SYSCTL_EN_PERI, 24) \ |
| 194 | GATE(K210_CLK_WDT1, K210_SYSCTL_EN_PERI, 25) \ |
| 195 | GATE(K210_CLK_SHA, K210_SYSCTL_EN_PERI, 26) \ |
| 196 | GATE(K210_CLK_OTP, K210_SYSCTL_EN_PERI, 27) \ |
| 197 | GATE(K210_CLK_RTC, K210_SYSCTL_EN_PERI, 29) |
| 198 | |
| 199 | #define _GATEIFY(id) K210_CLK_GATE_##id |
| 200 | #define GATEIFY(id) _GATEIFY(id) |
| 201 | |
| 202 | enum k210_gate_ids { |
| 203 | #define GATE(id, ...) GATEIFY(id), |
| 204 | GATE_LIST |
| 205 | #undef GATE |
| 206 | }; |
| 207 | |
| 208 | struct k210_gate_params { |
| 209 | u8 off; |
| 210 | u8 bit_idx; |
| 211 | }; |
| 212 | |
| 213 | static const struct k210_gate_params k210_gates[] = { |
| 214 | #define GATE(id, _off, _idx) \ |
| 215 | [GATEIFY(id)] = { \ |
| 216 | .off = (_off), \ |
| 217 | .bit_idx = (_idx), \ |
| 218 | }, |
| 219 | GATE_LIST |
| 220 | #undef GATE |
| 221 | }; |
| 222 | |
| 223 | #undef GATE_LIST |
| 224 | |
| 225 | #define MUX(id, reg, shift, width) \ |
| 226 | MUX_PARENTS(id, generic_sels, reg, shift, width) |
| 227 | #define MUX_LIST \ |
| 228 | MUX_PARENTS(K210_CLK_PLL2, pll2_sels, K210_SYSCTL_PLL2, 26, 2) \ |
| 229 | MUX_PARENTS(K210_CLK_ACLK, aclk_sels, K210_SYSCTL_SEL0, 0, 1) \ |
| 230 | MUX(K210_CLK_SPI3, K210_SYSCTL_SEL0, 12, 1) \ |
| 231 | MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \ |
| 232 | MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \ |
| 233 | MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1) |
| 234 | |
| 235 | #define _MUXIFY(id) K210_CLK_MUX_##id |
| 236 | #define MUXIFY(id) _MUXIFY(id) |
| 237 | |
| 238 | enum k210_mux_ids { |
| 239 | #define MUX_PARENTS(id, ...) MUXIFY(id), |
| 240 | MUX_LIST |
| 241 | #undef MUX_PARENTS |
| 242 | K210_CLK_MUX_NONE, |
| 243 | }; |
| 244 | |
| 245 | struct k210_mux_params { |
| 246 | const char *const *parent_names; |
| 247 | u8 num_parents; |
| 248 | u8 off; |
| 249 | u8 shift; |
| 250 | u8 width; |
| 251 | }; |
| 252 | |
| 253 | static const struct k210_mux_params k210_muxes[] = { |
| 254 | #define MUX_PARENTS(id, parents, _off, _shift, _width) \ |
| 255 | [MUXIFY(id)] = { \ |
| 256 | .parent_names = (const char * const *)(parents), \ |
| 257 | .num_parents = ARRAY_SIZE(parents), \ |
| 258 | .off = (_off), \ |
| 259 | .shift = (_shift), \ |
| 260 | .width = (_width), \ |
| 261 | }, |
| 262 | MUX_LIST |
| 263 | #undef MUX_PARENTS |
| 264 | }; |
| 265 | |
| 266 | #undef MUX |
| 267 | #undef MUX_LIST |
| 268 | |
| 269 | struct k210_pll_params { |
| 270 | u8 off; |
| 271 | u8 lock_off; |
| 272 | u8 shift; |
| 273 | u8 width; |
| 274 | }; |
| 275 | |
| 276 | static const struct k210_pll_params k210_plls[] = { |
| 277 | #define PLL(_off, _shift, _width) { \ |
| 278 | .off = (_off), \ |
| 279 | .lock_off = K210_SYSCTL_PLL_LOCK, \ |
| 280 | .shift = (_shift), \ |
| 281 | .width = (_width), \ |
| 282 | } |
| 283 | [0] = PLL(K210_SYSCTL_PLL0, 0, 2), |
| 284 | [1] = PLL(K210_SYSCTL_PLL1, 8, 1), |
| 285 | [2] = PLL(K210_SYSCTL_PLL2, 16, 1), |
| 286 | #undef PLL |
| 287 | }; |
| 288 | |
| 289 | #define COMP(id) \ |
| 290 | COMP_FULL(id, MUXIFY(id), DIVIFY(id), GATEIFY(id)) |
| 291 | #define COMP_NOMUX(id) \ |
| 292 | COMP_FULL(id, K210_CLK_MUX_NONE, DIVIFY(id), GATEIFY(id)) |
| 293 | #define COMP_LIST \ |
| 294 | COMP(K210_CLK_SPI3) \ |
| 295 | COMP(K210_CLK_TIMER0) \ |
| 296 | COMP(K210_CLK_TIMER1) \ |
| 297 | COMP(K210_CLK_TIMER2) \ |
| 298 | COMP_NOMUX(K210_CLK_SRAM0) \ |
| 299 | COMP_NOMUX(K210_CLK_SRAM1) \ |
| 300 | COMP_NOMUX(K210_CLK_ROM) \ |
| 301 | COMP_NOMUX(K210_CLK_DVP) \ |
| 302 | COMP_NOMUX(K210_CLK_APB0) \ |
| 303 | COMP_NOMUX(K210_CLK_APB1) \ |
| 304 | COMP_NOMUX(K210_CLK_APB2) \ |
| 305 | COMP_NOMUX(K210_CLK_AI) \ |
| 306 | COMP_NOMUX(K210_CLK_I2S0) \ |
| 307 | COMP_NOMUX(K210_CLK_I2S1) \ |
| 308 | COMP_NOMUX(K210_CLK_I2S2) \ |
| 309 | COMP_NOMUX(K210_CLK_WDT0) \ |
| 310 | COMP_NOMUX(K210_CLK_WDT1) \ |
| 311 | COMP_NOMUX(K210_CLK_SPI0) \ |
| 312 | COMP_NOMUX(K210_CLK_SPI1) \ |
| 313 | COMP_NOMUX(K210_CLK_SPI2) \ |
| 314 | COMP_NOMUX(K210_CLK_I2C0) \ |
| 315 | COMP_NOMUX(K210_CLK_I2C1) \ |
| 316 | COMP_NOMUX(K210_CLK_I2C2) |
| 317 | |
| 318 | #define _COMPIFY(id) K210_CLK_COMP_##id |
| 319 | #define COMPIFY(id) _COMPIFY(id) |
| 320 | |
| 321 | enum k210_comp_ids { |
| 322 | #define COMP_FULL(id, ...) COMPIFY(id), |
| 323 | COMP_LIST |
| 324 | #undef COMP_FULL |
| 325 | }; |
| 326 | |
| 327 | struct k210_comp_params { |
| 328 | u8 mux; |
| 329 | u8 div; |
| 330 | u8 gate; |
| 331 | }; |
| 332 | |
| 333 | static const struct k210_comp_params k210_comps[] = { |
| 334 | #define COMP_FULL(id, _mux, _div, _gate) \ |
| 335 | [COMPIFY(id)] = { \ |
| 336 | .mux = (_mux), \ |
| 337 | .div = (_div), \ |
| 338 | .gate = (_gate), \ |
| 339 | }, |
| 340 | COMP_LIST |
| 341 | #undef COMP_FULL |
| 342 | }; |
| 343 | |
| 344 | #undef COMP |
| 345 | #undef COMP_ID |
| 346 | #undef COMP_NOMUX |
| 347 | #undef COMP_NOMUX_ID |
| 348 | #undef COMP_LIST |
| 349 | |
Sean Anderson | 09ad08f | 2021-04-08 22:13:08 -0400 | [diff] [blame^] | 350 | static struct clk *k210_bypass_children __section(.data); |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 351 | |
| 352 | /* Helper functions to create sub-clocks */ |
| 353 | static struct clk_mux *k210_create_mux(const struct k210_mux_params *params, |
| 354 | void *base) |
| 355 | { |
| 356 | struct clk_mux *mux = kzalloc(sizeof(*mux), GFP_KERNEL); |
| 357 | |
| 358 | if (!mux) |
| 359 | return mux; |
| 360 | |
| 361 | mux->reg = base + params->off; |
| 362 | mux->mask = BIT(params->width) - 1; |
| 363 | mux->shift = params->shift; |
| 364 | mux->parent_names = params->parent_names; |
| 365 | mux->num_parents = params->num_parents; |
| 366 | |
| 367 | return mux; |
| 368 | } |
| 369 | |
| 370 | static struct clk_divider *k210_create_div(const struct k210_div_params *params, |
| 371 | void *base) |
| 372 | { |
| 373 | struct clk_divider *div = kzalloc(sizeof(*div), GFP_KERNEL); |
| 374 | |
| 375 | if (!div) |
| 376 | return div; |
| 377 | |
| 378 | div->reg = base + params->off; |
| 379 | div->shift = params->shift; |
| 380 | div->width = params->width; |
| 381 | div->flags = params->flags; |
| 382 | |
| 383 | return div; |
| 384 | } |
| 385 | |
| 386 | static struct clk_gate *k210_create_gate(const struct k210_gate_params *params, |
| 387 | void *base) |
| 388 | { |
| 389 | struct clk_gate *gate = kzalloc(sizeof(*gate), GFP_KERNEL); |
| 390 | |
| 391 | if (!gate) |
| 392 | return gate; |
| 393 | |
| 394 | gate->reg = base + params->off; |
| 395 | gate->bit_idx = params->bit_idx; |
| 396 | |
| 397 | return gate; |
| 398 | } |
| 399 | |
| 400 | static struct k210_pll *k210_create_pll(const struct k210_pll_params *params, |
| 401 | void *base) |
| 402 | { |
| 403 | struct k210_pll *pll = kzalloc(sizeof(*pll), GFP_KERNEL); |
| 404 | |
| 405 | if (!pll) |
| 406 | return pll; |
| 407 | |
| 408 | pll->reg = base + params->off; |
| 409 | pll->lock = base + params->lock_off; |
| 410 | pll->shift = params->shift; |
| 411 | pll->width = params->width; |
| 412 | |
| 413 | return pll; |
| 414 | } |
| 415 | |
| 416 | /* Create all sub-clocks, and then register the composite clock */ |
| 417 | static struct clk *k210_register_comp(const struct k210_comp_params *params, |
| 418 | void *base, const char *name, |
| 419 | const char *parent) |
| 420 | { |
| 421 | const char *const *parent_names; |
| 422 | int num_parents; |
| 423 | struct clk *comp; |
| 424 | const struct clk_ops *mux_ops; |
| 425 | struct clk_mux *mux; |
| 426 | struct clk_divider *div; |
| 427 | struct clk_gate *gate; |
| 428 | |
| 429 | if (params->mux == K210_CLK_MUX_NONE) { |
| 430 | if (!parent) |
| 431 | return ERR_PTR(-EINVAL); |
| 432 | |
| 433 | mux_ops = NULL; |
| 434 | mux = NULL; |
| 435 | parent_names = &parent; |
| 436 | num_parents = 1; |
| 437 | } else { |
| 438 | mux_ops = &clk_mux_ops; |
| 439 | mux = k210_create_mux(&k210_muxes[params->mux], base); |
| 440 | if (!mux) |
| 441 | return ERR_PTR(-ENOMEM); |
| 442 | |
| 443 | parent_names = mux->parent_names; |
| 444 | num_parents = mux->num_parents; |
| 445 | } |
| 446 | |
| 447 | div = k210_create_div(&k210_divs[params->div], base); |
| 448 | if (!div) { |
| 449 | comp = ERR_PTR(-ENOMEM); |
| 450 | goto cleanup_mux; |
| 451 | } |
| 452 | |
| 453 | gate = k210_create_gate(&k210_gates[params->gate], base); |
| 454 | if (!gate) { |
| 455 | comp = ERR_PTR(-ENOMEM); |
| 456 | goto cleanup_div; |
| 457 | } |
| 458 | |
| 459 | comp = clk_register_composite(NULL, name, parent_names, num_parents, |
| 460 | &mux->clk, mux_ops, |
| 461 | &div->clk, &clk_divider_ops, |
| 462 | &gate->clk, &clk_gate_ops, 0); |
| 463 | if (IS_ERR(comp)) |
| 464 | goto cleanup_gate; |
| 465 | return comp; |
| 466 | |
| 467 | cleanup_gate: |
| 468 | free(gate); |
| 469 | cleanup_div: |
| 470 | free(div); |
| 471 | cleanup_mux: |
Heinrich Schuchardt | 8bb7496 | 2020-09-29 21:52:12 +0200 | [diff] [blame] | 472 | free(mux); |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 473 | return comp; |
| 474 | } |
| 475 | |
Sean Anderson | 09ad08f | 2021-04-08 22:13:08 -0400 | [diff] [blame^] | 476 | static bool __section(.data) probed; |
| 477 | |
| 478 | /* reset probed so we will probe again post-relocation */ |
| 479 | static int k210_clk_bind(struct udevice *dev) |
| 480 | { |
| 481 | probed = false; |
| 482 | return 0; |
| 483 | } |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 484 | |
| 485 | static int k210_clk_probe(struct udevice *dev) |
| 486 | { |
| 487 | int ret; |
| 488 | const char *in0; |
| 489 | struct clk *in0_clk, *bypass; |
| 490 | struct clk_mux *mux; |
| 491 | struct clk_divider *div; |
| 492 | struct k210_pll *pll; |
| 493 | void *base; |
| 494 | |
| 495 | /* |
| 496 | * Only one instance of this driver allowed. This prevents weird bugs |
| 497 | * when the driver fails part-way through probing. Some clocks will |
| 498 | * already have been registered, and re-probing will register them |
| 499 | * again, creating a bunch of duplicates. Better error-handling/cleanup |
| 500 | * could fix this, but it's Probably Not Worth It (TM). |
| 501 | */ |
| 502 | if (probed) |
Simon Glass | 9042bf6 | 2021-03-25 10:26:08 +1300 | [diff] [blame] | 503 | return -EINVAL; |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 504 | |
| 505 | base = dev_read_addr_ptr(dev_get_parent(dev)); |
| 506 | if (!base) |
| 507 | return -EINVAL; |
| 508 | |
| 509 | in0_clk = kzalloc(sizeof(*in0_clk), GFP_KERNEL); |
| 510 | if (!in0_clk) |
| 511 | return -ENOMEM; |
| 512 | |
| 513 | ret = clk_get_by_index(dev, 0, in0_clk); |
| 514 | if (ret) |
| 515 | return ret; |
| 516 | in0 = in0_clk->dev->name; |
| 517 | |
| 518 | probed = true; |
| 519 | |
| 520 | aclk_sels[0] = in0; |
| 521 | pll2_sels[0] = in0; |
| 522 | |
| 523 | /* |
| 524 | * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we |
| 525 | * need to manually reparent it whenever we configure pll0 |
| 526 | */ |
| 527 | pll = k210_create_pll(&k210_plls[0], base); |
| 528 | if (pll) { |
| 529 | bypass = k210_register_bypass("pll0", in0, &pll->clk, |
| 530 | &k210_pll_ops, in0_clk); |
| 531 | clk_dm(K210_CLK_PLL0, bypass); |
| 532 | } else { |
| 533 | return -ENOMEM; |
| 534 | } |
| 535 | |
Sean Anderson | f93ce98 | 2021-04-08 22:13:06 -0400 | [diff] [blame] | 536 | pll = k210_create_pll(&k210_plls[1], base); |
| 537 | if (pll) |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 538 | clk_dm(K210_CLK_PLL1, |
Sean Anderson | f93ce98 | 2021-04-08 22:13:06 -0400 | [diff] [blame] | 539 | k210_register_pll_struct("pll1", in0, pll)); |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 540 | |
| 541 | /* PLL2 is muxed, so set up a composite clock */ |
| 542 | mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_PLL2)], base); |
| 543 | pll = k210_create_pll(&k210_plls[2], base); |
| 544 | if (!mux || !pll) { |
| 545 | free(mux); |
| 546 | free(pll); |
| 547 | } else { |
| 548 | clk_dm(K210_CLK_PLL2, |
| 549 | clk_register_composite(NULL, "pll2", pll2_sels, |
| 550 | ARRAY_SIZE(pll2_sels), |
| 551 | &mux->clk, &clk_mux_ops, |
| 552 | &pll->clk, &k210_pll_ops, |
| 553 | &pll->clk, &k210_pll_ops, 0)); |
| 554 | } |
| 555 | |
| 556 | /* Half-frequency clocks for "even" dividers */ |
| 557 | clk_dm(K210_CLK_IN0_H, k210_clk_half("in0_half", in0)); |
| 558 | clk_dm(K210_CLK_PLL0_H, k210_clk_half("pll0_half", "pll0")); |
| 559 | clk_dm(K210_CLK_PLL2_H, k210_clk_half("pll2_half", "pll2")); |
| 560 | |
| 561 | /* ACLK has no gate */ |
| 562 | mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_ACLK)], base); |
| 563 | div = k210_create_div(&k210_divs[DIVIFY(K210_CLK_ACLK)], base); |
| 564 | if (!mux || !div) { |
| 565 | free(mux); |
| 566 | free(div); |
| 567 | } else { |
| 568 | struct clk *aclk = |
| 569 | clk_register_composite(NULL, "aclk", aclk_sels, |
| 570 | ARRAY_SIZE(aclk_sels), |
| 571 | &mux->clk, &clk_mux_ops, |
| 572 | &div->clk, &clk_divider_ops, |
| 573 | NULL, NULL, 0); |
| 574 | clk_dm(K210_CLK_ACLK, aclk); |
| 575 | if (!IS_ERR(aclk)) { |
| 576 | k210_bypass_children = aclk; |
| 577 | k210_bypass_set_children(bypass, |
| 578 | &k210_bypass_children, 1); |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | #define REGISTER_COMP(id, name) \ |
| 583 | clk_dm(id, \ |
| 584 | k210_register_comp(&k210_comps[COMPIFY(id)], base, name, NULL)) |
| 585 | REGISTER_COMP(K210_CLK_SPI3, "spi3"); |
| 586 | REGISTER_COMP(K210_CLK_TIMER0, "timer0"); |
| 587 | REGISTER_COMP(K210_CLK_TIMER1, "timer1"); |
| 588 | REGISTER_COMP(K210_CLK_TIMER2, "timer2"); |
| 589 | #undef REGISTER_COMP |
| 590 | |
| 591 | /* Dividing clocks, no mux */ |
| 592 | #define REGISTER_COMP_NOMUX(id, name, parent) \ |
| 593 | clk_dm(id, \ |
| 594 | k210_register_comp(&k210_comps[COMPIFY(id)], base, name, parent)) |
| 595 | REGISTER_COMP_NOMUX(K210_CLK_SRAM0, "sram0", "aclk"); |
| 596 | REGISTER_COMP_NOMUX(K210_CLK_SRAM1, "sram1", "aclk"); |
| 597 | REGISTER_COMP_NOMUX(K210_CLK_ROM, "rom", "aclk"); |
| 598 | REGISTER_COMP_NOMUX(K210_CLK_DVP, "dvp", "aclk"); |
| 599 | REGISTER_COMP_NOMUX(K210_CLK_APB0, "apb0", "aclk"); |
| 600 | REGISTER_COMP_NOMUX(K210_CLK_APB1, "apb1", "aclk"); |
| 601 | REGISTER_COMP_NOMUX(K210_CLK_APB2, "apb2", "aclk"); |
| 602 | REGISTER_COMP_NOMUX(K210_CLK_AI, "ai", "pll1"); |
| 603 | REGISTER_COMP_NOMUX(K210_CLK_I2S0, "i2s0", "pll2_half"); |
| 604 | REGISTER_COMP_NOMUX(K210_CLK_I2S1, "i2s1", "pll2_half"); |
| 605 | REGISTER_COMP_NOMUX(K210_CLK_I2S2, "i2s2", "pll2_half"); |
| 606 | REGISTER_COMP_NOMUX(K210_CLK_WDT0, "wdt0", "in0_half"); |
| 607 | REGISTER_COMP_NOMUX(K210_CLK_WDT1, "wdt1", "in0_half"); |
| 608 | REGISTER_COMP_NOMUX(K210_CLK_SPI0, "spi0", "pll0_half"); |
| 609 | REGISTER_COMP_NOMUX(K210_CLK_SPI1, "spi1", "pll0_half"); |
| 610 | REGISTER_COMP_NOMUX(K210_CLK_SPI2, "spi2", "pll0_half"); |
| 611 | REGISTER_COMP_NOMUX(K210_CLK_I2C0, "i2c0", "pll0_half"); |
| 612 | REGISTER_COMP_NOMUX(K210_CLK_I2C1, "i2c1", "pll0_half"); |
| 613 | REGISTER_COMP_NOMUX(K210_CLK_I2C2, "i2c2", "pll0_half"); |
| 614 | #undef REGISTER_COMP_NOMUX |
| 615 | |
| 616 | /* Dividing clocks */ |
| 617 | #define REGISTER_DIV(id, name, parent) do {\ |
| 618 | const struct k210_div_params *params = &k210_divs[DIVIFY(id)]; \ |
| 619 | clk_dm(id, \ |
| 620 | clk_register_divider(NULL, name, parent, 0, base + params->off, \ |
| 621 | params->shift, params->width, 0)); \ |
| 622 | } while (false) |
| 623 | REGISTER_DIV(K210_CLK_I2S0_M, "i2s0_m", "pll2_half"); |
| 624 | REGISTER_DIV(K210_CLK_I2S1_M, "i2s1_m", "pll2_half"); |
| 625 | REGISTER_DIV(K210_CLK_I2S2_M, "i2s2_m", "pll2_half"); |
| 626 | #undef REGISTER_DIV |
| 627 | |
| 628 | /* Gated clocks */ |
| 629 | #define REGISTER_GATE(id, name, parent) do { \ |
| 630 | const struct k210_gate_params *params = &k210_gates[GATEIFY(id)]; \ |
| 631 | clk_dm(id, \ |
| 632 | clk_register_gate(NULL, name, parent, 0, base + params->off, \ |
| 633 | params->bit_idx, 0, NULL)); \ |
| 634 | } while (false) |
| 635 | REGISTER_GATE(K210_CLK_CPU, "cpu", "aclk"); |
| 636 | REGISTER_GATE(K210_CLK_DMA, "dma", "aclk"); |
| 637 | REGISTER_GATE(K210_CLK_FFT, "fft", "aclk"); |
| 638 | REGISTER_GATE(K210_CLK_GPIO, "gpio", "apb0"); |
| 639 | REGISTER_GATE(K210_CLK_UART1, "uart1", "apb0"); |
| 640 | REGISTER_GATE(K210_CLK_UART2, "uart2", "apb0"); |
| 641 | REGISTER_GATE(K210_CLK_UART3, "uart3", "apb0"); |
| 642 | REGISTER_GATE(K210_CLK_FPIOA, "fpioa", "apb0"); |
| 643 | REGISTER_GATE(K210_CLK_SHA, "sha", "apb0"); |
| 644 | REGISTER_GATE(K210_CLK_AES, "aes", "apb1"); |
| 645 | REGISTER_GATE(K210_CLK_OTP, "otp", "apb1"); |
| 646 | REGISTER_GATE(K210_CLK_RTC, "rtc", in0); |
| 647 | #undef REGISTER_GATE |
| 648 | |
Sean Anderson | a952c3a | 2020-09-28 10:52:27 -0400 | [diff] [blame] | 649 | /* The MTIME register in CLINT runs at one 50th the CPU clock speed */ |
| 650 | clk_dm(K210_CLK_CLINT, |
Sean Anderson | 800c7f6 | 2021-04-08 22:13:07 -0400 | [diff] [blame] | 651 | clk_register_fixed_factor(NULL, "clint", "aclk", 0, 1, 50)); |
Sean Anderson | a952c3a | 2020-09-28 10:52:27 -0400 | [diff] [blame] | 652 | |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 653 | return 0; |
| 654 | } |
| 655 | |
| 656 | static const struct udevice_id k210_clk_ids[] = { |
| 657 | { .compatible = "kendryte,k210-clk" }, |
| 658 | { }, |
| 659 | }; |
| 660 | |
| 661 | U_BOOT_DRIVER(k210_clk) = { |
| 662 | .name = "k210_clk", |
| 663 | .id = UCLASS_CLK, |
| 664 | .of_match = k210_clk_ids, |
| 665 | .ops = &k210_clk_ops, |
Sean Anderson | 09ad08f | 2021-04-08 22:13:08 -0400 | [diff] [blame^] | 666 | .bind = k210_clk_bind, |
Sean Anderson | f9c7d4f | 2020-06-24 06:41:11 -0400 | [diff] [blame] | 667 | .probe = k210_clk_probe, |
| 668 | }; |