1 /* $OpenBSD: mvclock.c,v 1.3 2019/04/30 20:00:25 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_SB_GBE1_CORE 0x7 251 #define PERIPH_SB_GBE0_CORE 0x8 252 #define PERIPH_SB_USB32_USB2_SYS 0xb 253 #define PERIPH_SB_USB32_SS_SYS 0xc 254 255 #define PERIPH_TBG_SEL 0x0 256 #define PERIPH_TBG_SEL_MASK 0x3 257 #define PERIPH_DIV_SEL0 0x4 258 #define PERIPH_DIV_SEL1 0x8 259 #define PERIPH_DIV_SEL2 0xc 260 #define PERIPH_DIV_SEL_MASK 0x7 261 #define PERIPH_CLK_SEL 0x10 262 #define PERIPH_CLK_DIS 0x14 263 264 void a3700_periph_enable(struct mvclock_softc *, uint32_t, int); 265 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t); 266 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t, 267 uint32_t, uint32_t); 268 269 void 270 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on) 271 { 272 struct mvclock_softc *sc = cookie; 273 uint32_t idx = cells[0]; 274 275 switch (idx) { 276 case PERIPH_NB_MMC: 277 return a3700_periph_enable(sc, 2, on); 278 default: 279 break; 280 } 281 282 printf("%s: 0x%08x\n", __func__, idx); 283 } 284 285 uint32_t 286 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells) 287 { 288 struct mvclock_softc *sc = cookie; 289 uint32_t idx = cells[0]; 290 uint32_t freq; 291 292 switch (idx) { 293 case PERIPH_NB_MMC: 294 freq = a3700_periph_tbg_get_frequency(sc, 0); 295 freq /= a3700_periph_get_double_div(sc, 296 PERIPH_DIV_SEL2, 16, 13); 297 return freq; 298 default: 299 break; 300 } 301 302 printf("%s: 0x%08x\n", __func__, idx); 303 return 0; 304 } 305 306 void 307 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on) 308 { 309 struct mvclock_softc *sc = cookie; 310 uint32_t idx = cells[0]; 311 312 switch (idx) { 313 case PERIPH_SB_GBE1_CORE: 314 return a3700_periph_enable(sc, 4, on); 315 case PERIPH_SB_GBE0_CORE: 316 return a3700_periph_enable(sc, 5, on); 317 case PERIPH_SB_USB32_USB2_SYS: 318 return a3700_periph_enable(sc, 16, on); 319 case PERIPH_SB_USB32_SS_SYS: 320 return a3700_periph_enable(sc, 17, on); 321 default: 322 break; 323 } 324 325 printf("%s: 0x%08x\n", __func__, idx); 326 } 327 328 uint32_t 329 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells) 330 { 331 uint32_t idx = cells[0]; 332 333 printf("%s: 0x%08x\n", __func__, idx); 334 return 0; 335 } 336 337 void 338 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on) 339 { 340 uint32_t reg; 341 342 reg = HREAD4(sc, PERIPH_CLK_DIS); 343 reg &= ~(1 << idx); 344 if (!on) 345 reg |= (1 << idx); 346 HWRITE4(sc, PERIPH_CLK_DIS, reg); 347 } 348 349 uint32_t 350 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx) 351 { 352 uint32_t reg; 353 354 reg = HREAD4(sc, PERIPH_TBG_SEL); 355 reg >>= idx; 356 reg &= PERIPH_TBG_SEL_MASK; 357 358 return clock_get_frequency_idx(sc->sc_cd.cd_node, reg); 359 } 360 361 uint32_t 362 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off, 363 uint32_t idx0, uint32_t idx1) 364 { 365 uint32_t reg = HREAD4(sc, off); 366 return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) * 367 ((reg >> idx1) & PERIPH_DIV_SEL_MASK); 368 } 369 370 /* Armada 3700 TBG block */ 371 372 #define TBG_A_P 0 373 #define TBG_B_P 1 374 #define TBG_A_S 2 375 #define TBG_B_S 3 376 377 #define TBG_CTRL0 0x4 378 #define TBG_A_FBDIV_SHIFT 2 379 #define TBG_B_FBDIV_SHIFT 18 380 #define TBG_CTRL1 0x8 381 #define TBG_A_VCODIV_SE_SHIFT 0 382 #define TBG_B_VCODIV_SE_SHIFT 16 383 #define TBG_CTRL7 0x20 384 #define TBG_A_REFDIV_SHIFT 0 385 #define TBG_B_REFDIV_SHIFT 16 386 #define TBG_CTRL8 0x30 387 #define TBG_A_VCODIV_DIFF_SHIFT 1 388 #define TBG_B_VCODIV_DIFF_SHIFT 17 389 #define TBG_DIV_MASK 0x1ff 390 391 uint32_t 392 a3700_tbg_get_frequency(void *cookie, uint32_t *cells) 393 { 394 struct mvclock_softc *sc = cookie; 395 uint32_t idx = cells[0]; 396 uint64_t mult, div, freq; 397 uint32_t reg, vcodiv; 398 399 switch (idx) { 400 case TBG_A_P: 401 vcodiv = HREAD4(sc, TBG_CTRL8); 402 vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT; 403 vcodiv &= TBG_DIV_MASK; 404 break; 405 case TBG_B_P: 406 vcodiv = HREAD4(sc, TBG_CTRL8); 407 vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT; 408 vcodiv &= TBG_DIV_MASK; 409 break; 410 case TBG_A_S: 411 vcodiv = HREAD4(sc, TBG_CTRL1); 412 vcodiv >>= TBG_A_VCODIV_SE_SHIFT; 413 vcodiv &= TBG_DIV_MASK; 414 break; 415 case TBG_B_S: 416 vcodiv = HREAD4(sc, TBG_CTRL1); 417 vcodiv >>= TBG_B_VCODIV_SE_SHIFT; 418 vcodiv &= TBG_DIV_MASK; 419 break; 420 default: 421 printf("%s: 0x%08x\n", __func__, idx); 422 return 0; 423 } 424 425 reg = HREAD4(sc, TBG_CTRL0); 426 if (idx == TBG_A_P || idx == TBG_A_S) 427 reg >>= TBG_A_FBDIV_SHIFT; 428 else 429 reg >>= TBG_B_FBDIV_SHIFT; 430 reg &= TBG_DIV_MASK; 431 mult = reg << 2; 432 433 reg = HREAD4(sc, TBG_CTRL7); 434 if (idx == TBG_A_P || idx == TBG_A_S) 435 reg >>= TBG_A_REFDIV_SHIFT; 436 else 437 reg >>= TBG_B_REFDIV_SHIFT; 438 reg &= TBG_DIV_MASK; 439 div = reg; 440 441 if (div == 0) 442 div = 1; 443 div *= 1 << vcodiv; 444 445 freq = clock_get_frequency(sc->sc_cd.cd_node, NULL); 446 return (freq * mult) / div; 447 } 448 449 /* Armada 3700 XTAL block */ 450 451 #define XTAL 0xc 452 #define XTAL_MODE (1 << 31) 453 454 uint32_t 455 a3700_xtal_get_frequency(void *cookie, uint32_t *cells) 456 { 457 struct mvclock_softc *sc = cookie; 458 struct regmap *rm; 459 460 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); 461 KASSERT(rm != NULL); 462 463 if (regmap_read_4(rm, XTAL) & XTAL_MODE) 464 return 40000000; 465 else 466 return 25000000; 467 } 468