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