1 /* $OpenBSD: sxiccmu.c,v 1.1 2017/01/21 08:26:49 patrick Exp $ */ 2 /* 3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2013 Artturi Alm 5 * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/kernel.h> 24 #include <sys/malloc.h> 25 #include <sys/time.h> 26 #include <sys/device.h> 27 28 #include <machine/bus.h> 29 #include <machine/fdt.h> 30 #include <machine/intr.h> 31 32 #include <dev/fdt/sunxireg.h> 33 34 #include <dev/ofw/openfirm.h> 35 #include <dev/ofw/ofw_clock.h> 36 #include <dev/ofw/fdt.h> 37 38 #ifdef DEBUG_CCMU 39 #define DPRINTF(x) do { printf x; } while (0) 40 #else 41 #define DPRINTF(x) 42 #endif 43 44 struct sxiccmu_ccu_bit { 45 uint16_t reg; 46 uint8_t bit; 47 uint8_t parent; 48 }; 49 50 #include "sxiccmu_clocks.h" 51 52 struct sxiccmu_softc { 53 struct device sc_dev; 54 bus_space_tag_t sc_iot; 55 bus_space_handle_t sc_ioh; 56 57 struct sxiccmu_ccu_bit *sc_gates; 58 int sc_ngates; 59 struct clock_device sc_cd; 60 61 struct sxiccmu_ccu_bit *sc_resets; 62 int sc_nresets; 63 struct reset_device sc_rd; 64 65 uint32_t (*sc_get_frequency)(struct sxiccmu_softc *, 66 uint32_t); 67 int (*sc_set_frequency)(struct sxiccmu_softc *, 68 uint32_t, uint32_t); 69 }; 70 71 int sxiccmu_match(struct device *, void *, void *); 72 void sxiccmu_attach(struct device *, struct device *, void *); 73 74 struct cfattach sxiccmu_ca = { 75 sizeof (struct sxiccmu_softc), sxiccmu_match, sxiccmu_attach 76 }; 77 78 struct cfdriver sxiccmu_cd = { 79 NULL, "sxiccmu", DV_DULL 80 }; 81 82 void sxiccmu_attach_clock(struct sxiccmu_softc *, int); 83 84 uint32_t sxiccmu_ccu_get_frequency(void *, uint32_t *); 85 int sxiccmu_ccu_set_frequency(void *, uint32_t *, uint32_t); 86 void sxiccmu_ccu_enable(void *, uint32_t *, int); 87 void sxiccmu_ccu_reset(void *, uint32_t *, int); 88 89 uint32_t sxiccmu_a64_get_frequency(struct sxiccmu_softc *, uint32_t); 90 int sxiccmu_a64_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t); 91 uint32_t sxiccmu_h3_get_frequency(struct sxiccmu_softc *, uint32_t); 92 int sxiccmu_h3_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t); 93 94 int 95 sxiccmu_match(struct device *parent, void *match, void *aux) 96 { 97 struct fdt_attach_args *faa = aux; 98 99 if (faa->fa_node == OF_finddevice("/clocks")) { 100 int node = OF_parent(faa->fa_node); 101 102 return (OF_is_compatible(node, "allwinner,sun4i-a10") || 103 OF_is_compatible(node, "allwinner,sun5i-a10s") || 104 OF_is_compatible(node, "allwinner,sun5i-r8") || 105 OF_is_compatible(node, "allwinner,sun50i-a64") || 106 OF_is_compatible(node, "allwinner,sun7i-a20") || 107 OF_is_compatible(node, "allwinner,sun8i-h3") || 108 OF_is_compatible(node, "allwinner,sun9i-a80")); 109 } 110 111 return (OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ccu") || 112 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ccu")); 113 } 114 115 void 116 sxiccmu_attach(struct device *parent, struct device *self, void *aux) 117 { 118 struct sxiccmu_softc *sc = (struct sxiccmu_softc *)self; 119 struct fdt_attach_args *faa = aux; 120 int node = faa->fa_node; 121 122 sc->sc_iot = faa->fa_iot; 123 if (faa->fa_nreg > 0 && bus_space_map(sc->sc_iot, 124 faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh)) 125 panic("%s: bus_space_map failed!", __func__); 126 127 printf("\n"); 128 129 if (OF_is_compatible(node, "allwinner,sun50i-a64-ccu")) { 130 KASSERT(faa->fa_nreg > 0); 131 sc->sc_gates = sun50i_a64_gates; 132 sc->sc_ngates = nitems(sun50i_a64_gates); 133 sc->sc_resets = sun50i_a64_resets; 134 sc->sc_nresets = nitems(sun50i_a64_resets); 135 sc->sc_get_frequency = sxiccmu_a64_get_frequency; 136 sc->sc_set_frequency = sxiccmu_a64_set_frequency; 137 } else if (OF_is_compatible(node, "allwinner,sun8i-h3-ccu")) { 138 KASSERT(faa->fa_nreg > 0); 139 sc->sc_gates = sun8i_h3_gates; 140 sc->sc_ngates = nitems(sun8i_h3_gates); 141 sc->sc_resets = sun8i_h3_resets; 142 sc->sc_nresets = nitems(sun8i_h3_resets); 143 sc->sc_get_frequency = sxiccmu_h3_get_frequency; 144 sc->sc_set_frequency = sxiccmu_h3_set_frequency; 145 } else { 146 for (node = OF_child(node); node; node = OF_peer(node)) 147 sxiccmu_attach_clock(sc, node); 148 } 149 150 if (sc->sc_gates) { 151 sc->sc_cd.cd_node = node; 152 sc->sc_cd.cd_cookie = sc; 153 sc->sc_cd.cd_get_frequency = sxiccmu_ccu_get_frequency; 154 sc->sc_cd.cd_set_frequency = sxiccmu_ccu_set_frequency; 155 sc->sc_cd.cd_enable = sxiccmu_ccu_enable; 156 clock_register(&sc->sc_cd); 157 } 158 159 if (sc->sc_resets) { 160 sc->sc_rd.rd_node = node; 161 sc->sc_rd.rd_cookie = sc; 162 sc->sc_rd.rd_reset = sxiccmu_ccu_reset; 163 reset_register(&sc->sc_rd); 164 } 165 } 166 167 /* 168 * Device trees for the Allwinner SoCs have basically a clock node per 169 * register of the clock control unit. Attaching a separate driver to 170 * each of them would be crazy, so we handle them here. 171 */ 172 173 struct sxiccmu_clock { 174 int sc_node; 175 bus_space_tag_t sc_iot; 176 bus_space_handle_t sc_ioh; 177 178 struct clock_device sc_cd; 179 struct reset_device sc_rd; 180 }; 181 182 struct sxiccmu_device { 183 const char *compat; 184 uint32_t (*get_frequency)(void *, uint32_t *); 185 int (*set_frequency)(void *, uint32_t *, uint32_t); 186 void (*enable)(void *, uint32_t *, int); 187 void (*reset)(void *, uint32_t *, int); 188 }; 189 190 uint32_t sxiccmu_gen_get_frequency(void *, uint32_t *); 191 uint32_t sxiccmu_osc_get_frequency(void *, uint32_t *); 192 uint32_t sxiccmu_pll6_get_frequency(void *, uint32_t *); 193 void sxiccmu_pll6_enable(void *, uint32_t *, int); 194 uint32_t sxiccmu_apb1_get_frequency(void *, uint32_t *); 195 int sxiccmu_gmac_set_frequency(void *, uint32_t *, uint32_t); 196 int sxiccmu_mmc_set_frequency(void *, uint32_t *, uint32_t); 197 void sxiccmu_mmc_enable(void *, uint32_t *, int); 198 void sxiccmu_gate_enable(void *, uint32_t *, int); 199 void sxiccmu_reset(void *, uint32_t *, int); 200 201 struct sxiccmu_device sxiccmu_devices[] = { 202 { 203 .compat = "allwinner,sun4i-a10-osc-clk", 204 .get_frequency = sxiccmu_osc_get_frequency, 205 }, 206 { 207 .compat = "allwinner,sun4i-a10-pll6-clk", 208 .get_frequency = sxiccmu_pll6_get_frequency, 209 .enable = sxiccmu_pll6_enable 210 }, 211 { 212 .compat = "allwinner,sun4i-a10-apb1-clk", 213 .get_frequency = sxiccmu_apb1_get_frequency, 214 }, 215 { 216 .compat = "allwinner,sun4i-a10-ahb-gates-clk", 217 .get_frequency = sxiccmu_gen_get_frequency, 218 .enable = sxiccmu_gate_enable 219 }, 220 { 221 .compat = "allwinner,sun4i-a10-apb0-gates-clk", 222 .get_frequency = sxiccmu_gen_get_frequency, 223 .enable = sxiccmu_gate_enable 224 }, 225 { 226 .compat = "allwinner,sun4i-a10-apb1-gates-clk", 227 .get_frequency = sxiccmu_gen_get_frequency, 228 .enable = sxiccmu_gate_enable 229 }, 230 { 231 .compat = "allwinner,sun4i-a10-mmc-clk", 232 .set_frequency = sxiccmu_mmc_set_frequency, 233 .enable = sxiccmu_mmc_enable 234 }, 235 { 236 .compat = "allwinner,sun4i-a10-usb-clk", 237 .get_frequency = sxiccmu_gen_get_frequency, 238 .enable = sxiccmu_gate_enable, 239 .reset = sxiccmu_reset 240 }, 241 { 242 .compat = "allwinner,sun5i-a10s-ahb-gates-clk", 243 .get_frequency = sxiccmu_gen_get_frequency, 244 .enable = sxiccmu_gate_enable 245 }, 246 { 247 .compat = "allwinner,sun5i-a10s-apb0-gates-clk", 248 .get_frequency = sxiccmu_gen_get_frequency, 249 .enable = sxiccmu_gate_enable 250 }, 251 { 252 .compat = "allwinner,sun5i-a10s-apb1-gates-clk", 253 .get_frequency = sxiccmu_gen_get_frequency, 254 .enable = sxiccmu_gate_enable 255 }, 256 { 257 .compat = "allwinner,sun5i-a13-ahb-gates-clk", 258 .get_frequency = sxiccmu_gen_get_frequency, 259 .enable = sxiccmu_gate_enable 260 }, 261 { 262 .compat = "allwinner,sun5i-a13-apb0-gates-clk", 263 .get_frequency = sxiccmu_gen_get_frequency, 264 .enable = sxiccmu_gate_enable 265 }, 266 { 267 .compat = "allwinner,sun5i-a13-apb1-gates-clk", 268 .get_frequency = sxiccmu_gen_get_frequency, 269 .enable = sxiccmu_gate_enable 270 }, 271 { 272 .compat = "allwinner,sun5i-a13-usb-clk", 273 .get_frequency = sxiccmu_gen_get_frequency, 274 .enable = sxiccmu_gate_enable, 275 .reset = sxiccmu_reset 276 }, 277 { 278 .compat = "allwinner,sun6i-a31-ahb1-reset", 279 .reset = sxiccmu_reset 280 }, 281 { 282 .compat = "allwinner,sun6i-a31-clock-reset", 283 .reset = sxiccmu_reset 284 }, 285 { 286 .compat = "allwinner,sun7i-a20-ahb-gates-clk", 287 .get_frequency = sxiccmu_gen_get_frequency, 288 .enable = sxiccmu_gate_enable 289 }, 290 { 291 .compat = "allwinner,sun7i-a20-apb0-gates-clk", 292 .get_frequency = sxiccmu_gen_get_frequency, 293 .enable = sxiccmu_gate_enable 294 }, 295 { 296 .compat = "allwinner,sun7i-a20-apb1-gates-clk", 297 .get_frequency = sxiccmu_gen_get_frequency, 298 .enable = sxiccmu_gate_enable 299 }, 300 { 301 .compat = "allwinner,sun7i-a20-gmac-clk", 302 .set_frequency = sxiccmu_gmac_set_frequency 303 }, 304 { 305 .compat = "allwinner,sun8i-h3-apb0-gates-clk", 306 .get_frequency = sxiccmu_gen_get_frequency, 307 .enable = sxiccmu_gate_enable 308 }, 309 { 310 .compat = "allwinner,sun9i-a80-apb1-clk", 311 .get_frequency = sxiccmu_apb1_get_frequency, 312 }, 313 { 314 .compat = "allwinner,sun9i-a80-ahb0-gates-clk", 315 .get_frequency = sxiccmu_gen_get_frequency, 316 .enable = sxiccmu_gate_enable 317 }, 318 { 319 .compat = "allwinner,sun9i-a80-ahb1-gates-clk", 320 .get_frequency = sxiccmu_gen_get_frequency, 321 .enable = sxiccmu_gate_enable 322 }, 323 { 324 .compat = "allwinner,sun9i-a80-ahb2-gates-clk", 325 .get_frequency = sxiccmu_gen_get_frequency, 326 .enable = sxiccmu_gate_enable 327 }, 328 { 329 .compat = "allwinner,sun9i-a80-apb0-gates-clk", 330 .get_frequency = sxiccmu_gen_get_frequency, 331 .enable = sxiccmu_gate_enable 332 }, 333 { 334 .compat = "allwinner,sun9i-a80-apb1-gates-clk", 335 .get_frequency = sxiccmu_gen_get_frequency, 336 .enable = sxiccmu_gate_enable 337 }, 338 { 339 .compat = "allwinner,sun9i-a80-apbs-gates-clk", 340 .get_frequency = sxiccmu_gen_get_frequency, 341 .enable = sxiccmu_gate_enable 342 }, 343 { 344 .compat = "allwinner,sun9i-a80-mmc-clk", 345 .set_frequency = sxiccmu_mmc_set_frequency, 346 .enable = sxiccmu_mmc_enable 347 }, 348 { 349 .compat = "allwinner,sun9i-a80-usb-mod-clk", 350 .get_frequency = sxiccmu_gen_get_frequency, 351 .enable = sxiccmu_gate_enable, 352 .reset = sxiccmu_reset 353 }, 354 { 355 .compat = "allwinner,sun9i-a80-usb-phy-clk", 356 .get_frequency = sxiccmu_gen_get_frequency, 357 .enable = sxiccmu_gate_enable, 358 .reset = sxiccmu_reset 359 }, 360 }; 361 362 void 363 sxiccmu_attach_clock(struct sxiccmu_softc *sc, int node) 364 { 365 struct sxiccmu_clock *clock; 366 uint32_t reg[2]; 367 int i; 368 369 for (i = 0; i < nitems(sxiccmu_devices); i++) 370 if (OF_is_compatible(node, sxiccmu_devices[i].compat)) 371 break; 372 if (i == nitems(sxiccmu_devices)) 373 return; 374 375 clock = malloc(sizeof(*clock), M_DEVBUF, M_WAITOK); 376 clock->sc_node = node; 377 378 if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) == sizeof(reg)) { 379 clock->sc_iot = sc->sc_iot; 380 if (bus_space_map(clock->sc_iot, reg[0], reg[1], 0, 381 &clock->sc_ioh)) { 382 printf("%s: can't map registers", sc->sc_dev.dv_xname); 383 free(clock, M_DEVBUF, sizeof(*clock)); 384 return; 385 } 386 } 387 388 clock->sc_cd.cd_node = node; 389 clock->sc_cd.cd_cookie = clock; 390 clock->sc_cd.cd_get_frequency = sxiccmu_devices[i].get_frequency; 391 clock->sc_cd.cd_set_frequency = sxiccmu_devices[i].set_frequency; 392 clock->sc_cd.cd_enable = sxiccmu_devices[i].enable; 393 clock_register(&clock->sc_cd); 394 395 if (sxiccmu_devices[i].reset) { 396 clock->sc_rd.rd_node = node; 397 clock->sc_rd.rd_cookie = clock; 398 clock->sc_rd.rd_reset = sxiccmu_devices[i].reset; 399 reset_register(&clock->sc_rd); 400 } 401 } 402 403 /* 404 * A "generic" function that simply gets the clock frequency from the 405 * parent clock. Useful for clock gating devices that don't scale 406 * their clocks. 407 */ 408 uint32_t 409 sxiccmu_gen_get_frequency(void *cookie, uint32_t *cells) 410 { 411 struct sxiccmu_clock *sc = cookie; 412 413 return clock_get_frequency(sc->sc_node, NULL); 414 } 415 416 uint32_t 417 sxiccmu_osc_get_frequency(void *cookie, uint32_t *cells) 418 { 419 struct sxiccmu_clock *sc = cookie; 420 421 return OF_getpropint(sc->sc_node, "clock-frequency", 24000000); 422 } 423 424 #define CCU_PLL6_ENABLE (1U << 31) 425 #define CCU_PLL6_BYPASS_EN (1U << 30) 426 #define CCU_PLL6_SATA_CLK_EN (1U << 14) 427 #define CCU_PLL6_FACTOR_N(x) (((x) >> 8) & 0x1f) 428 #define CCU_PLL6_FACTOR_N_MASK (0x1f << 8) 429 #define CCU_PLL6_FACTOR_N_SHIFT 8 430 #define CCU_PLL6_FACTOR_K(x) (((x) >> 4) & 0x3) 431 #define CCU_PLL6_FACTOR_K_MASK (0x3 << 4) 432 #define CCU_PLL6_FACTOR_K_SHIFT 4 433 #define CCU_PLL6_FACTOR_M(x) (((x) >> 0) & 0x3) 434 #define CCU_PLL6_FACTOR_M_MASK (0x3 << 0) 435 #define CCU_PLL6_FACTOR_M_SHIFT 0 436 437 uint32_t 438 sxiccmu_pll6_get_frequency(void *cookie, uint32_t *cells) 439 { 440 struct sxiccmu_clock *sc = cookie; 441 uint32_t reg, k, m, n, freq; 442 uint32_t idx = cells[0]; 443 444 /* XXX Assume bypass is disabled. */ 445 reg = SXIREAD4(sc, 0); 446 k = CCU_PLL6_FACTOR_K(reg) + 1; 447 m = CCU_PLL6_FACTOR_M(reg) + 1; 448 n = CCU_PLL6_FACTOR_N(reg); 449 450 freq = clock_get_frequency_idx(sc->sc_node, 0); 451 switch (idx) { 452 case 0: 453 return (freq * n * k) / m / 6; /* pll6_sata */ 454 case 1: 455 return (freq * n * k) / 2; /* pll6_other */ 456 case 2: 457 return (freq * n * k); /* pll6 */ 458 case 3: 459 return (freq * n * k) / 4; /* pll6_div_4 */ 460 } 461 462 return 0; 463 } 464 465 void 466 sxiccmu_pll6_enable(void *cookie, uint32_t *cells, int on) 467 { 468 struct sxiccmu_clock *sc = cookie; 469 uint32_t idx = cells[0]; 470 uint32_t reg; 471 472 /* 473 * Since this clock has several outputs, we never turn it off. 474 */ 475 476 reg = SXIREAD4(sc, 0); 477 switch (idx) { 478 case 0: /* pll6_sata */ 479 if (on) 480 reg |= CCU_PLL6_SATA_CLK_EN; 481 else 482 reg &= ~CCU_PLL6_SATA_CLK_EN; 483 /* FALLTHROUGH */ 484 case 1: /* pll6_other */ 485 case 2: /* pll6 */ 486 case 3: /* pll6_div_4 */ 487 if (on) 488 reg |= CCU_PLL6_ENABLE; 489 } 490 SXIWRITE4(sc, 0, reg); 491 } 492 493 #define CCU_APB1_CLK_RAT_N(x) (((x) >> 16) & 0x3) 494 #define CCU_APB1_CLK_RAT_M(x) (((x) >> 0) & 0x1f) 495 #define CCU_APB1_CLK_SRC_SEL(x) (((x) >> 24) & 0x3) 496 497 uint32_t 498 sxiccmu_apb1_get_frequency(void *cookie, uint32_t *cells) 499 { 500 struct sxiccmu_clock *sc = cookie; 501 uint32_t reg, m, n, freq; 502 int idx; 503 504 reg = SXIREAD4(sc, 0); 505 m = CCU_APB1_CLK_RAT_M(reg); 506 n = CCU_APB1_CLK_RAT_N(reg); 507 idx = CCU_APB1_CLK_SRC_SEL(reg); 508 509 freq = clock_get_frequency_idx(sc->sc_node, idx); 510 return freq / (1 << n) / (m + 1); 511 } 512 513 #define CCU_GMAC_CLK_PIT (1 << 2) 514 #define CCU_GMAC_CLK_TCS (3 << 0) 515 #define CCU_GMAC_CLK_TCS_MII 0 516 #define CCU_GMAC_CLK_TCS_EXT_125 1 517 #define CCU_GMAC_CLK_TCS_INT_RGMII 2 518 519 int 520 sxiccmu_gmac_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 521 { 522 struct sxiccmu_clock *sc = cookie; 523 524 switch (freq) { 525 case 25000000: /* MMI, 25 MHz */ 526 SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS, 527 CCU_GMAC_CLK_TCS_MII); 528 break; 529 case 125000000: /* RGMII, 125 MHz */ 530 SXICMS4(sc, 0, CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS, 531 CCU_GMAC_CLK_PIT|CCU_GMAC_CLK_TCS_INT_RGMII); 532 break; 533 default: 534 return -1; 535 } 536 537 return 0; 538 } 539 540 #define CCU_SDx_SCLK_GATING (1U << 31) 541 #define CCU_SDx_CLK_SRC_SEL_OSC24M (0 << 24) 542 #define CCU_SDx_CLK_SRC_SEL_PLL6 (1 << 24) 543 #define CCU_SDx_CLK_SRC_SEL_PLL5 (2 << 24) 544 #define CCU_SDx_CLK_SRC_SEL_MASK (3 << 24) 545 #define CCU_SDx_CLK_DIV_RATIO_N_MASK (3 << 16) 546 #define CCU_SDx_CLK_DIV_RATIO_N_SHIFT 16 547 #define CCU_SDx_CLK_DIV_RATIO_M_MASK (7 << 0) 548 #define CCU_SDx_CLK_DIV_RATIO_M_SHIFT 0 549 550 int 551 sxiccmu_mmc_do_set_frequency(struct sxiccmu_clock *sc, uint32_t freq, 552 uint32_t parent_freq) 553 { 554 uint32_t reg, m, n; 555 uint32_t clk_src; 556 557 switch (freq) { 558 case 400000: 559 n = 2, m = 15; 560 clk_src = CCU_SDx_CLK_SRC_SEL_OSC24M; 561 break; 562 case 25000000: 563 case 26000000: 564 case 50000000: 565 case 52000000: 566 n = 0, m = 0; 567 clk_src = CCU_SDx_CLK_SRC_SEL_PLL6; 568 while ((parent_freq / (1 << n) / 16) > freq) 569 n++; 570 while ((parent_freq / (1 << n) / (m + 1)) > freq) 571 m++; 572 break; 573 default: 574 return -1; 575 } 576 577 reg = SXIREAD4(sc, 0); 578 reg &= ~CCU_SDx_CLK_SRC_SEL_MASK; 579 reg |= clk_src; 580 reg &= ~CCU_SDx_CLK_DIV_RATIO_N_MASK; 581 reg |= n << CCU_SDx_CLK_DIV_RATIO_N_SHIFT; 582 reg &= ~CCU_SDx_CLK_DIV_RATIO_M_MASK; 583 reg |= m << CCU_SDx_CLK_DIV_RATIO_M_SHIFT; 584 SXIWRITE4(sc, 0, reg); 585 586 return 0; 587 } 588 589 int 590 sxiccmu_mmc_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 591 { 592 struct sxiccmu_clock *sc = cookie; 593 uint32_t parent_freq; 594 595 if (cells[0] != 0) 596 return -1; 597 598 parent_freq = clock_get_frequency_idx(sc->sc_node, 1); 599 return sxiccmu_mmc_do_set_frequency(sc, freq, parent_freq); 600 } 601 602 void 603 sxiccmu_mmc_enable(void *cookie, uint32_t *cells, int on) 604 { 605 struct sxiccmu_clock *sc = cookie; 606 607 if (cells[0] != 0) 608 return; 609 610 if (on) 611 SXISET4(sc, 0, CCU_SDx_SCLK_GATING); 612 else 613 SXICLR4(sc, 0, CCU_SDx_SCLK_GATING); 614 } 615 616 void 617 sxiccmu_gate_enable(void *cookie, uint32_t *cells, int on) 618 { 619 struct sxiccmu_clock *sc = cookie; 620 int reg = cells[0] / 32; 621 int bit = cells[0] % 32; 622 623 if (on) { 624 clock_enable(sc->sc_node, NULL); 625 SXISET4(sc, reg * 4, (1U << bit)); 626 } else { 627 SXICLR4(sc, reg * 4, (1U << bit)); 628 clock_disable(sc->sc_node, NULL); 629 } 630 } 631 632 void 633 sxiccmu_reset(void *cookie, uint32_t *cells, int assert) 634 { 635 struct sxiccmu_clock *sc = cookie; 636 int reg = cells[0] / 32; 637 int bit = cells[0] % 32; 638 639 if (assert) 640 SXICLR4(sc, reg * 4, (1U << bit)); 641 else 642 SXISET4(sc, reg * 4, (1U << bit)); 643 } 644 645 /* 646 * Device trees for the Allwinner A80 have most of the clock nodes 647 * replaced with a single clock control unit node. 648 */ 649 650 uint32_t 651 sxiccmu_ccu_get_frequency(void *cookie, uint32_t *cells) 652 { 653 struct sxiccmu_softc *sc = cookie; 654 uint32_t idx = cells[0]; 655 uint32_t parent; 656 657 if (idx < sc->sc_ngates && sc->sc_gates[idx].parent) { 658 parent = sc->sc_gates[idx].parent; 659 return sxiccmu_ccu_get_frequency(sc, &parent); 660 } 661 662 return sc->sc_get_frequency(sc, idx); 663 } 664 665 uint32_t 666 sxiccmu_a64_get_frequency(struct sxiccmu_softc *sc, uint32_t idx) 667 { 668 switch (idx) { 669 case A64_CLK_PLL_PERIPH0: 670 /* XXX default value. */ 671 return 600000000; 672 case A64_CLK_PLL_PERIPH0_2X: 673 return sxiccmu_a64_get_frequency(sc, A64_CLK_PLL_PERIPH0) * 2; 674 case A64_CLK_APB2: 675 /* XXX Controlled by a MUX. */ 676 return 24000000; 677 } 678 679 printf("%s: 0x%08x\n", __func__, idx); 680 return 0; 681 } 682 683 uint32_t 684 sxiccmu_h3_get_frequency(struct sxiccmu_softc *sc, uint32_t idx) 685 { 686 switch (idx) { 687 case H3_CLK_PLL_PERIPH0: 688 /* XXX default value. */ 689 return 600000000; 690 case H3_CLK_APB2: 691 /* XXX Controlled by a MUX. */ 692 return 24000000; 693 } 694 695 printf("%s: 0x%08x\n", __func__, idx); 696 return 0; 697 } 698 699 int 700 sxiccmu_ccu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 701 { 702 struct sxiccmu_softc *sc = cookie; 703 uint32_t idx = cells[0]; 704 705 return sc->sc_set_frequency(sc, idx, freq); 706 } 707 708 int 709 sxiccmu_a64_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq) 710 { 711 struct sxiccmu_clock clock; 712 uint32_t parent, parent_freq; 713 714 switch (idx) { 715 case A64_CLK_MMC0: 716 case A64_CLK_MMC1: 717 case A64_CLK_MMC2: 718 clock.sc_iot = sc->sc_iot; 719 bus_space_subregion(sc->sc_iot, sc->sc_ioh, 720 sc->sc_gates[idx].reg, 4, &clock.sc_ioh); 721 parent = A64_CLK_PLL_PERIPH0_2X; 722 parent_freq = sxiccmu_ccu_get_frequency(sc, &parent); 723 return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq); 724 } 725 726 printf("%s: 0x%08x\n", __func__, idx); 727 return -1; 728 } 729 730 int 731 sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq) 732 { 733 struct sxiccmu_clock clock; 734 uint32_t parent, parent_freq; 735 736 switch (idx) { 737 case H3_CLK_MMC0: 738 case H3_CLK_MMC1: 739 case H3_CLK_MMC2: 740 clock.sc_iot = sc->sc_iot; 741 bus_space_subregion(sc->sc_iot, sc->sc_ioh, 742 sc->sc_gates[idx].reg, 4, &clock.sc_ioh); 743 parent = H3_CLK_PLL_PERIPH0; 744 parent_freq = sxiccmu_ccu_get_frequency(sc, &parent); 745 return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq); 746 } 747 748 printf("%s: 0x%08x\n", __func__, idx); 749 return -1; 750 } 751 752 void 753 sxiccmu_ccu_enable(void *cookie, uint32_t *cells, int on) 754 { 755 struct sxiccmu_softc *sc = cookie; 756 uint32_t idx = cells[0]; 757 int reg, bit; 758 759 if (idx >= sc->sc_ngates || sc->sc_gates[idx].reg == 0) { 760 printf("%s: 0x%08x\n", __func__, cells[0]); 761 return; 762 } 763 764 reg = sc->sc_gates[idx].reg; 765 bit = sc->sc_gates[idx].bit; 766 767 if (on) 768 SXISET4(sc, reg, (1U << bit)); 769 else 770 SXICLR4(sc, reg, (1U << bit)); 771 } 772 773 void 774 sxiccmu_ccu_reset(void *cookie, uint32_t *cells, int assert) 775 { 776 struct sxiccmu_softc *sc = cookie; 777 uint32_t idx = cells[0]; 778 int reg, bit; 779 780 if (idx >= sc->sc_nresets || sc->sc_resets[idx].reg == 0) { 781 printf("%s: 0x%08x\n", __func__, cells[0]); 782 return; 783 } 784 785 reg = sc->sc_resets[idx].reg; 786 bit = sc->sc_resets[idx].bit; 787 788 if (assert) 789 SXICLR4(sc, reg, (1U << bit)); 790 else 791 SXISET4(sc, reg, (1U << bit)); 792 } 793