tegra124_cpufreq.c revision 308336
1/*- 2 * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 308336 2016-11-05 11:00:19Z mmel $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/cpu.h> 34#include <sys/kernel.h> 35#include <sys/lock.h> 36#include <sys/malloc.h> 37#include <sys/module.h> 38 39#include <machine/bus.h> 40#include <machine/cpu.h> 41 42#include <dev/extres/clk/clk.h> 43#include <dev/extres/regulator/regulator.h> 44#include <dev/ofw/ofw_bus_subr.h> 45 46#include <arm/nvidia/tegra_efuse.h> 47 48#include "cpufreq_if.h" 49 50#define XXX 51 52/* CPU voltage table entry */ 53struct speedo_entry { 54 uint64_t freq; /* Frequency point */ 55 int c0; /* Coeeficient values for */ 56 int c1; /* quadratic equation: */ 57 int c2; /* c2 * speedo^2 + c1 * speedo + c0 */ 58}; 59 60struct cpu_volt_def { 61 int min_uvolt; /* Min allowed CPU voltage */ 62 int max_uvolt; /* Max allowed CPU voltage */ 63 int step_uvolt; /* Step of CPU voltage */ 64 int speedo_scale; /* Scaling factor for cvt */ 65 int speedo_nitems; /* Size of speedo table */ 66 struct speedo_entry *speedo_tbl; /* CPU voltage table */ 67}; 68 69struct cpu_speed_point { 70 uint64_t freq; /* Frequecy */ 71 int uvolt; /* Requested voltage */ 72}; 73 74static struct speedo_entry tegra124_speedo_dpll_tbl[] = 75{ 76 { 204000000ULL, 1112619, -29295, 402}, 77 { 306000000ULL, 1150460, -30585, 402}, 78 { 408000000ULL, 1190122, -31865, 402}, 79 { 510000000ULL, 1231606, -33155, 402}, 80 { 612000000ULL, 1274912, -34435, 402}, 81 { 714000000ULL, 1320040, -35725, 402}, 82 { 816000000ULL, 1366990, -37005, 402}, 83 { 918000000ULL, 1415762, -38295, 402}, 84 {1020000000ULL, 1466355, -39575, 402}, 85 {1122000000ULL, 1518771, -40865, 402}, 86 {1224000000ULL, 1573009, -42145, 402}, 87 {1326000000ULL, 1629068, -43435, 402}, 88 {1428000000ULL, 1686950, -44715, 402}, 89 {1530000000ULL, 1746653, -46005, 402}, 90 {1632000000ULL, 1808179, -47285, 402}, 91 {1734000000ULL, 1871526, -48575, 402}, 92 {1836000000ULL, 1936696, -49855, 402}, 93 {1938000000ULL, 2003687, -51145, 402}, 94 {2014500000ULL, 2054787, -52095, 402}, 95 {2116500000ULL, 2124957, -53385, 402}, 96 {2218500000ULL, 2196950, -54665, 402}, 97 {2320500000ULL, 2270765, -55955, 402}, 98 {2320500000ULL, 2270765, -55955, 402}, 99 {2422500000ULL, 2346401, -57235, 402}, 100 {2524500000ULL, 2437299, -58535, 402}, 101}; 102 103static struct cpu_volt_def tegra124_cpu_volt_dpll_def = 104{ 105 .min_uvolt = 900000, /* 0.9 V */ 106 .max_uvolt = 1260000, /* 1.26 */ 107 .step_uvolt = 10000, /* 10 mV */ 108 .speedo_scale = 100, 109 .speedo_nitems = nitems(tegra124_speedo_dpll_tbl), 110 .speedo_tbl = tegra124_speedo_dpll_tbl, 111}; 112 113static struct speedo_entry tegra124_speedo_pllx_tbl[] = 114{ 115 { 204000000ULL, 800000, 0, 0}, 116 { 306000000ULL, 800000, 0, 0}, 117 { 408000000ULL, 800000, 0, 0}, 118 { 510000000ULL, 800000, 0, 0}, 119 { 612000000ULL, 800000, 0, 0}, 120 { 714000000ULL, 800000, 0, 0}, 121 { 816000000ULL, 820000, 0, 0}, 122 { 918000000ULL, 840000, 0, 0}, 123 {1020000000ULL, 880000, 0, 0}, 124 {1122000000ULL, 900000, 0, 0}, 125 {1224000000ULL, 930000, 0, 0}, 126 {1326000000ULL, 960000, 0, 0}, 127 {1428000000ULL, 990000, 0, 0}, 128 {1530000000ULL, 1020000, 0, 0}, 129 {1632000000ULL, 1070000, 0, 0}, 130 {1734000000ULL, 1100000, 0, 0}, 131 {1836000000ULL, 1140000, 0, 0}, 132 {1938000000ULL, 1180000, 0, 0}, 133 {2014500000ULL, 1220000, 0, 0}, 134 {2116500000ULL, 1260000, 0, 0}, 135 {2218500000ULL, 1310000, 0, 0}, 136 {2320500000ULL, 1360000, 0, 0}, 137 {2397000000ULL, 1400000, 0, 0}, 138 {2499000000ULL, 1400000, 0, 0}, 139}; 140 141 142static struct cpu_volt_def tegra124_cpu_volt_pllx_def = 143{ 144 .min_uvolt = 1000000, /* XXX 0.9 V doesn't work on all boards */ 145 .max_uvolt = 1260000, /* 1.26 */ 146 .step_uvolt = 10000, /* 10 mV */ 147 .speedo_scale = 100, 148 .speedo_nitems = nitems(tegra124_speedo_pllx_tbl), 149 .speedo_tbl = tegra124_speedo_pllx_tbl, 150}; 151 152static uint64_t cpu_freq_tbl[] = { 153 204000000ULL, 154 306000000ULL, 155 408000000ULL, 156 510000000ULL, 157 612000000ULL, 158 714000000ULL, 159 816000000ULL, 160 918000000ULL, 161 1020000000ULL, 162 1122000000ULL, 163 1224000000ULL, 164 1326000000ULL, 165 1428000000ULL, 166 1530000000ULL, 167 1632000000ULL, 168 1734000000ULL, 169 1836000000ULL, 170 1938000000ULL, 171 2014000000ULL, 172 2116000000ULL, 173 2218000000ULL, 174 2320000000ULL, 175 2422000000ULL, 176 2524000000ULL, 177}; 178 179static uint64_t cpu_max_freq[] = { 180 2014500000ULL, 181 2320500000ULL, 182 2116500000ULL, 183 2524500000ULL, 184}; 185 186struct tegra124_cpufreq_softc { 187 device_t dev; 188 phandle_t node; 189 190 regulator_t supply_vdd_cpu; 191 clk_t clk_cpu_g; 192 clk_t clk_cpu_lp; 193 clk_t clk_pll_x; 194 clk_t clk_pll_p; 195 clk_t clk_dfll; 196 197 int process_id; 198 int speedo_id; 199 int speedo_value; 200 201 uint64_t cpu_max_freq; 202 struct cpu_volt_def *cpu_def; 203 struct cpu_speed_point *speed_points; 204 int nspeed_points; 205 206 struct cpu_speed_point *act_speed_point; 207 208 int latency; 209}; 210 211static int cpufreq_lowest_freq = 1; 212TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq); 213 214#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div)) 215 216#define ROUND_UP(val, div) roundup(val, div) 217#define ROUND_DOWN(val, div) rounddown(val, div) 218 219/* 220 * Compute requesetd voltage for given frequency and SoC process variations, 221 * - compute base voltage from speedo value using speedo table 222 * - round up voltage to next regulator step 223 * - clamp it to regulator limits 224 */ 225static int 226freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq) 227{ 228 int uv, scale, min_uvolt, max_uvolt, step_uvolt; 229 struct speedo_entry *ent; 230 int i; 231 232 /* Get speedo entry with higher frequency */ 233 ent = NULL; 234 for (i = 0; i < sc->cpu_def->speedo_nitems; i++) { 235 if (sc->cpu_def->speedo_tbl[i].freq >= freq) { 236 ent = &sc->cpu_def->speedo_tbl[i]; 237 break; 238 } 239 } 240 if (ent == NULL) 241 ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1]; 242 scale = sc->cpu_def->speedo_scale; 243 244 245 /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */ 246 uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale); 247 uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) + 248 ent->c0; 249 step_uvolt = sc->cpu_def->step_uvolt; 250 /* Round up it to next regulator step */ 251 uv = ROUND_UP(uv, step_uvolt); 252 253 /* Clamp result */ 254 min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt); 255 max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt); 256 if (uv < min_uvolt) 257 uv = min_uvolt; 258 if (uv > max_uvolt) 259 uv = max_uvolt; 260 return (uv); 261 262} 263 264static void 265build_speed_points(struct tegra124_cpufreq_softc *sc) { 266 int i; 267 268 sc->nspeed_points = nitems(cpu_freq_tbl); 269 sc->speed_points = malloc(sizeof(struct cpu_speed_point) * 270 sc->nspeed_points, M_DEVBUF, M_NOWAIT); 271 for (i = 0; i < sc->nspeed_points; i++) { 272 sc->speed_points[i].freq = cpu_freq_tbl[i]; 273 sc->speed_points[i].uvolt = freq_to_voltage(sc, 274 cpu_freq_tbl[i]); 275 } 276} 277 278static struct cpu_speed_point * 279get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq) 280{ 281 int i; 282 283 if (sc->speed_points[0].freq >= freq) 284 return (sc->speed_points + 0); 285 286 for (i = 0; i < sc->nspeed_points - 1; i++) { 287 if (sc->speed_points[i + 1].freq > freq) 288 return (sc->speed_points + i); 289 } 290 291 return (sc->speed_points + sc->nspeed_points - 1); 292} 293 294static int 295tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) 296{ 297 struct tegra124_cpufreq_softc *sc; 298 int i, j, max_cnt; 299 300 if (sets == NULL || count == NULL) 301 return (EINVAL); 302 303 sc = device_get_softc(dev); 304 memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); 305 306 max_cnt = min(sc->nspeed_points, *count); 307 for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) { 308 if (sc->cpu_max_freq < sc->speed_points[j].freq) 309 continue; 310 sets[i].freq = sc->speed_points[j].freq / 1000000; 311 sets[i].volts = sc->speed_points[j].uvolt / 1000; 312 sets[i].lat = sc->latency; 313 sets[i].dev = dev; 314 i++; 315 } 316 *count = i; 317 318 return (0); 319} 320 321static int 322set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq) 323{ 324 struct cpu_speed_point *point; 325 int rv; 326 327 point = get_speed_point(sc, freq); 328 329 if (sc->act_speed_point->uvolt < point->uvolt) { 330 /* set cpu voltage */ 331 rv = regulator_set_voltage(sc->supply_vdd_cpu, 332 point->uvolt, point->uvolt); 333 DELAY(10000); 334 if (rv != 0) 335 return (rv); 336 } 337 338 /* Switch supermux to PLLP first */ 339 rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p); 340 if (rv != 0) { 341 device_printf(sc->dev, "Can't set parent to PLLP\n"); 342 return (rv); 343 } 344 345 /* Set PLLX frequency */ 346 rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN); 347 if (rv != 0) { 348 device_printf(sc->dev, "Can't set CPU clock frequency\n"); 349 return (rv); 350 } 351 352 rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x); 353 if (rv != 0) { 354 device_printf(sc->dev, "Can't set parent to PLLX\n"); 355 return (rv); 356 } 357 358 if (sc->act_speed_point->uvolt > point->uvolt) { 359 /* set cpu voltage */ 360 rv = regulator_set_voltage(sc->supply_vdd_cpu, 361 point->uvolt, point->uvolt); 362 if (rv != 0) 363 return (rv); 364 } 365 366 sc->act_speed_point = point; 367 368 return (0); 369} 370 371static int 372tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf) 373{ 374 struct tegra124_cpufreq_softc *sc; 375 uint64_t freq; 376 int rv; 377 378 if (cf == NULL || cf->freq < 0) 379 return (EINVAL); 380 381 sc = device_get_softc(dev); 382 383 freq = cf->freq; 384 if (freq < cpufreq_lowest_freq) 385 freq = cpufreq_lowest_freq; 386 freq *= 1000000; 387 if (freq >= sc->cpu_max_freq) 388 freq = sc->cpu_max_freq; 389 rv = set_cpu_freq(sc, freq); 390 391 return (rv); 392} 393 394static int 395tegra124_cpufreq_get(device_t dev, struct cf_setting *cf) 396{ 397 struct tegra124_cpufreq_softc *sc; 398 399 if (cf == NULL) 400 return (EINVAL); 401 402 sc = device_get_softc(dev); 403 memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 404 cf->dev = NULL; 405 cf->freq = sc->act_speed_point->freq / 1000000; 406 cf->volts = sc->act_speed_point->uvolt / 1000; 407 /* Transition latency in us. */ 408 cf->lat = sc->latency; 409 /* Driver providing this setting. */ 410 cf->dev = dev; 411 412 return (0); 413} 414 415 416static int 417tegra124_cpufreq_type(device_t dev, int *type) 418{ 419 420 if (type == NULL) 421 return (EINVAL); 422 *type = CPUFREQ_TYPE_ABSOLUTE; 423 424 return (0); 425} 426 427static int 428get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node) 429{ 430 int rv; 431 device_t parent_dev; 432 433 parent_dev = device_get_parent(sc->dev); 434 rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply", 435 &sc->supply_vdd_cpu); 436 if (rv != 0) { 437 device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n"); 438 return (rv); 439 } 440 441 rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g); 442 if (rv != 0) { 443 device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv); 444 return (ENXIO); 445 } 446 447 rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp); 448 if (rv != 0) { 449 device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n"); 450 return (ENXIO); 451 } 452 453 rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x); 454 if (rv != 0) { 455 device_printf(sc->dev, "Cannot get 'pll_x' clock\n"); 456 return (ENXIO); 457 } 458 rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p); 459 if (rv != 0) { 460 device_printf(parent_dev, "Cannot get 'pll_p' clock\n"); 461 return (ENXIO); 462 } 463 rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll); 464 if (rv != 0) { 465 /* XXX DPLL is not implemented yet */ 466/* 467 device_printf(sc->dev, "Cannot get 'dfll' clock\n"); 468 return (ENXIO); 469*/ 470 } 471 return (0); 472} 473 474static void 475tegra124_cpufreq_identify(driver_t *driver, device_t parent) 476{ 477 478 if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL) 479 return; 480 if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL) 481 device_printf(parent, "add child failed\n"); 482} 483 484static int 485tegra124_cpufreq_probe(device_t dev) 486{ 487 488 if (device_get_unit(dev) != 0) 489 return (ENXIO); 490 device_set_desc(dev, "CPU Frequency Control"); 491 492 return (0); 493} 494 495static int 496tegra124_cpufreq_attach(device_t dev) 497{ 498 struct tegra124_cpufreq_softc *sc; 499 uint64_t freq; 500 int rv; 501 502 sc = device_get_softc(dev); 503 sc->dev = dev; 504 sc->node = ofw_bus_get_node(device_get_parent(dev)); 505 506 sc->process_id = tegra_sku_info.cpu_process_id; 507 sc->speedo_id = tegra_sku_info.cpu_speedo_id; 508 sc->speedo_value = tegra_sku_info.cpu_speedo_value; 509 510 /* Tegra 124 */ 511 /* XXX DPLL is not implemented yet */ 512 if (1) 513 sc->cpu_def = &tegra124_cpu_volt_pllx_def; 514 else 515 sc->cpu_def = &tegra124_cpu_volt_dpll_def; 516 517 518 rv = get_fdt_resources(sc, sc->node); 519 if (rv != 0) { 520 return (rv); 521 } 522 523 build_speed_points(sc); 524 525 rv = clk_get_freq(sc->clk_cpu_g, &freq); 526 if (rv != 0) { 527 device_printf(dev, "Can't get CPU clock frequency\n"); 528 return (rv); 529 } 530 if (sc->speedo_id < nitems(cpu_max_freq)) 531 sc->cpu_max_freq = cpu_max_freq[sc->speedo_id]; 532 else 533 sc->cpu_max_freq = cpu_max_freq[0]; 534 sc->act_speed_point = get_speed_point(sc, freq); 535 536 /* Set safe startup CPU frequency. */ 537 rv = set_cpu_freq(sc, 1632000000); 538 if (rv != 0) { 539 device_printf(dev, "Can't set initial CPU clock frequency\n"); 540 return (rv); 541 } 542 543 /* This device is controlled by cpufreq(4). */ 544 cpufreq_register(dev); 545 546 return (0); 547} 548 549static int 550tegra124_cpufreq_detach(device_t dev) 551{ 552 struct tegra124_cpufreq_softc *sc; 553 554 sc = device_get_softc(dev); 555 cpufreq_unregister(dev); 556 557 if (sc->supply_vdd_cpu != NULL) 558 regulator_release(sc->supply_vdd_cpu); 559 560 if (sc->clk_cpu_g != NULL) 561 clk_release(sc->clk_cpu_g); 562 if (sc->clk_cpu_lp != NULL) 563 clk_release(sc->clk_cpu_lp); 564 if (sc->clk_pll_x != NULL) 565 clk_release(sc->clk_pll_x); 566 if (sc->clk_pll_p != NULL) 567 clk_release(sc->clk_pll_p); 568 if (sc->clk_dfll != NULL) 569 clk_release(sc->clk_dfll); 570 return (0); 571} 572 573static device_method_t tegra124_cpufreq_methods[] = { 574 /* Device interface */ 575 DEVMETHOD(device_identify, tegra124_cpufreq_identify), 576 DEVMETHOD(device_probe, tegra124_cpufreq_probe), 577 DEVMETHOD(device_attach, tegra124_cpufreq_attach), 578 DEVMETHOD(device_detach, tegra124_cpufreq_detach), 579 580 /* cpufreq interface */ 581 DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set), 582 DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get), 583 DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings), 584 DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type), 585 586 DEVMETHOD_END 587}; 588 589static devclass_t tegra124_cpufreq_devclass; 590static DEFINE_CLASS_0(cpufreq, tegra124_cpufreq_driver, 591 tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc)); 592DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, 593 tegra124_cpufreq_devclass, NULL, NULL); 594