1 /* $OpenBSD: exclock.c,v 1.9 2021/03/25 04:12:01 jsg Exp $ */ 2 /* 3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> 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/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/ofw/openfirm.h> 26 #include <dev/ofw/ofw_clock.h> 27 #include <dev/ofw/fdt.h> 28 29 /* registers */ 30 #define CLOCK_APLL_CON0 0x0100 31 #define CLOCK_APLL_CON1 0x0104 32 #define CLOCK_BPLL_CON0 0x0110 33 #define CLOCK_BPLL_CON1 0x0114 34 #define CLOCK_EPLL_CON0 0x0130 35 #define CLOCK_EPLL_CON1 0x0134 36 #define CLOCK_EPLL_CON2 0x0138 37 #define CLOCK_VPLL_CON0 0x0140 38 #define CLOCK_VPLL_CON1 0x0144 39 #define CLOCK_VPLL_CON2 0x0148 40 #define CLOCK_CLK_DIV_CPU0 0x0500 41 #define CLOCK_CLK_DIV_CPU1 0x0504 42 #define CLOCK_CLK_DIV_TOP0 0x0510 43 #define CLOCK_CLK_DIV_TOP1 0x0514 44 #define CLOCK_PLL_DIV2_SEL 0x0A24 45 #define CLOCK_MPLL_CON0 0x4100 46 #define CLOCK_MPLL_CON1 0x4104 47 48 /* bits and bytes */ 49 #define MPLL_FOUT_SEL_SHIFT 0x4 50 #define MPLL_FOUT_SEL_MASK 0x1 51 #define BPLL_FOUT_SEL_SHIFT 0x0 52 #define BPLL_FOUT_SEL_MASK 0x1 53 54 #define HCLK_FREQ 24000000 55 56 #define HREAD4(sc, reg) \ 57 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 58 #define HWRITE4(sc, reg, val) \ 59 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 60 #define HSET4(sc, reg, bits) \ 61 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 62 #define HCLR4(sc, reg, bits) \ 63 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 64 65 struct exclock_softc { 66 struct device sc_dev; 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_ioh; 69 70 struct clock_device sc_cd; 71 }; 72 73 enum clocks { 74 /* OSC */ 75 OSC, /* 24 MHz OSC */ 76 77 /* PLLs */ 78 APLL, /* ARM core clock */ 79 MPLL, /* System bus clock for memory controller */ 80 BPLL, /* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */ 81 CPLL, /* Multi Format Video Hardware Codec clock */ 82 GPLL, /* Graphic 3D processor clock or other clocks for DVFS flexibility */ 83 EPLL, /* Audio interface clocks and clocks for other external device interfaces */ 84 VPLL, /* dithered PLL, helps to reduce the EMI of display and camera */ 85 KPLL, 86 }; 87 88 struct exclock_softc *exclock_sc; 89 90 int exclock_match(struct device *, void *, void *); 91 void exclock_attach(struct device *, struct device *, void *); 92 uint32_t exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int); 93 uint32_t exclock_get_pll_clk(struct exclock_softc *, enum clocks); 94 uint32_t exclock_get_armclk(struct exclock_softc *); 95 uint32_t exclock_get_kfcclk(struct exclock_softc *); 96 unsigned int exclock_get_i2cclk(void); 97 98 struct cfattach exclock_ca = { 99 sizeof (struct exclock_softc), exclock_match, exclock_attach 100 }; 101 102 struct cfdriver exclock_cd = { 103 NULL, "exclock", DV_DULL 104 }; 105 106 uint32_t exynos5250_get_frequency(void *, uint32_t *); 107 int exynos5250_set_frequency(void *, uint32_t *, uint32_t); 108 void exynos5250_enable(void *, uint32_t *, int); 109 uint32_t exynos5420_get_frequency(void *, uint32_t *); 110 int exynos5420_set_frequency(void *, uint32_t *, uint32_t); 111 void exynos5420_enable(void *, uint32_t *, int); 112 113 int 114 exclock_match(struct device *parent, void *match, void *aux) 115 { 116 struct fdt_attach_args *faa = aux; 117 118 if (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock") || 119 OF_is_compatible(faa->fa_node, "samsung,exynos5800-clock")) 120 return 10; /* Must beat syscon(4). */ 121 122 return 0; 123 } 124 125 void 126 exclock_attach(struct device *parent, struct device *self, void *aux) 127 { 128 struct exclock_softc *sc = (struct exclock_softc *)self; 129 struct fdt_attach_args *faa = aux; 130 131 sc->sc_iot = faa->fa_iot; 132 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 133 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 134 panic("%s: bus_space_map failed!", __func__); 135 136 exclock_sc = sc; 137 138 printf("\n"); 139 140 if (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock")) { 141 /* Exynos 5250 */ 142 sc->sc_cd.cd_enable = exynos5250_enable; 143 sc->sc_cd.cd_get_frequency = exynos5250_get_frequency; 144 sc->sc_cd.cd_set_frequency = exynos5250_set_frequency; 145 } else { 146 /* Exynos 5420/5800 */ 147 sc->sc_cd.cd_enable = exynos5420_enable; 148 sc->sc_cd.cd_get_frequency = exynos5420_get_frequency; 149 sc->sc_cd.cd_set_frequency = exynos5420_set_frequency; 150 } 151 152 sc->sc_cd.cd_node = faa->fa_node; 153 sc->sc_cd.cd_cookie = sc; 154 clock_register(&sc->sc_cd); 155 } 156 157 /* 158 * Exynos 5250 159 */ 160 161 /* Clocks */ 162 #define EXYNOS5250_CLK_ARM_CLK 9 163 164 uint32_t 165 exynos5250_get_frequency(void *cookie, uint32_t *cells) 166 { 167 struct exclock_softc *sc = cookie; 168 uint32_t idx = cells[0]; 169 170 switch (idx) { 171 case EXYNOS5250_CLK_ARM_CLK: 172 return exclock_get_armclk(sc); 173 } 174 175 printf("%s: 0x%08x\n", __func__, idx); 176 return 0; 177 } 178 179 int 180 exynos5250_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 181 { 182 uint32_t idx = cells[0]; 183 184 switch (idx) { 185 case EXYNOS5250_CLK_ARM_CLK: 186 return -1; 187 } 188 189 printf("%s: 0x%08x\n", __func__, idx); 190 return -1; 191 } 192 193 void 194 exynos5250_enable(void *cookie, uint32_t *cells, int on) 195 { 196 uint32_t idx = cells[0]; 197 198 printf("%s: 0x%08x\n", __func__, idx); 199 } 200 201 /* 202 * Exynos 5420/5800 203 */ 204 205 /* Clocks */ 206 #define EXYNOS5420_CLK_FIN_PLL 1 207 #define EXYNOS5420_CLK_FOUT_RPLL 6 208 #define EXYNOS5420_CLK_FOUT_SPLL 8 209 #define EXYNOS5420_CLK_ARM_CLK 13 210 #define EXYNOS5420_CLK_KFC_CLK 14 211 #define EXYNOS5420_CLK_SCLK_MMC2 134 212 #define EXYNOS5420_CLK_MMC2 353 213 #define EXYNOS5420_CLK_USBH20 365 214 #define EXYNOS5420_CLK_USBD300 366 215 #define EXYNOS5420_CLK_USBD301 367 216 #define EXYNOS5420_CLK_SCLK_SPLL -1 217 218 /* Registers */ 219 #define EXYNOS5420_RPLL_CON0 0x10140 220 #define EXYNOS5420_RPLL_CON1 0x10144 221 #define EXYNOS5420_SPLL_CON0 0x10160 222 #define EXYNOS5420_SRC_TOP6 0x10218 223 #define EXYNOS5420_DIV_FSYS1 0x1054c 224 #define EXYNOS5420_SRC_FSYS 0x10244 225 #define EXYNOS5420_GATE_TOP_SCLK_FSYS 0x10840 226 #define EXYNOS5420_GATE_IP_FSYS 0x10944 227 #define EXYNOS5420_KPLL_CON0 0x28100 228 #define EXYNOS5420_SRC_KFC 0x28200 229 #define EXYNOS5420_DIV_KFC0 0x28500 230 231 uint32_t 232 exynos5420_get_frequency(void *cookie, uint32_t *cells) 233 { 234 struct exclock_softc *sc = cookie; 235 uint32_t idx = cells[0]; 236 uint32_t reg, div, mux; 237 uint32_t kdiv, mdiv, pdiv, sdiv; 238 uint64_t freq; 239 240 switch (idx) { 241 case EXYNOS5420_CLK_FIN_PLL: 242 return 24000000; 243 case EXYNOS5420_CLK_ARM_CLK: 244 return exclock_get_armclk(sc); 245 case EXYNOS5420_CLK_KFC_CLK: 246 return exclock_get_kfcclk(sc); 247 case EXYNOS5420_CLK_SCLK_MMC2: 248 reg = HREAD4(sc, EXYNOS5420_DIV_FSYS1); 249 div = ((reg >> 20) & ((1 << 10) - 1)) + 1; 250 reg = HREAD4(sc, EXYNOS5420_SRC_FSYS); 251 mux = ((reg >> 16) & ((1 << 3) - 1)); 252 switch (mux) { 253 case 0: 254 idx = EXYNOS5420_CLK_FIN_PLL; 255 break; 256 case 4: 257 idx = EXYNOS5420_CLK_SCLK_SPLL; 258 break; 259 default: 260 idx = 0; 261 break; 262 } 263 return exynos5420_get_frequency(sc, &idx) / div; 264 case EXYNOS5420_CLK_SCLK_SPLL: 265 reg = HREAD4(sc, EXYNOS5420_SRC_TOP6); 266 mux = ((reg >> 8) & ((1 << 1) - 1)); 267 switch (mux) { 268 case 0: 269 idx = EXYNOS5420_CLK_FIN_PLL; 270 break; 271 case 1: 272 idx = EXYNOS5420_CLK_FOUT_SPLL; 273 break; 274 } 275 return exynos5420_get_frequency(sc, &idx); 276 case EXYNOS5420_CLK_FOUT_RPLL: 277 reg = HREAD4(sc, EXYNOS5420_RPLL_CON0); 278 mdiv = (reg >> 16) & 0x1ff; 279 pdiv = (reg >> 8) & 0x3f; 280 sdiv = (reg >> 0) & 0x7; 281 reg = HREAD4(sc, EXYNOS5420_RPLL_CON1); 282 kdiv = (reg >> 0) & 0xffff; 283 idx = EXYNOS5420_CLK_FIN_PLL; 284 freq = exynos5420_get_frequency(sc, &idx); 285 freq = ((mdiv << 16) + kdiv) * freq / (pdiv * (1 << sdiv)); 286 return (freq >> 16); 287 case EXYNOS5420_CLK_FOUT_SPLL: 288 reg = HREAD4(sc, EXYNOS5420_SPLL_CON0); 289 mdiv = (reg >> 16) & 0x3ff; 290 pdiv = (reg >> 8) & 0x3f; 291 sdiv = (reg >> 0) & 0x7; 292 idx = EXYNOS5420_CLK_FIN_PLL; 293 freq = exynos5420_get_frequency(sc, &idx); 294 return mdiv * freq / (pdiv * (1 << sdiv)); 295 } 296 297 printf("%s: 0x%08x\n", __func__, idx); 298 return 0; 299 } 300 301 int 302 exynos5420_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 303 { 304 uint32_t idx = cells[0]; 305 306 switch (idx) { 307 case EXYNOS5420_CLK_ARM_CLK: 308 case EXYNOS5420_CLK_KFC_CLK: 309 return -1; 310 } 311 312 printf("%s: 0x%08x\n", __func__, idx); 313 return -1; 314 } 315 316 void 317 exynos5420_enable(void *cookie, uint32_t *cells, int on) 318 { 319 uint32_t idx = cells[0]; 320 321 switch (idx) { 322 case EXYNOS5420_CLK_SCLK_MMC2: /* CLK_GATE_TOP_SCLK_FSYS */ 323 case EXYNOS5420_CLK_MMC2: /* CLK_GATE_IP_FSYS */ 324 case EXYNOS5420_CLK_USBH20: /* CLK_GATE_IP_FSYS */ 325 case EXYNOS5420_CLK_USBD300: /* CLK_GATE_IP_FSYS */ 326 case EXYNOS5420_CLK_USBD301: /* CLK_GATE_IP_FSYS */ 327 /* Enabled by default. */ 328 return; 329 } 330 331 printf("%s: 0x%08x\n", __func__, idx); 332 } 333 334 uint32_t 335 exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k) 336 { 337 uint64_t freq; 338 uint32_t m, p, s = 0, mask, fout; 339 /* 340 * APLL_CON: MIDV [25:16] 341 * MPLL_CON: MIDV [25:16] 342 * EPLL_CON: MIDV [24:16] 343 * VPLL_CON: MIDV [24:16] 344 * BPLL_CON: MIDV [25:16]: Exynos5 345 */ 346 347 switch (pll) 348 { 349 case APLL: 350 case MPLL: 351 case BPLL: 352 case KPLL: 353 mask = 0x3ff; 354 break; 355 default: 356 mask = 0x1ff; 357 } 358 359 m = (r >> 16) & mask; 360 361 /* PDIV [13:8] */ 362 p = (r >> 8) & 0x3f; 363 /* SDIV [2:0] */ 364 s = r & 0x7; 365 366 freq = HCLK_FREQ; 367 368 if (pll == EPLL) { 369 k = k & 0xffff; 370 /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */ 371 fout = (m + k / 65536) * (freq / (p * (1 << s))); 372 } else if (pll == VPLL) { 373 k = k & 0xfff; 374 /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */ 375 fout = (m + k / 1024) * (freq / (p * (1 << s))); 376 } else { 377 /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */ 378 fout = m * (freq / (p * (1 << s))); 379 } 380 381 return fout; 382 } 383 384 uint32_t 385 exclock_get_pll_clk(struct exclock_softc *sc, enum clocks pll) 386 { 387 uint32_t freq; 388 389 switch (pll) { 390 case APLL: 391 freq = exclock_decode_pll_clk(pll, 392 HREAD4(sc, CLOCK_APLL_CON0), 0); 393 break; 394 case MPLL: 395 freq = exclock_decode_pll_clk(pll, 396 HREAD4(sc, CLOCK_MPLL_CON0), 0); 397 break; 398 case BPLL: 399 freq = exclock_decode_pll_clk(pll, 400 HREAD4(sc, CLOCK_BPLL_CON0), 0); 401 break; 402 case EPLL: 403 freq = exclock_decode_pll_clk(pll, 404 HREAD4(sc, CLOCK_EPLL_CON0), 405 HREAD4(sc, CLOCK_EPLL_CON1)); 406 break; 407 case VPLL: 408 freq = exclock_decode_pll_clk(pll, 409 HREAD4(sc, CLOCK_VPLL_CON0), 410 HREAD4(sc, CLOCK_VPLL_CON1)); 411 break; 412 case KPLL: 413 freq = exclock_decode_pll_clk(pll, 414 HREAD4(sc, EXYNOS5420_KPLL_CON0), 0); 415 break; 416 default: 417 return 0; 418 } 419 420 /* 421 * According to the user manual, in EVT1 MPLL and BPLL always gives 422 * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock. 423 */ 424 if (pll == MPLL || pll == BPLL) { 425 uint32_t freq_sel; 426 uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL); 427 428 switch (pll) { 429 case MPLL: 430 freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT) 431 & MPLL_FOUT_SEL_MASK; 432 break; 433 case BPLL: 434 freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT) 435 & BPLL_FOUT_SEL_MASK; 436 break; 437 default: 438 freq_sel = -1; 439 break; 440 } 441 442 if (freq_sel == 0) 443 freq /= 2; 444 } 445 446 return freq; 447 } 448 449 uint32_t 450 exclock_get_armclk(struct exclock_softc *sc) 451 { 452 uint32_t div, armclk, arm_ratio, arm2_ratio; 453 454 div = HREAD4(sc, CLOCK_CLK_DIV_CPU0); 455 456 /* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */ 457 arm_ratio = (div >> 0) & 0x7; 458 arm2_ratio = (div >> 28) & 0x7; 459 460 armclk = exclock_get_pll_clk(sc, APLL) / (arm_ratio + 1); 461 armclk /= (arm2_ratio + 1); 462 463 return armclk; 464 } 465 466 uint32_t 467 exclock_get_kfcclk(struct exclock_softc *sc) 468 { 469 uint32_t div, kfc_ratio; 470 471 div = HREAD4(sc, EXYNOS5420_DIV_KFC0); 472 473 /* KFC_RATIO: [2:0] */ 474 kfc_ratio = (div >> 0) & 0x7; 475 476 return exclock_get_pll_clk(sc, KPLL) / (kfc_ratio + 1); 477 } 478 479 unsigned int 480 exclock_get_i2cclk(void) 481 { 482 struct exclock_softc *sc = exclock_sc; 483 uint32_t aclk_66, aclk_66_pre, div, ratio; 484 485 div = HREAD4(sc, CLOCK_CLK_DIV_TOP1); 486 ratio = (div >> 24) & 0x7; 487 aclk_66_pre = exclock_get_pll_clk(sc, MPLL) / (ratio + 1); 488 div = HREAD4(sc, CLOCK_CLK_DIV_TOP0); 489 ratio = (div >> 0) & 0x7; 490 aclk_66 = aclk_66_pre / (ratio + 1); 491 492 return aclk_66 / 1000; 493 } 494