1 /* $OpenBSD: mvclock.c,v 1.13 2022/06/05 02:43:44 dlg Exp $ */ 2 /* 3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_clock.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/fdt.h> 30 31 #define HREAD4(sc, reg) \ 32 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 33 #define HWRITE4(sc, reg, val) \ 34 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 35 #define HSET4(sc, reg, bits) \ 36 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 37 #define HCLR4(sc, reg, bits) \ 38 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 39 40 struct mvclock_softc { 41 struct device sc_dev; 42 bus_space_tag_t sc_iot; 43 bus_space_handle_t sc_ioh; 44 45 struct clock_device sc_cd; 46 }; 47 48 int mvclock_match(struct device *, void *, void *); 49 void mvclock_attach(struct device *, struct device *, void *); 50 51 const struct cfattach mvclock_ca = { 52 sizeof (struct mvclock_softc), mvclock_match, mvclock_attach 53 }; 54 55 struct cfdriver mvclock_cd = { 56 NULL, "mvclock", DV_DULL 57 }; 58 59 uint32_t ap806_get_frequency(void *, uint32_t *); 60 uint32_t cp110_get_frequency(void *, uint32_t *); 61 void cp110_enable(void *, uint32_t *, int); 62 63 void a3700_periph_nb_enable(void *, uint32_t *, int); 64 uint32_t a3700_periph_nb_get_frequency(void *, uint32_t *); 65 void a3700_periph_sb_enable(void *, uint32_t *, int); 66 uint32_t a3700_periph_sb_get_frequency(void *, uint32_t *); 67 uint32_t a3700_tbg_get_frequency(void *, uint32_t *); 68 69 int 70 mvclock_match(struct device *parent, void *match, void *aux) 71 { 72 struct fdt_attach_args *faa = aux; 73 int node = faa->fa_node; 74 75 if (OF_is_compatible(node, "marvell,ap806-clock") || 76 OF_is_compatible(node, "marvell,ap807-clock") || 77 OF_is_compatible(node, "marvell,cp110-clock") || 78 OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") || 79 OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") || 80 OF_is_compatible(node, "marvell,armada-3700-tbg-clock") || 81 OF_is_compatible(node, "marvell,armada-3700-xtal-clock")) 82 return 10; /* Must beat syscon(4). */ 83 84 return 0; 85 } 86 87 void 88 mvclock_attach(struct device *parent, struct device *self, void *aux) 89 { 90 struct mvclock_softc *sc = (struct mvclock_softc *)self; 91 struct fdt_attach_args *faa = aux; 92 int node = faa->fa_node; 93 94 if (faa->fa_nreg > 0) { 95 sc->sc_iot = faa->fa_iot; 96 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 97 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 98 printf(": can't map registers\n"); 99 return; 100 } 101 } 102 103 printf("\n"); 104 105 sc->sc_cd.cd_node = node; 106 sc->sc_cd.cd_cookie = sc; 107 if (OF_is_compatible(node, "marvell,ap806-clock") || 108 OF_is_compatible(node, "marvell,ap807-clock")) { 109 sc->sc_cd.cd_get_frequency = ap806_get_frequency; 110 } else if (OF_is_compatible(node, "marvell,cp110-clock")) { 111 sc->sc_cd.cd_get_frequency = cp110_get_frequency; 112 sc->sc_cd.cd_enable = cp110_enable; 113 } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) { 114 sc->sc_cd.cd_enable = a3700_periph_nb_enable; 115 sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency; 116 } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) { 117 sc->sc_cd.cd_enable = a3700_periph_sb_enable; 118 sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency; 119 } else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) { 120 sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency; 121 } 122 clock_register(&sc->sc_cd); 123 } 124 125 /* AP806 block */ 126 127 #define AP806_CORE_FIXED 2 128 #define AP806_CORE_MSS 3 129 #define AP806_CORE_SDIO 4 130 131 uint32_t 132 ap806_get_frequency(void *cookie, uint32_t *cells) 133 { 134 uint32_t idx = cells[0]; 135 136 switch (idx) { 137 case AP806_CORE_FIXED: 138 /* fixed PLL at 1200MHz */ 139 return 1200000000; 140 case AP806_CORE_MSS: 141 /* MSS clock is fixed clock divided by 6 */ 142 return 200000000; 143 case AP806_CORE_SDIO: 144 /* SDIO/eMMC clock is fixed clock divided by 3 */ 145 return 400000000; 146 default: 147 break; 148 } 149 150 printf("%s: 0x%08x\n", __func__, idx); 151 return 0; 152 } 153 154 /* CP110 block */ 155 156 #define CP110_PM_CLOCK_GATING_CTRL 0x220 157 158 #define CP110_CORE_APLL 0 159 #define CP110_CORE_PPV2 1 160 #define CP110_CORE_X2CORE 2 161 #define CP110_CORE_CORE 3 162 #define CP110_CORE_SDIO 5 163 164 #define CP110_GATE_PPV2 3 165 #define CP110_GATE_SDIO 4 166 #define CP110_GATE_SLOW_IO 21 167 168 uint32_t 169 cp110_get_frequency(void *cookie, uint32_t *cells) 170 { 171 struct mvclock_softc *sc = cookie; 172 uint32_t mod = cells[0]; 173 uint32_t idx = cells[1]; 174 uint32_t parent[2] = { 0, 0 }; 175 176 /* Core clocks */ 177 if (mod == 0) { 178 switch (idx) { 179 case CP110_CORE_APLL: 180 /* fixed PLL at 1GHz */ 181 return 1000000000; 182 case CP110_CORE_PPV2: 183 /* PPv2 clock is APLL/3 */ 184 return 333333333; 185 case CP110_CORE_X2CORE: 186 /* X2CORE clock is APLL/2 */ 187 return 500000000; 188 case CP110_CORE_CORE: 189 /* Core clock is X2CORE/2 */ 190 return 250000000; 191 case CP110_CORE_SDIO: 192 /* SDIO clock is APLL/2.5 */ 193 return 400000000; 194 default: 195 break; 196 } 197 } 198 199 /* Gateable clocks */ 200 if (mod == 1) { 201 switch (idx) { 202 case CP110_GATE_PPV2: 203 parent[1] = CP110_CORE_PPV2; 204 break; 205 case CP110_GATE_SDIO: 206 parent[1] = CP110_CORE_SDIO; 207 break; 208 case CP110_GATE_SLOW_IO: 209 parent[1] = CP110_CORE_X2CORE; 210 break; 211 default: 212 break; 213 } 214 215 if (parent[1] != 0) 216 return cp110_get_frequency(sc, parent); 217 } 218 219 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 220 return 0; 221 } 222 223 void 224 cp110_enable(void *cookie, uint32_t *cells, int on) 225 { 226 struct mvclock_softc *sc = cookie; 227 uint32_t mod = cells[0]; 228 uint32_t idx = cells[1]; 229 230 /* Gateable clocks */ 231 if (mod == 1 && idx < 32) { 232 struct regmap *rm; 233 uint32_t reg; 234 235 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); 236 if (rm == NULL) { 237 printf("%s: can't enable clock 0x%08x 0x%08x\n", 238 sc->sc_dev.dv_xname, mod, idx); 239 return; 240 } 241 reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL); 242 if (on) 243 reg |= (1U << idx); 244 else 245 reg &= ~(1U << idx); 246 regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg); 247 return; 248 } 249 250 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 251 } 252 253 /* Armada 3700 Periph block */ 254 255 /* North Bridge clocks */ 256 #define PERIPH_NB_MMC 0x00 257 #define PERIPH_NB_SATA 0x01 /* SATA Host */ 258 #define PERIPH_NB_SEC_AT 0x02 /* Security AT */ 259 #define PERIPH_NB_SEC_DAP 0x03 /* Security DAP */ 260 #define PERIPH_NB_TSECM 0x04 /* Security Engine */ 261 #define PERIPH_NB_SETM_TMX 0x05 /* Serial Embedded Trace Module */ 262 #define PERIPH_NB_AVS 0x06 /* Adaptive Voltage Scaling */ 263 #define PERIPH_NB_SQF 0x07 /* SPI */ 264 #define PERIPH_NB_I2C2 0x09 265 #define PERIPH_NB_I2C1 0x0a 266 #define PERIPH_NB_DDR_PHY 0x0b 267 #define PERIPH_NB_DDR_FCLK 0x0c 268 #define PERIPH_NB_TRACE 0x0d 269 #define PERIPH_NB_COUNTER 0x0e 270 #define PERIPH_NB_EIO97 0x0f 271 #define PERIPH_NB_CPU 0x10 272 273 /* South Bridge clocks */ 274 #define PERIPH_SB_GBE_50 0x00 /* 50MHz parent for gbe */ 275 #define PERIPH_SB_GBE_CORE 0x01 /* parent for gbe core */ 276 #define PERIPH_SB_GBE_125 0x02 /* 125MHz parent for gbe */ 277 #define PERIPH_SB_GBE1_50 0x03 /* 50MHz parent for gbe port 1 */ 278 #define PERIPH_SB_GBE0_50 0x04 /* 50MHz parent for gbe port 0 */ 279 #define PERIPH_SB_GBE1_125 0x05 /* 125MHz parent for gbe port 1 */ 280 #define PERIPH_SB_GBE0_125 0x06 /* 125MHz parent for gbe port 0 */ 281 #define PERIPH_SB_GBE1_CORE 0x07 /* gbe core port 1 */ 282 #define PERIPH_SB_GBE0_CORE 0x08 /* gbe core port 0 */ 283 #define PERIPH_SB_GBE_BM 0x09 /* gbe buffer manager */ 284 #define PERIPH_SB_SDIO 0x0a 285 #define PERIPH_SB_USB32_USB2_SYS 0x0b /* USB 2 clock */ 286 #define PERIPH_SB_USB32_SS_SYS 0x0c /* USB 3 clock */ 287 #define PERIPH_SB_PCIE 0x0d 288 289 #define PERIPH_TBG_SEL 0x0 290 #define PERIPH_TBG_SEL_MASK 0x3 291 #define PERIPH_DIV_SEL0 0x4 292 #define PERIPH_DIV_SEL1 0x8 293 #define PERIPH_DIV_SEL2 0xc 294 #define PERIPH_DIV_SEL_MASK 0x7 295 #define PERIPH_CLK_SEL 0x10 296 #define PERIPH_CLK_DIS 0x14 297 298 void a3700_periph_enable(struct mvclock_softc *, uint32_t, int); 299 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t); 300 uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t); 301 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t, 302 uint32_t, uint32_t); 303 304 void 305 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on) 306 { 307 struct mvclock_softc *sc = cookie; 308 uint32_t idx = cells[0]; 309 310 switch (idx) { 311 case PERIPH_NB_MMC: 312 return a3700_periph_enable(sc, 2, on); 313 case PERIPH_NB_SQF: 314 return a3700_periph_enable(sc, 12, on); 315 case PERIPH_NB_I2C2: 316 return a3700_periph_enable(sc, 16, on); 317 case PERIPH_NB_I2C1: 318 return a3700_periph_enable(sc, 17, on); 319 default: 320 break; 321 } 322 323 printf("%s: 0x%08x\n", __func__, idx); 324 } 325 326 uint32_t 327 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells) 328 { 329 struct mvclock_softc *sc = cookie; 330 uint32_t idx = cells[0]; 331 uint32_t freq; 332 333 switch (idx) { 334 case PERIPH_NB_MMC: 335 freq = a3700_periph_tbg_get_frequency(sc, 0); 336 freq /= a3700_periph_get_double_div(sc, 337 PERIPH_DIV_SEL2, 16, 13); 338 return freq; 339 case PERIPH_NB_SQF: 340 freq = a3700_periph_tbg_get_frequency(sc, 12); 341 freq /= a3700_periph_get_double_div(sc, 342 PERIPH_DIV_SEL1, 27, 24); 343 return freq; 344 case PERIPH_NB_CPU: 345 freq = a3700_periph_tbg_get_frequency(sc, 22); 346 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28); 347 return freq; 348 default: 349 break; 350 } 351 352 printf("%s: 0x%08x\n", __func__, idx); 353 return 0; 354 } 355 356 void 357 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on) 358 { 359 struct mvclock_softc *sc = cookie; 360 uint32_t idx = cells[0]; 361 362 switch (idx) { 363 case PERIPH_SB_GBE1_CORE: 364 return a3700_periph_enable(sc, 4, on); 365 case PERIPH_SB_GBE0_CORE: 366 return a3700_periph_enable(sc, 5, on); 367 case PERIPH_SB_USB32_USB2_SYS: 368 return a3700_periph_enable(sc, 16, on); 369 case PERIPH_SB_USB32_SS_SYS: 370 return a3700_periph_enable(sc, 17, on); 371 default: 372 break; 373 } 374 375 printf("%s: 0x%08x\n", __func__, idx); 376 } 377 378 uint32_t 379 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells) 380 { 381 struct mvclock_softc *sc = cookie; 382 uint32_t idx = cells[0]; 383 uint32_t freq; 384 385 switch (idx) { 386 case PERIPH_SB_GBE_CORE: 387 freq = a3700_periph_tbg_get_frequency(sc, 8); 388 freq /= a3700_periph_get_double_div(sc, 389 PERIPH_DIV_SEL1, 18, 21); 390 return freq; 391 case PERIPH_SB_GBE1_CORE: 392 idx = PERIPH_SB_GBE_CORE; 393 freq = a3700_periph_sb_get_frequency(sc, &idx); 394 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL1, 13) + 1; 395 return freq; 396 case PERIPH_SB_GBE0_CORE: 397 idx = PERIPH_SB_GBE_CORE; 398 freq = a3700_periph_sb_get_frequency(sc, &idx); 399 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL1, 14) + 1; 400 return freq; 401 default: 402 break; 403 } 404 405 printf("%s: 0x%08x\n", __func__, idx); 406 return 0; 407 } 408 409 void 410 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on) 411 { 412 uint32_t reg; 413 414 reg = HREAD4(sc, PERIPH_CLK_DIS); 415 reg &= ~(1 << idx); 416 if (!on) 417 reg |= (1 << idx); 418 HWRITE4(sc, PERIPH_CLK_DIS, reg); 419 } 420 421 uint32_t 422 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx) 423 { 424 uint32_t reg; 425 426 reg = HREAD4(sc, PERIPH_TBG_SEL); 427 reg >>= idx; 428 reg &= PERIPH_TBG_SEL_MASK; 429 430 return clock_get_frequency_idx(sc->sc_cd.cd_node, reg); 431 } 432 433 uint32_t 434 a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx) 435 { 436 uint32_t reg = HREAD4(sc, off); 437 return ((reg >> idx) & PERIPH_DIV_SEL_MASK); 438 } 439 440 uint32_t 441 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off, 442 uint32_t idx0, uint32_t idx1) 443 { 444 uint32_t reg = HREAD4(sc, off); 445 return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) * 446 ((reg >> idx1) & PERIPH_DIV_SEL_MASK); 447 } 448 449 /* Armada 3700 TBG block */ 450 451 #define TBG_A_P 0 452 #define TBG_B_P 1 453 #define TBG_A_S 2 454 #define TBG_B_S 3 455 456 #define TBG_CTRL0 0x4 457 #define TBG_A_FBDIV_SHIFT 2 458 #define TBG_B_FBDIV_SHIFT 18 459 #define TBG_CTRL1 0x8 460 #define TBG_A_VCODIV_SE_SHIFT 0 461 #define TBG_B_VCODIV_SE_SHIFT 16 462 #define TBG_CTRL7 0x20 463 #define TBG_A_REFDIV_SHIFT 0 464 #define TBG_B_REFDIV_SHIFT 16 465 #define TBG_CTRL8 0x30 466 #define TBG_A_VCODIV_DIFF_SHIFT 1 467 #define TBG_B_VCODIV_DIFF_SHIFT 17 468 #define TBG_DIV_MASK 0x1ff 469 470 uint32_t 471 a3700_tbg_get_frequency(void *cookie, uint32_t *cells) 472 { 473 struct mvclock_softc *sc = cookie; 474 uint32_t idx = cells[0]; 475 uint64_t mult, div, freq; 476 uint32_t reg, vcodiv; 477 478 switch (idx) { 479 case TBG_A_P: 480 vcodiv = HREAD4(sc, TBG_CTRL8); 481 vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT; 482 vcodiv &= TBG_DIV_MASK; 483 break; 484 case TBG_B_P: 485 vcodiv = HREAD4(sc, TBG_CTRL8); 486 vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT; 487 vcodiv &= TBG_DIV_MASK; 488 break; 489 case TBG_A_S: 490 vcodiv = HREAD4(sc, TBG_CTRL1); 491 vcodiv >>= TBG_A_VCODIV_SE_SHIFT; 492 vcodiv &= TBG_DIV_MASK; 493 break; 494 case TBG_B_S: 495 vcodiv = HREAD4(sc, TBG_CTRL1); 496 vcodiv >>= TBG_B_VCODIV_SE_SHIFT; 497 vcodiv &= TBG_DIV_MASK; 498 break; 499 default: 500 printf("%s: 0x%08x\n", __func__, idx); 501 return 0; 502 } 503 504 reg = HREAD4(sc, TBG_CTRL0); 505 if (idx == TBG_A_P || idx == TBG_A_S) 506 reg >>= TBG_A_FBDIV_SHIFT; 507 else 508 reg >>= TBG_B_FBDIV_SHIFT; 509 reg &= TBG_DIV_MASK; 510 mult = reg << 2; 511 512 reg = HREAD4(sc, TBG_CTRL7); 513 if (idx == TBG_A_P || idx == TBG_A_S) 514 reg >>= TBG_A_REFDIV_SHIFT; 515 else 516 reg >>= TBG_B_REFDIV_SHIFT; 517 reg &= TBG_DIV_MASK; 518 div = reg; 519 520 if (div == 0) 521 div = 1; 522 div *= 1 << vcodiv; 523 524 freq = clock_get_frequency(sc->sc_cd.cd_node, NULL); 525 return (freq * mult) / div; 526 } 527