1 /* $OpenBSD: mvclock.c,v 1.7 2020/05/22 10:06:59 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 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_SDIO 4 160 #define CP110_GATE_SLOW_IO 21 161 162 uint32_t 163 cp110_get_frequency(void *cookie, uint32_t *cells) 164 { 165 struct mvclock_softc *sc = cookie; 166 uint32_t mod = cells[0]; 167 uint32_t idx = cells[1]; 168 uint32_t parent[2] = { 0, 0 }; 169 170 /* Core clocks */ 171 if (mod == 0) { 172 switch (idx) { 173 case CP110_CORE_APLL: 174 /* fixed PLL at 1GHz */ 175 return 1000000000; 176 case CP110_CORE_PPV2: 177 /* PPv2 clock is APLL/3 */ 178 return 333333333; 179 case CP110_CORE_X2CORE: 180 /* X2CORE clock is APLL/2 */ 181 return 500000000; 182 case CP110_CORE_CORE: 183 /* Core clock is X2CORE/2 */ 184 return 250000000; 185 case CP110_CORE_SDIO: 186 /* SDIO clock is APLL/2.5 */ 187 return 400000000; 188 default: 189 break; 190 } 191 } 192 193 /* Gatable clocks */ 194 if (mod == 1) { 195 switch (idx) { 196 case CP110_GATE_SDIO: 197 parent[1] = CP110_CORE_SDIO; 198 break; 199 case CP110_GATE_SLOW_IO: 200 parent[1] = CP110_CORE_X2CORE; 201 break; 202 default: 203 break; 204 } 205 206 if (parent[1] != 0) 207 return cp110_get_frequency(sc, parent); 208 } 209 210 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 211 return 0; 212 } 213 214 void 215 cp110_enable(void *cookie, uint32_t *cells, int on) 216 { 217 struct mvclock_softc *sc = cookie; 218 uint32_t mod = cells[0]; 219 uint32_t idx = cells[1]; 220 221 /* Gatable clocks */ 222 if (mod == 1 && idx < 32) { 223 struct regmap *rm; 224 uint32_t reg; 225 226 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); 227 if (rm == NULL) { 228 printf("%s: can't enable clock 0x%08x 0x%08x\n", 229 sc->sc_dev.dv_xname, mod, idx); 230 return; 231 } 232 reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL); 233 if (on) 234 reg |= (1U << idx); 235 else 236 reg &= ~(1U << idx); 237 regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg); 238 return; 239 } 240 241 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); 242 } 243 244 /* Armada 3700 Periph block */ 245 246 #define PERIPH_NB_MMC 0x0 247 #define PERIPH_NB_SQF 0x7 248 #define PERIPH_NB_I2C2 0x9 249 #define PERIPH_NB_I2C1 0xa 250 #define PERIPH_NB_CPU 0x10 251 #define PERIPH_SB_GBE1_CORE 0x7 252 #define PERIPH_SB_GBE0_CORE 0x8 253 #define PERIPH_SB_USB32_USB2_SYS 0xb 254 #define PERIPH_SB_USB32_SS_SYS 0xc 255 256 #define PERIPH_TBG_SEL 0x0 257 #define PERIPH_TBG_SEL_MASK 0x3 258 #define PERIPH_DIV_SEL0 0x4 259 #define PERIPH_DIV_SEL1 0x8 260 #define PERIPH_DIV_SEL2 0xc 261 #define PERIPH_DIV_SEL_MASK 0x7 262 #define PERIPH_CLK_SEL 0x10 263 #define PERIPH_CLK_DIS 0x14 264 265 void a3700_periph_enable(struct mvclock_softc *, uint32_t, int); 266 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t); 267 uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t); 268 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t, 269 uint32_t, uint32_t); 270 271 void 272 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on) 273 { 274 struct mvclock_softc *sc = cookie; 275 uint32_t idx = cells[0]; 276 277 switch (idx) { 278 case PERIPH_NB_MMC: 279 return a3700_periph_enable(sc, 2, on); 280 case PERIPH_NB_SQF: 281 return a3700_periph_enable(sc, 12, on); 282 case PERIPH_NB_I2C2: 283 return a3700_periph_enable(sc, 16, on); 284 case PERIPH_NB_I2C1: 285 return a3700_periph_enable(sc, 17, on); 286 default: 287 break; 288 } 289 290 printf("%s: 0x%08x\n", __func__, idx); 291 } 292 293 uint32_t 294 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells) 295 { 296 struct mvclock_softc *sc = cookie; 297 uint32_t idx = cells[0]; 298 uint32_t freq; 299 300 switch (idx) { 301 case PERIPH_NB_MMC: 302 freq = a3700_periph_tbg_get_frequency(sc, 0); 303 freq /= a3700_periph_get_double_div(sc, 304 PERIPH_DIV_SEL2, 16, 13); 305 return freq; 306 case PERIPH_NB_SQF: 307 freq = a3700_periph_tbg_get_frequency(sc, 12); 308 freq /= a3700_periph_get_double_div(sc, 309 PERIPH_DIV_SEL1, 27, 24); 310 return freq; 311 case PERIPH_NB_CPU: 312 freq = a3700_periph_tbg_get_frequency(sc, 22); 313 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28); 314 return freq; 315 default: 316 break; 317 } 318 319 printf("%s: 0x%08x\n", __func__, idx); 320 return 0; 321 } 322 323 void 324 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on) 325 { 326 struct mvclock_softc *sc = cookie; 327 uint32_t idx = cells[0]; 328 329 switch (idx) { 330 case PERIPH_SB_GBE1_CORE: 331 return a3700_periph_enable(sc, 4, on); 332 case PERIPH_SB_GBE0_CORE: 333 return a3700_periph_enable(sc, 5, on); 334 case PERIPH_SB_USB32_USB2_SYS: 335 return a3700_periph_enable(sc, 16, on); 336 case PERIPH_SB_USB32_SS_SYS: 337 return a3700_periph_enable(sc, 17, on); 338 default: 339 break; 340 } 341 342 printf("%s: 0x%08x\n", __func__, idx); 343 } 344 345 uint32_t 346 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells) 347 { 348 uint32_t idx = cells[0]; 349 350 printf("%s: 0x%08x\n", __func__, idx); 351 return 0; 352 } 353 354 void 355 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on) 356 { 357 uint32_t reg; 358 359 reg = HREAD4(sc, PERIPH_CLK_DIS); 360 reg &= ~(1 << idx); 361 if (!on) 362 reg |= (1 << idx); 363 HWRITE4(sc, PERIPH_CLK_DIS, reg); 364 } 365 366 uint32_t 367 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx) 368 { 369 uint32_t reg; 370 371 reg = HREAD4(sc, PERIPH_TBG_SEL); 372 reg >>= idx; 373 reg &= PERIPH_TBG_SEL_MASK; 374 375 return clock_get_frequency_idx(sc->sc_cd.cd_node, reg); 376 } 377 378 uint32_t 379 a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx) 380 { 381 uint32_t reg = HREAD4(sc, off); 382 return ((reg >> idx) & PERIPH_DIV_SEL_MASK); 383 } 384 385 uint32_t 386 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off, 387 uint32_t idx0, uint32_t idx1) 388 { 389 uint32_t reg = HREAD4(sc, off); 390 return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) * 391 ((reg >> idx1) & PERIPH_DIV_SEL_MASK); 392 } 393 394 /* Armada 3700 TBG block */ 395 396 #define TBG_A_P 0 397 #define TBG_B_P 1 398 #define TBG_A_S 2 399 #define TBG_B_S 3 400 401 #define TBG_CTRL0 0x4 402 #define TBG_A_FBDIV_SHIFT 2 403 #define TBG_B_FBDIV_SHIFT 18 404 #define TBG_CTRL1 0x8 405 #define TBG_A_VCODIV_SE_SHIFT 0 406 #define TBG_B_VCODIV_SE_SHIFT 16 407 #define TBG_CTRL7 0x20 408 #define TBG_A_REFDIV_SHIFT 0 409 #define TBG_B_REFDIV_SHIFT 16 410 #define TBG_CTRL8 0x30 411 #define TBG_A_VCODIV_DIFF_SHIFT 1 412 #define TBG_B_VCODIV_DIFF_SHIFT 17 413 #define TBG_DIV_MASK 0x1ff 414 415 uint32_t 416 a3700_tbg_get_frequency(void *cookie, uint32_t *cells) 417 { 418 struct mvclock_softc *sc = cookie; 419 uint32_t idx = cells[0]; 420 uint64_t mult, div, freq; 421 uint32_t reg, vcodiv; 422 423 switch (idx) { 424 case TBG_A_P: 425 vcodiv = HREAD4(sc, TBG_CTRL8); 426 vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT; 427 vcodiv &= TBG_DIV_MASK; 428 break; 429 case TBG_B_P: 430 vcodiv = HREAD4(sc, TBG_CTRL8); 431 vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT; 432 vcodiv &= TBG_DIV_MASK; 433 break; 434 case TBG_A_S: 435 vcodiv = HREAD4(sc, TBG_CTRL1); 436 vcodiv >>= TBG_A_VCODIV_SE_SHIFT; 437 vcodiv &= TBG_DIV_MASK; 438 break; 439 case TBG_B_S: 440 vcodiv = HREAD4(sc, TBG_CTRL1); 441 vcodiv >>= TBG_B_VCODIV_SE_SHIFT; 442 vcodiv &= TBG_DIV_MASK; 443 break; 444 default: 445 printf("%s: 0x%08x\n", __func__, idx); 446 return 0; 447 } 448 449 reg = HREAD4(sc, TBG_CTRL0); 450 if (idx == TBG_A_P || idx == TBG_A_S) 451 reg >>= TBG_A_FBDIV_SHIFT; 452 else 453 reg >>= TBG_B_FBDIV_SHIFT; 454 reg &= TBG_DIV_MASK; 455 mult = reg << 2; 456 457 reg = HREAD4(sc, TBG_CTRL7); 458 if (idx == TBG_A_P || idx == TBG_A_S) 459 reg >>= TBG_A_REFDIV_SHIFT; 460 else 461 reg >>= TBG_B_REFDIV_SHIFT; 462 reg &= TBG_DIV_MASK; 463 div = reg; 464 465 if (div == 0) 466 div = 1; 467 div *= 1 << vcodiv; 468 469 freq = clock_get_frequency(sc->sc_cd.cd_node, NULL); 470 return (freq * mult) / div; 471 } 472