1296936Smmel/*- 2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3296936Smmel * All rights reserved. 4296936Smmel * 5296936Smmel * Redistribution and use in source and binary forms, with or without 6296936Smmel * modification, are permitted provided that the following conditions 7296936Smmel * are met: 8296936Smmel * 1. Redistributions of source code must retain the above copyright 9296936Smmel * notice, this list of conditions and the following disclaimer. 10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright 11296936Smmel * notice, this list of conditions and the following disclaimer in the 12296936Smmel * documentation and/or other materials provided with the distribution. 13296936Smmel * 14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17296936Smmel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24296936Smmel * SUCH DAMAGE. 25296936Smmel */ 26296936Smmel 27296936Smmel#include <sys/cdefs.h> 28296936Smmel__FBSDID("$FreeBSD: releng/11.0/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 301288 2016-06-03 21:11:34Z pfg $"); 29296936Smmel 30296936Smmel#include <sys/param.h> 31296936Smmel#include <sys/systm.h> 32296936Smmel#include <sys/bus.h> 33296936Smmel#include <sys/cpu.h> 34296936Smmel#include <sys/kernel.h> 35296936Smmel#include <sys/lock.h> 36296936Smmel#include <sys/malloc.h> 37296936Smmel#include <sys/module.h> 38296936Smmel 39296936Smmel#include <machine/bus.h> 40296936Smmel#include <machine/cpu.h> 41296936Smmel 42296936Smmel#include <dev/extres/clk/clk.h> 43296936Smmel#include <dev/extres/regulator/regulator.h> 44296936Smmel#include <dev/ofw/ofw_bus_subr.h> 45296936Smmel 46296936Smmel#include <arm/nvidia/tegra_efuse.h> 47296936Smmel 48296936Smmel#include "cpufreq_if.h" 49296936Smmel 50296936Smmel#define XXX 51296936Smmel 52296936Smmel/* CPU voltage table entry */ 53296936Smmelstruct speedo_entry { 54296936Smmel uint64_t freq; /* Frequency point */ 55296936Smmel int c0; /* Coeeficient values for */ 56296936Smmel int c1; /* quadratic equation: */ 57296936Smmel int c2; /* c2 * speedo^2 + c1 * speedo + c0 */ 58296936Smmel}; 59296936Smmel 60296936Smmelstruct cpu_volt_def { 61296936Smmel int min_uvolt; /* Min allowed CPU voltage */ 62296936Smmel int max_uvolt; /* Max allowed CPU voltage */ 63296936Smmel int step_uvolt; /* Step of CPU voltage */ 64296936Smmel int speedo_scale; /* Scaling factor for cvt */ 65296936Smmel int speedo_nitems; /* Size of speedo table */ 66296936Smmel struct speedo_entry *speedo_tbl; /* CPU voltage table */ 67296936Smmel}; 68296936Smmel 69296936Smmelstruct cpu_speed_point { 70296936Smmel uint64_t freq; /* Frequecy */ 71296936Smmel int uvolt; /* Requested voltage */ 72296936Smmel}; 73296936Smmel 74296936Smmelstatic struct speedo_entry tegra124_speedo_dpll_tbl[] = 75296936Smmel{ 76296936Smmel { 204000000ULL, 1112619, -29295, 402}, 77296936Smmel { 306000000ULL, 1150460, -30585, 402}, 78296936Smmel { 408000000ULL, 1190122, -31865, 402}, 79296936Smmel { 510000000ULL, 1231606, -33155, 402}, 80296936Smmel { 612000000ULL, 1274912, -34435, 402}, 81296936Smmel { 714000000ULL, 1320040, -35725, 402}, 82296936Smmel { 816000000ULL, 1366990, -37005, 402}, 83296936Smmel { 918000000ULL, 1415762, -38295, 402}, 84296936Smmel {1020000000ULL, 1466355, -39575, 402}, 85296936Smmel {1122000000ULL, 1518771, -40865, 402}, 86296936Smmel {1224000000ULL, 1573009, -42145, 402}, 87296936Smmel {1326000000ULL, 1629068, -43435, 402}, 88296936Smmel {1428000000ULL, 1686950, -44715, 402}, 89296936Smmel {1530000000ULL, 1746653, -46005, 402}, 90296936Smmel {1632000000ULL, 1808179, -47285, 402}, 91296936Smmel {1734000000ULL, 1871526, -48575, 402}, 92296936Smmel {1836000000ULL, 1936696, -49855, 402}, 93296936Smmel {1938000000ULL, 2003687, -51145, 402}, 94296936Smmel {2014500000ULL, 2054787, -52095, 402}, 95296936Smmel {2116500000ULL, 2124957, -53385, 402}, 96296936Smmel {2218500000ULL, 2196950, -54665, 402}, 97296936Smmel {2320500000ULL, 2270765, -55955, 402}, 98296936Smmel {2320500000ULL, 2270765, -55955, 402}, 99296936Smmel {2422500000ULL, 2346401, -57235, 402}, 100296936Smmel {2524500000ULL, 2437299, -58535, 402}, 101296936Smmel}; 102296936Smmel 103296936Smmelstatic struct cpu_volt_def tegra124_cpu_volt_dpll_def = 104296936Smmel{ 105296936Smmel .min_uvolt = 900000, /* 0.9 V */ 106296936Smmel .max_uvolt = 1260000, /* 1.26 */ 107296936Smmel .step_uvolt = 10000, /* 10 mV */ 108296936Smmel .speedo_scale = 100, 109296936Smmel .speedo_nitems = nitems(tegra124_speedo_dpll_tbl), 110296936Smmel .speedo_tbl = tegra124_speedo_dpll_tbl, 111296936Smmel}; 112296936Smmel 113296936Smmelstatic struct speedo_entry tegra124_speedo_pllx_tbl[] = 114296936Smmel{ 115296936Smmel { 204000000ULL, 800000, 0, 0}, 116296936Smmel { 306000000ULL, 800000, 0, 0}, 117296936Smmel { 408000000ULL, 800000, 0, 0}, 118296936Smmel { 510000000ULL, 800000, 0, 0}, 119296936Smmel { 612000000ULL, 800000, 0, 0}, 120296936Smmel { 714000000ULL, 800000, 0, 0}, 121296936Smmel { 816000000ULL, 820000, 0, 0}, 122296936Smmel { 918000000ULL, 840000, 0, 0}, 123296936Smmel {1020000000ULL, 880000, 0, 0}, 124296936Smmel {1122000000ULL, 900000, 0, 0}, 125296936Smmel {1224000000ULL, 930000, 0, 0}, 126296936Smmel {1326000000ULL, 960000, 0, 0}, 127296936Smmel {1428000000ULL, 990000, 0, 0}, 128296936Smmel {1530000000ULL, 1020000, 0, 0}, 129296936Smmel {1632000000ULL, 1070000, 0, 0}, 130296936Smmel {1734000000ULL, 1100000, 0, 0}, 131296936Smmel {1836000000ULL, 1140000, 0, 0}, 132296936Smmel {1938000000ULL, 1180000, 0, 0}, 133296936Smmel {2014500000ULL, 1220000, 0, 0}, 134296936Smmel {2116500000ULL, 1260000, 0, 0}, 135296936Smmel {2218500000ULL, 1310000, 0, 0}, 136296936Smmel {2320500000ULL, 1360000, 0, 0}, 137296936Smmel {2397000000ULL, 1400000, 0, 0}, 138296936Smmel {2499000000ULL, 1400000, 0, 0}, 139296936Smmel}; 140296936Smmel 141296936Smmel 142296936Smmelstatic struct cpu_volt_def tegra124_cpu_volt_pllx_def = 143296936Smmel{ 144296936Smmel .min_uvolt = 900000, /* 0.9 V */ 145296936Smmel .max_uvolt = 1260000, /* 1.26 */ 146296936Smmel .step_uvolt = 10000, /* 10 mV */ 147296936Smmel .speedo_scale = 100, 148296936Smmel .speedo_nitems = nitems(tegra124_speedo_pllx_tbl), 149296936Smmel .speedo_tbl = tegra124_speedo_pllx_tbl, 150296936Smmel}; 151296936Smmel 152296936Smmelstatic uint64_t cpu_freq_tbl[] = { 153296936Smmel 204000000ULL, 154296936Smmel 306000000ULL, 155296936Smmel 408000000ULL, 156296936Smmel 510000000ULL, 157296936Smmel 612000000ULL, 158296936Smmel 714000000ULL, 159296936Smmel 816000000ULL, 160296936Smmel 918000000ULL, 161296936Smmel 1020000000ULL, 162296936Smmel 1122000000ULL, 163296936Smmel 1224000000ULL, 164296936Smmel 1326000000ULL, 165296936Smmel 1428000000ULL, 166296936Smmel 1530000000ULL, 167296936Smmel 1632000000ULL, 168296936Smmel 1734000000ULL, 169296936Smmel 1836000000ULL, 170296936Smmel 1938000000ULL, 171296936Smmel 2014000000ULL, 172296936Smmel 2116000000ULL, 173296936Smmel 2218000000ULL, 174296936Smmel 2320000000ULL, 175296936Smmel 2320000000ULL, 176296936Smmel 2422000000ULL, 177296936Smmel 2524000000ULL, 178296936Smmel}; 179296936Smmel 180296936Smmelstatic uint64_t cpu_max_freq[] = { 181296936Smmel 2014500000ULL, 182296936Smmel 2320500000ULL, 183296936Smmel 2116500000ULL, 184296936Smmel 2524500000ULL, 185296936Smmel}; 186296936Smmel 187296936Smmelstruct tegra124_cpufreq_softc { 188296936Smmel device_t dev; 189296936Smmel phandle_t node; 190296936Smmel 191296936Smmel regulator_t supply_vdd_cpu; 192296936Smmel clk_t clk_cpu_g; 193296936Smmel clk_t clk_cpu_lp; 194296936Smmel clk_t clk_pll_x; 195296936Smmel clk_t clk_pll_p; 196296936Smmel clk_t clk_dfll; 197296936Smmel 198296936Smmel int process_id; 199296936Smmel int speedo_id; 200296936Smmel int speedo_value; 201296936Smmel 202296936Smmel uint64_t cpu_max_freq; 203296936Smmel struct cpu_volt_def *cpu_def; 204296936Smmel struct cpu_speed_point *speed_points; 205296936Smmel int nspeed_points; 206296936Smmel 207296936Smmel struct cpu_speed_point *act_speed_point; 208296936Smmel 209296936Smmel int latency; 210296936Smmel}; 211296936Smmel 212296936Smmelstatic int cpufreq_lowest_freq = 1; 213296936SmmelTUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq); 214296936Smmel 215296936Smmel#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div)) 216296936Smmel 217301288Spfg#define ROUND_UP(val, div) roundup(val, div) 218301288Spfg#define ROUND_DOWN(val, div) rounddown(val, div) 219296936Smmel 220296936Smmel/* 221296936Smmel * Compute requesetd voltage for given frequency and SoC process variations, 222296936Smmel * - compute base voltage from speedo value using speedo table 223296936Smmel * - round up voltage to next regulator step 224296936Smmel * - clamp it to regulator limits 225296936Smmel */ 226296936Smmelstatic int 227296936Smmelfreq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq) 228296936Smmel{ 229296936Smmel int uv, scale, min_uvolt, max_uvolt, step_uvolt; 230296936Smmel struct speedo_entry *ent; 231296936Smmel int i; 232296936Smmel 233296936Smmel /* Get speedo entry with higher frequency */ 234296936Smmel ent = NULL; 235296936Smmel for (i = 0; i < sc->cpu_def->speedo_nitems; i++) { 236296936Smmel if (sc->cpu_def->speedo_tbl[i].freq >= freq) { 237296936Smmel ent = &sc->cpu_def->speedo_tbl[i]; 238296936Smmel break; 239296936Smmel } 240296936Smmel } 241296936Smmel if (ent == NULL) 242296936Smmel ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1]; 243296936Smmel scale = sc->cpu_def->speedo_scale; 244296936Smmel 245296936Smmel 246296936Smmel /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */ 247296936Smmel uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale); 248296936Smmel uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) + 249296936Smmel ent->c0; 250296936Smmel step_uvolt = sc->cpu_def->step_uvolt; 251296936Smmel /* Round up it to next regulator step */ 252296936Smmel uv = ROUND_UP(uv, step_uvolt); 253296936Smmel 254296936Smmel /* Clamp result */ 255296936Smmel min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt); 256296936Smmel max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt); 257296936Smmel if (uv < min_uvolt) 258296936Smmel uv = min_uvolt; 259296936Smmel if (uv > max_uvolt) 260296936Smmel uv = max_uvolt; 261296936Smmel return (uv); 262296936Smmel 263296936Smmel} 264296936Smmel 265296936Smmelstatic void 266296936Smmelbuild_speed_points(struct tegra124_cpufreq_softc *sc) { 267296936Smmel int i; 268296936Smmel 269296936Smmel sc->nspeed_points = nitems(cpu_freq_tbl); 270296936Smmel sc->speed_points = malloc(sizeof(struct cpu_speed_point) * 271296936Smmel sc->nspeed_points, M_DEVBUF, M_NOWAIT); 272296936Smmel for (i = 0; i < sc->nspeed_points; i++) { 273296936Smmel sc->speed_points[i].freq = cpu_freq_tbl[i]; 274296936Smmel sc->speed_points[i].uvolt = freq_to_voltage(sc, 275296936Smmel cpu_freq_tbl[i]); 276296936Smmel } 277296936Smmel} 278296936Smmel 279296936Smmelstatic struct cpu_speed_point * 280296936Smmelget_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq) 281296936Smmel{ 282296936Smmel int i; 283296936Smmel 284296936Smmel if (sc->speed_points[0].freq >= freq) 285296936Smmel return (sc->speed_points + 0); 286296936Smmel 287296936Smmel for (i = 0; i < sc->nspeed_points - 1; i++) { 288296936Smmel if (sc->speed_points[i + 1].freq > freq) 289296936Smmel return (sc->speed_points + i); 290296936Smmel } 291296936Smmel 292296936Smmel return (sc->speed_points + sc->nspeed_points - 1); 293296936Smmel} 294296936Smmel 295296936Smmelstatic int 296296936Smmeltegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) 297296936Smmel{ 298296936Smmel struct tegra124_cpufreq_softc *sc; 299296936Smmel int i, j, max_cnt; 300296936Smmel 301296936Smmel if (sets == NULL || count == NULL) 302296936Smmel return (EINVAL); 303296936Smmel 304296936Smmel sc = device_get_softc(dev); 305296936Smmel memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); 306296936Smmel 307296936Smmel max_cnt = min(sc->nspeed_points, *count); 308296936Smmel for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) { 309296936Smmel if (sc->cpu_max_freq < sc->speed_points[j].freq) 310296936Smmel continue; 311296936Smmel sets[i].freq = sc->speed_points[j].freq / 1000000; 312296936Smmel sets[i].volts = sc->speed_points[j].uvolt / 1000; 313296936Smmel sets[i].lat = sc->latency; 314296936Smmel sets[i].dev = dev; 315296936Smmel i++; 316296936Smmel } 317296936Smmel *count = i; 318296936Smmel 319296936Smmel return (0); 320296936Smmel} 321296936Smmel 322296936Smmelstatic int 323296936Smmelset_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq) 324296936Smmel{ 325296936Smmel struct cpu_speed_point *point; 326296936Smmel int rv; 327296936Smmel 328296936Smmel point = get_speed_point(sc, freq); 329296936Smmel 330296936Smmel if (sc->act_speed_point->uvolt < point->uvolt) { 331296936Smmel /* set cpu voltage */ 332296936Smmel rv = regulator_set_voltage(sc->supply_vdd_cpu, 333296936Smmel point->uvolt, point->uvolt); 334296936Smmel DELAY(10000); 335296936Smmel if (rv != 0) 336296936Smmel return (rv); 337296936Smmel } 338297576Smmel 339297576Smmel /* Switch supermux to PLLP first */ 340297576Smmel rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p); 341296936Smmel if (rv != 0) { 342297576Smmel device_printf(sc->dev, "Can't set parent to PLLP\n"); 343297576Smmel return (rv); 344297576Smmel } 345297576Smmel 346297576Smmel /* Set PLLX frequency */ 347297576Smmel rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN); 348297576Smmel if (rv != 0) { 349296936Smmel device_printf(sc->dev, "Can't set CPU clock frequency\n"); 350296936Smmel return (rv); 351296936Smmel } 352296936Smmel 353297576Smmel rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x); 354297576Smmel if (rv != 0) { 355297576Smmel device_printf(sc->dev, "Can't set parent to PLLX\n"); 356297576Smmel return (rv); 357297576Smmel } 358297576Smmel 359296936Smmel if (sc->act_speed_point->uvolt > point->uvolt) { 360296936Smmel /* set cpu voltage */ 361296936Smmel rv = regulator_set_voltage(sc->supply_vdd_cpu, 362296936Smmel point->uvolt, point->uvolt); 363296936Smmel if (rv != 0) 364296936Smmel return (rv); 365296936Smmel } 366296936Smmel 367296936Smmel sc->act_speed_point = point; 368296936Smmel 369296936Smmel return (0); 370296936Smmel} 371296936Smmel 372296936Smmelstatic int 373296936Smmeltegra124_cpufreq_set(device_t dev, const struct cf_setting *cf) 374296936Smmel{ 375296936Smmel struct tegra124_cpufreq_softc *sc; 376296936Smmel uint64_t freq; 377296936Smmel int rv; 378296936Smmel 379296936Smmel if (cf == NULL || cf->freq < 0) 380296936Smmel return (EINVAL); 381296936Smmel 382296936Smmel sc = device_get_softc(dev); 383296936Smmel 384296936Smmel freq = cf->freq; 385296936Smmel if (freq < cpufreq_lowest_freq) 386296936Smmel freq = cpufreq_lowest_freq; 387296936Smmel freq *= 1000000; 388296936Smmel if (freq >= sc->cpu_max_freq) 389296936Smmel freq = sc->cpu_max_freq; 390296936Smmel rv = set_cpu_freq(sc, freq); 391296936Smmel 392296936Smmel return (rv); 393296936Smmel} 394296936Smmel 395296936Smmelstatic int 396296936Smmeltegra124_cpufreq_get(device_t dev, struct cf_setting *cf) 397296936Smmel{ 398296936Smmel struct tegra124_cpufreq_softc *sc; 399296936Smmel 400296936Smmel if (cf == NULL) 401296936Smmel return (EINVAL); 402296936Smmel 403296936Smmel sc = device_get_softc(dev); 404296936Smmel memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 405296936Smmel cf->dev = NULL; 406296936Smmel cf->freq = sc->act_speed_point->freq / 1000000; 407296936Smmel cf->volts = sc->act_speed_point->uvolt / 1000; 408296936Smmel /* Transition latency in us. */ 409296936Smmel cf->lat = sc->latency; 410296936Smmel /* Driver providing this setting. */ 411296936Smmel cf->dev = dev; 412296936Smmel 413296936Smmel return (0); 414296936Smmel} 415296936Smmel 416296936Smmel 417296936Smmelstatic int 418296936Smmeltegra124_cpufreq_type(device_t dev, int *type) 419296936Smmel{ 420296936Smmel 421296936Smmel if (type == NULL) 422296936Smmel return (EINVAL); 423296936Smmel *type = CPUFREQ_TYPE_ABSOLUTE; 424296936Smmel 425296936Smmel return (0); 426296936Smmel} 427296936Smmel 428296936Smmelstatic int 429296936Smmelget_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node) 430296936Smmel{ 431296936Smmel int rv; 432296936Smmel device_t parent_dev; 433296936Smmel 434296936Smmel parent_dev = device_get_parent(sc->dev); 435296936Smmel rv = regulator_get_by_ofw_property(parent_dev, "vdd-cpu-supply", 436296936Smmel &sc->supply_vdd_cpu); 437296936Smmel if (rv != 0) { 438296936Smmel device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n"); 439296936Smmel return (rv); 440296936Smmel } 441296936Smmel 442296936Smmel rv = clk_get_by_ofw_name(parent_dev, "cpu_g", &sc->clk_cpu_g); 443296936Smmel if (rv != 0) { 444296936Smmel device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv); 445296936Smmel return (ENXIO); 446296936Smmel } 447296936Smmel 448296936Smmel rv = clk_get_by_ofw_name(parent_dev, "cpu_lp", &sc->clk_cpu_lp); 449296936Smmel if (rv != 0) { 450296936Smmel device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n"); 451296936Smmel return (ENXIO); 452296936Smmel } 453296936Smmel 454296936Smmel rv = clk_get_by_ofw_name(parent_dev, "pll_x", &sc->clk_pll_x); 455296936Smmel if (rv != 0) { 456296936Smmel device_printf(sc->dev, "Cannot get 'pll_x' clock\n"); 457296936Smmel return (ENXIO); 458296936Smmel } 459296936Smmel rv = clk_get_by_ofw_name(parent_dev, "pll_p", &sc->clk_pll_p); 460296936Smmel if (rv != 0) { 461296936Smmel device_printf(parent_dev, "Cannot get 'pll_p' clock\n"); 462296936Smmel return (ENXIO); 463296936Smmel } 464296936Smmel rv = clk_get_by_ofw_name(parent_dev, "dfll", &sc->clk_dfll); 465296936Smmel if (rv != 0) { 466296936Smmel /* XXX DPLL is not implemented yet */ 467296936Smmel/* 468296936Smmel device_printf(sc->dev, "Cannot get 'dfll' clock\n"); 469296936Smmel return (ENXIO); 470296936Smmel*/ 471296936Smmel } 472296936Smmel return (0); 473296936Smmel} 474296936Smmel 475296936Smmelstatic void 476296936Smmeltegra124_cpufreq_identify(driver_t *driver, device_t parent) 477296936Smmel{ 478296936Smmel 479296936Smmel if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL) 480296936Smmel return; 481296936Smmel if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL) 482296936Smmel device_printf(parent, "add child failed\n"); 483296936Smmel} 484296936Smmel 485296936Smmelstatic int 486296936Smmeltegra124_cpufreq_probe(device_t dev) 487296936Smmel{ 488296936Smmel 489296936Smmel if (device_get_unit(dev) != 0) 490296936Smmel return (ENXIO); 491296936Smmel device_set_desc(dev, "CPU Frequency Control"); 492296936Smmel 493296936Smmel return (0); 494296936Smmel} 495296936Smmel 496296936Smmelstatic int 497296936Smmeltegra124_cpufreq_attach(device_t dev) 498296936Smmel{ 499296936Smmel struct tegra124_cpufreq_softc *sc; 500296936Smmel uint64_t freq; 501296936Smmel int rv; 502296936Smmel 503296936Smmel sc = device_get_softc(dev); 504296936Smmel sc->dev = dev; 505296936Smmel sc->node = ofw_bus_get_node(device_get_parent(dev)); 506296936Smmel 507296936Smmel sc->process_id = tegra_sku_info.cpu_process_id; 508296936Smmel sc->speedo_id = tegra_sku_info.cpu_speedo_id; 509296936Smmel sc->speedo_value = tegra_sku_info.cpu_speedo_value; 510296936Smmel 511296936Smmel /* Tegra 124 */ 512296936Smmel /* XXX DPLL is not implemented yet */ 513296936Smmel if (1) 514296936Smmel sc->cpu_def = &tegra124_cpu_volt_pllx_def; 515296936Smmel else 516296936Smmel sc->cpu_def = &tegra124_cpu_volt_dpll_def; 517296936Smmel 518296936Smmel 519296936Smmel rv = get_fdt_resources(sc, sc->node); 520296936Smmel if (rv != 0) { 521296936Smmel return (rv); 522296936Smmel } 523296936Smmel 524296936Smmel build_speed_points(sc); 525296936Smmel 526296936Smmel rv = clk_get_freq(sc->clk_cpu_g, &freq); 527296936Smmel if (rv != 0) { 528296936Smmel device_printf(dev, "Can't get CPU clock frequency\n"); 529296936Smmel return (rv); 530296936Smmel } 531296936Smmel if (sc->speedo_id < nitems(cpu_max_freq)) 532296936Smmel sc->cpu_max_freq = cpu_max_freq[sc->speedo_id]; 533296936Smmel else 534296936Smmel sc->cpu_max_freq = cpu_max_freq[0]; 535296936Smmel sc->act_speed_point = get_speed_point(sc, freq); 536296936Smmel 537296936Smmel /* Set safe startup CPU frequency. */ 538296936Smmel rv = set_cpu_freq(sc, 1632000000); 539296936Smmel if (rv != 0) { 540296936Smmel device_printf(dev, "Can't set initial CPU clock frequency\n"); 541296936Smmel return (rv); 542296936Smmel } 543296936Smmel 544296936Smmel /* This device is controlled by cpufreq(4). */ 545296936Smmel cpufreq_register(dev); 546296936Smmel 547296936Smmel return (0); 548296936Smmel} 549296936Smmel 550296936Smmelstatic int 551296936Smmeltegra124_cpufreq_detach(device_t dev) 552296936Smmel{ 553296936Smmel struct tegra124_cpufreq_softc *sc; 554296936Smmel 555296936Smmel sc = device_get_softc(dev); 556296936Smmel cpufreq_unregister(dev); 557296936Smmel 558296936Smmel if (sc->supply_vdd_cpu != NULL) 559296936Smmel regulator_release(sc->supply_vdd_cpu); 560296936Smmel 561296936Smmel if (sc->clk_cpu_g != NULL) 562296936Smmel clk_release(sc->clk_cpu_g); 563296936Smmel if (sc->clk_cpu_lp != NULL) 564296936Smmel clk_release(sc->clk_cpu_lp); 565296936Smmel if (sc->clk_pll_x != NULL) 566296936Smmel clk_release(sc->clk_pll_x); 567296936Smmel if (sc->clk_pll_p != NULL) 568296936Smmel clk_release(sc->clk_pll_p); 569296936Smmel if (sc->clk_dfll != NULL) 570296936Smmel clk_release(sc->clk_dfll); 571296936Smmel return (0); 572296936Smmel} 573296936Smmel 574296936Smmelstatic device_method_t tegra124_cpufreq_methods[] = { 575296936Smmel /* Device interface */ 576296936Smmel DEVMETHOD(device_identify, tegra124_cpufreq_identify), 577296936Smmel DEVMETHOD(device_probe, tegra124_cpufreq_probe), 578296936Smmel DEVMETHOD(device_attach, tegra124_cpufreq_attach), 579296936Smmel DEVMETHOD(device_detach, tegra124_cpufreq_detach), 580296936Smmel 581296936Smmel /* cpufreq interface */ 582296936Smmel DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set), 583296936Smmel DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get), 584296936Smmel DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings), 585296936Smmel DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type), 586296936Smmel 587296936Smmel DEVMETHOD_END 588296936Smmel}; 589296936Smmel 590296936Smmelstatic devclass_t tegra124_cpufreq_devclass; 591296936Smmelstatic driver_t tegra124_cpufreq_driver = { 592296936Smmel "tegra124_cpufreq", 593296936Smmel tegra124_cpufreq_methods, 594296936Smmel sizeof(struct tegra124_cpufreq_softc), 595296936Smmel}; 596296936Smmel 597296936SmmelDRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, 598296936Smmel tegra124_cpufreq_devclass, 0, 0); 599