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