1 /* $OpenBSD: mvclock.c,v 1.9 2021/10/24 17:52:26 mpi 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 return (OF_is_compatible(node, "marvell,ap806-clock") || 76 OF_is_compatible(node, "marvell,cp110-clock") || 77 OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") || 78 OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") || 79 OF_is_compatible(node, "marvell,armada-3700-tbg-clock") || 80 OF_is_compatible(node, "marvell,armada-3700-xtal-clock")); 81 } 82 83 void 84 mvclock_attach(struct device *parent, struct device *self, void *aux) 85 { 86 struct mvclock_softc *sc = (struct mvclock_softc *)self; 87 struct fdt_attach_args *faa = aux; 88 int node = faa->fa_node; 89 90 if (faa->fa_nreg > 0) { 91 sc->sc_iot = faa->fa_iot; 92 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 93 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 94 printf(": can't map registers\n"); 95 return; 96 } 97 } 98 99 printf("\n"); 100 101 sc->sc_cd.cd_node = node; 102 sc->sc_cd.cd_cookie = sc; 103 if (OF_is_compatible(node, "marvell,ap806-clock")) { 104 sc->sc_cd.cd_get_frequency = ap806_get_frequency; 105 } else if (OF_is_compatible(node, "marvell,cp110-clock")) { 106 sc->sc_cd.cd_get_frequency = cp110_get_frequency; 107 sc->sc_cd.cd_enable = cp110_enable; 108 } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) { 109 sc->sc_cd.cd_enable = a3700_periph_nb_enable; 110 sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency; 111 } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) { 112 sc->sc_cd.cd_enable = a3700_periph_sb_enable; 113 sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency; 114 } else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) { 115 sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency; 116 } 117 clock_register(&sc->sc_cd); 118 } 119 120 /* AP806 block */ 121 122 #define AP806_CORE_FIXED 2 123 #define AP806_CORE_MSS 3 124 #define AP806_CORE_SDIO 4 125 126 uint32_t 127 ap806_get_frequency(void *cookie, uint32_t *cells) 128 { 129 uint32_t idx = cells[0]; 130 131 switch (idx) { 132 case AP806_CORE_FIXED: 133 /* fixed PLL at 1200MHz */ 134 return 1200000000; 135 case AP806_CORE_MSS: 136 /* MSS clock is fixed clock divided by 6 */ 137 return 200000000; 138 case AP806_CORE_SDIO: 139 /* SDIO/eMMC clock is fixed clock divided by 3 */ 140 return 400000000; 141 default: 142 break; 143 } 144 145 printf("%s: 0x%08x\n", __func__, idx); 146 return 0; 147 } 148 149 /* CP110 block */ 150 151 #define CP110_PM_CLOCK_GATING_CTRL 0x220 152 153 #define CP110_CORE_APLL 0 154 #define CP110_CORE_PPV2 1 155 #define CP110_CORE_X2CORE 2 156 #define CP110_CORE_CORE 3 157 #define CP110_CORE_SDIO 5 158 159 #define CP110_GATE_PPV2 3 160 #define CP110_GATE_SDIO 4 161 #define CP110_GATE_SLOW_IO 21 162 163 uint32_t 164 cp110_get_frequency(void *cookie, uint32_t *cells) 165 { 166 struct mvclock_softc *sc = cookie; 167 uint32_t mod = cells[0]; 168 uint32_t idx = cells[1]; 169 uint32_t parent[2] = { 0, 0 }; 170 171 /* Core clocks */ 172 if (mod == 0) { 173 switch (idx) { 174 case CP110_CORE_APLL: 175 /* fixed PLL at 1GHz */ 176 return 1000000000; 177 case CP110_CORE_PPV2: 178 /* PPv2 clock is APLL/3 */ 179 return 333333333; 180 case CP110_CORE_X2CORE: 181 /* X2CORE clock is APLL/2 */ 182 return 500000000; 183 case CP110_CORE_CORE: 184 /* Core clock is X2CORE/2 */ 185 return 250000000; 186 case CP110_CORE_SDIO: 187 /* SDIO clock is APLL/2.5 */ 188 return 400000000; 189 default: 190 break; 191 } 192 } 193 194 /* Gatable clocks */ 195 if (mod == 1) { 196 switch (idx) { 197 case CP110_GATE_PPV2: 198 parent[1] = CP110_CORE_PPV2; 199 break; 200 case CP110_GATE_SDIO: 201 parent[1] = CP110_CORE_SDIO; 202 break; 203 case CP110_GATE_SLOW_IO: 204 parent[1] = CP110_CORE_X2CORE; 205 break; 206 default: 207 break; 208 } 209 210 if (parent[1] != 0) 211 return cp110_get_frequency(sc, parent); 212 } 213 214 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 215 return 0; 216 } 217 218 void 219 cp110_enable(void *cookie, uint32_t *cells, int on) 220 { 221 struct mvclock_softc *sc = cookie; 222 uint32_t mod = cells[0]; 223 uint32_t idx = cells[1]; 224 225 /* Gatable clocks */ 226 if (mod == 1 && idx < 32) { 227 struct regmap *rm; 228 uint32_t reg; 229 230 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); 231 if (rm == NULL) { 232 printf("%s: can't enable clock 0x%08x 0x%08x\n", 233 sc->sc_dev.dv_xname, mod, idx); 234 return; 235 } 236 reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL); 237 if (on) 238 reg |= (1U << idx); 239 else 240 reg &= ~(1U << idx); 241 regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg); 242 return; 243 } 244 245 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 246 } 247 248 /* Armada 3700 Periph block */ 249 250 #define PERIPH_NB_MMC 0x0 251 #define PERIPH_NB_SQF 0x7 252 #define PERIPH_NB_I2C2 0x9 253 #define PERIPH_NB_I2C1 0xa 254 #define PERIPH_NB_CPU 0x10 255 #define PERIPH_SB_GBE1_CORE 0x7 256 #define PERIPH_SB_GBE0_CORE 0x8 257 #define PERIPH_SB_USB32_USB2_SYS 0xb 258 #define PERIPH_SB_USB32_SS_SYS 0xc 259 260 #define PERIPH_TBG_SEL 0x0 261 #define PERIPH_TBG_SEL_MASK 0x3 262 #define PERIPH_DIV_SEL0 0x4 263 #define PERIPH_DIV_SEL1 0x8 264 #define PERIPH_DIV_SEL2 0xc 265 #define PERIPH_DIV_SEL_MASK 0x7 266 #define PERIPH_CLK_SEL 0x10 267 #define PERIPH_CLK_DIS 0x14 268 269 void a3700_periph_enable(struct mvclock_softc *, uint32_t, int); 270 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t); 271 uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t); 272 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t, 273 uint32_t, uint32_t); 274 275 void 276 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on) 277 { 278 struct mvclock_softc *sc = cookie; 279 uint32_t idx = cells[0]; 280 281 switch (idx) { 282 case PERIPH_NB_MMC: 283 return a3700_periph_enable(sc, 2, on); 284 case PERIPH_NB_SQF: 285 return a3700_periph_enable(sc, 12, on); 286 case PERIPH_NB_I2C2: 287 return a3700_periph_enable(sc, 16, on); 288 case PERIPH_NB_I2C1: 289 return a3700_periph_enable(sc, 17, on); 290 default: 291 break; 292 } 293 294 printf("%s: 0x%08x\n", __func__, idx); 295 } 296 297 uint32_t 298 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells) 299 { 300 struct mvclock_softc *sc = cookie; 301 uint32_t idx = cells[0]; 302 uint32_t freq; 303 304 switch (idx) { 305 case PERIPH_NB_MMC: 306 freq = a3700_periph_tbg_get_frequency(sc, 0); 307 freq /= a3700_periph_get_double_div(sc, 308 PERIPH_DIV_SEL2, 16, 13); 309 return freq; 310 case PERIPH_NB_SQF: 311 freq = a3700_periph_tbg_get_frequency(sc, 12); 312 freq /= a3700_periph_get_double_div(sc, 313 PERIPH_DIV_SEL1, 27, 24); 314 return freq; 315 case PERIPH_NB_CPU: 316 freq = a3700_periph_tbg_get_frequency(sc, 22); 317 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28); 318 return freq; 319 default: 320 break; 321 } 322 323 printf("%s: 0x%08x\n", __func__, idx); 324 return 0; 325 } 326 327 void 328 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on) 329 { 330 struct mvclock_softc *sc = cookie; 331 uint32_t idx = cells[0]; 332 333 switch (idx) { 334 case PERIPH_SB_GBE1_CORE: 335 return a3700_periph_enable(sc, 4, on); 336 case PERIPH_SB_GBE0_CORE: 337 return a3700_periph_enable(sc, 5, on); 338 case PERIPH_SB_USB32_USB2_SYS: 339 return a3700_periph_enable(sc, 16, on); 340 case PERIPH_SB_USB32_SS_SYS: 341 return a3700_periph_enable(sc, 17, on); 342 default: 343 break; 344 } 345 346 printf("%s: 0x%08x\n", __func__, idx); 347 } 348 349 uint32_t 350 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells) 351 { 352 uint32_t idx = cells[0]; 353 354 printf("%s: 0x%08x\n", __func__, idx); 355 return 0; 356 } 357 358 void 359 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on) 360 { 361 uint32_t reg; 362 363 reg = HREAD4(sc, PERIPH_CLK_DIS); 364 reg &= ~(1 << idx); 365 if (!on) 366 reg |= (1 << idx); 367 HWRITE4(sc, PERIPH_CLK_DIS, reg); 368 } 369 370 uint32_t 371 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx) 372 { 373 uint32_t reg; 374 375 reg = HREAD4(sc, PERIPH_TBG_SEL); 376 reg >>= idx; 377 reg &= PERIPH_TBG_SEL_MASK; 378 379 return clock_get_frequency_idx(sc->sc_cd.cd_node, reg); 380 } 381 382 uint32_t 383 a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx) 384 { 385 uint32_t reg = HREAD4(sc, off); 386 return ((reg >> idx) & PERIPH_DIV_SEL_MASK); 387 } 388 389 uint32_t 390 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off, 391 uint32_t idx0, uint32_t idx1) 392 { 393 uint32_t reg = HREAD4(sc, off); 394 return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) * 395 ((reg >> idx1) & PERIPH_DIV_SEL_MASK); 396 } 397 398 /* Armada 3700 TBG block */ 399 400 #define TBG_A_P 0 401 #define TBG_B_P 1 402 #define TBG_A_S 2 403 #define TBG_B_S 3 404 405 #define TBG_CTRL0 0x4 406 #define TBG_A_FBDIV_SHIFT 2 407 #define TBG_B_FBDIV_SHIFT 18 408 #define TBG_CTRL1 0x8 409 #define TBG_A_VCODIV_SE_SHIFT 0 410 #define TBG_B_VCODIV_SE_SHIFT 16 411 #define TBG_CTRL7 0x20 412 #define TBG_A_REFDIV_SHIFT 0 413 #define TBG_B_REFDIV_SHIFT 16 414 #define TBG_CTRL8 0x30 415 #define TBG_A_VCODIV_DIFF_SHIFT 1 416 #define TBG_B_VCODIV_DIFF_SHIFT 17 417 #define TBG_DIV_MASK 0x1ff 418 419 uint32_t 420 a3700_tbg_get_frequency(void *cookie, uint32_t *cells) 421 { 422 struct mvclock_softc *sc = cookie; 423 uint32_t idx = cells[0]; 424 uint64_t mult, div, freq; 425 uint32_t reg, vcodiv; 426 427 switch (idx) { 428 case TBG_A_P: 429 vcodiv = HREAD4(sc, TBG_CTRL8); 430 vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT; 431 vcodiv &= TBG_DIV_MASK; 432 break; 433 case TBG_B_P: 434 vcodiv = HREAD4(sc, TBG_CTRL8); 435 vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT; 436 vcodiv &= TBG_DIV_MASK; 437 break; 438 case TBG_A_S: 439 vcodiv = HREAD4(sc, TBG_CTRL1); 440 vcodiv >>= TBG_A_VCODIV_SE_SHIFT; 441 vcodiv &= TBG_DIV_MASK; 442 break; 443 case TBG_B_S: 444 vcodiv = HREAD4(sc, TBG_CTRL1); 445 vcodiv >>= TBG_B_VCODIV_SE_SHIFT; 446 vcodiv &= TBG_DIV_MASK; 447 break; 448 default: 449 printf("%s: 0x%08x\n", __func__, idx); 450 return 0; 451 } 452 453 reg = HREAD4(sc, TBG_CTRL0); 454 if (idx == TBG_A_P || idx == TBG_A_S) 455 reg >>= TBG_A_FBDIV_SHIFT; 456 else 457 reg >>= TBG_B_FBDIV_SHIFT; 458 reg &= TBG_DIV_MASK; 459 mult = reg << 2; 460 461 reg = HREAD4(sc, TBG_CTRL7); 462 if (idx == TBG_A_P || idx == TBG_A_S) 463 reg >>= TBG_A_REFDIV_SHIFT; 464 else 465 reg >>= TBG_B_REFDIV_SHIFT; 466 reg &= TBG_DIV_MASK; 467 div = reg; 468 469 if (div == 0) 470 div = 1; 471 div *= 1 << vcodiv; 472 473 freq = clock_get_frequency(sc->sc_cd.cd_node, NULL); 474 return (freq * mult) / div; 475 } 476