Path: blob/main/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
39507 views
/*-1* Copyright (c) 2016 Michal Meloun <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/bus.h>29#include <sys/cpu.h>30#include <sys/kernel.h>31#include <sys/lock.h>32#include <sys/malloc.h>33#include <sys/module.h>3435#include <machine/bus.h>36#include <machine/cpu.h>3738#include <dev/clk/clk.h>39#include <dev/regulator/regulator.h>40#include <dev/ofw/ofw_bus_subr.h>4142#include <arm/nvidia/tegra_efuse.h>4344#include "cpufreq_if.h"4546#define XXX4748/* CPU voltage table entry */49struct speedo_entry {50uint64_t freq; /* Frequency point */51int c0; /* Coeeficient values for */52int c1; /* quadratic equation: */53int c2; /* c2 * speedo^2 + c1 * speedo + c0 */54};5556struct cpu_volt_def {57int min_uvolt; /* Min allowed CPU voltage */58int max_uvolt; /* Max allowed CPU voltage */59int step_uvolt; /* Step of CPU voltage */60int speedo_scale; /* Scaling factor for cvt */61int speedo_nitems; /* Size of speedo table */62struct speedo_entry *speedo_tbl; /* CPU voltage table */63};6465struct cpu_speed_point {66uint64_t freq; /* Frequecy */67int uvolt; /* Requested voltage */68};6970static struct speedo_entry tegra124_speedo_dpll_tbl[] =71{72{ 204000000ULL, 1112619, -29295, 402},73{ 306000000ULL, 1150460, -30585, 402},74{ 408000000ULL, 1190122, -31865, 402},75{ 510000000ULL, 1231606, -33155, 402},76{ 612000000ULL, 1274912, -34435, 402},77{ 714000000ULL, 1320040, -35725, 402},78{ 816000000ULL, 1366990, -37005, 402},79{ 918000000ULL, 1415762, -38295, 402},80{1020000000ULL, 1466355, -39575, 402},81{1122000000ULL, 1518771, -40865, 402},82{1224000000ULL, 1573009, -42145, 402},83{1326000000ULL, 1629068, -43435, 402},84{1428000000ULL, 1686950, -44715, 402},85{1530000000ULL, 1746653, -46005, 402},86{1632000000ULL, 1808179, -47285, 402},87{1734000000ULL, 1871526, -48575, 402},88{1836000000ULL, 1936696, -49855, 402},89{1938000000ULL, 2003687, -51145, 402},90{2014500000ULL, 2054787, -52095, 402},91{2116500000ULL, 2124957, -53385, 402},92{2218500000ULL, 2196950, -54665, 402},93{2320500000ULL, 2270765, -55955, 402},94{2320500000ULL, 2270765, -55955, 402},95{2422500000ULL, 2346401, -57235, 402},96{2524500000ULL, 2437299, -58535, 402},97};9899static struct cpu_volt_def tegra124_cpu_volt_dpll_def =100{101.min_uvolt = 900000, /* 0.9 V */102.max_uvolt = 1260000, /* 1.26 */103.step_uvolt = 10000, /* 10 mV */104.speedo_scale = 100,105.speedo_nitems = nitems(tegra124_speedo_dpll_tbl),106.speedo_tbl = tegra124_speedo_dpll_tbl,107};108109static struct speedo_entry tegra124_speedo_pllx_tbl[] =110{111{ 204000000ULL, 800000, 0, 0},112{ 306000000ULL, 800000, 0, 0},113{ 408000000ULL, 800000, 0, 0},114{ 510000000ULL, 800000, 0, 0},115{ 612000000ULL, 800000, 0, 0},116{ 714000000ULL, 800000, 0, 0},117{ 816000000ULL, 820000, 0, 0},118{ 918000000ULL, 840000, 0, 0},119{1020000000ULL, 880000, 0, 0},120{1122000000ULL, 900000, 0, 0},121{1224000000ULL, 930000, 0, 0},122{1326000000ULL, 960000, 0, 0},123{1428000000ULL, 990000, 0, 0},124{1530000000ULL, 1020000, 0, 0},125{1632000000ULL, 1070000, 0, 0},126{1734000000ULL, 1100000, 0, 0},127{1836000000ULL, 1140000, 0, 0},128{1938000000ULL, 1180000, 0, 0},129{2014500000ULL, 1220000, 0, 0},130{2116500000ULL, 1260000, 0, 0},131{2218500000ULL, 1310000, 0, 0},132{2320500000ULL, 1360000, 0, 0},133{2397000000ULL, 1400000, 0, 0},134{2499000000ULL, 1400000, 0, 0},135};136137static struct cpu_volt_def tegra124_cpu_volt_pllx_def =138{139.min_uvolt = 1000000, /* XXX 0.9 V doesn't work on all boards */140.max_uvolt = 1260000, /* 1.26 */141.step_uvolt = 10000, /* 10 mV */142.speedo_scale = 100,143.speedo_nitems = nitems(tegra124_speedo_pllx_tbl),144.speedo_tbl = tegra124_speedo_pllx_tbl,145};146147static uint64_t cpu_freq_tbl[] = {148204000000ULL,149306000000ULL,150408000000ULL,151510000000ULL,152612000000ULL,153714000000ULL,154816000000ULL,155918000000ULL,1561020000000ULL,1571122000000ULL,1581224000000ULL,1591326000000ULL,1601428000000ULL,1611530000000ULL,1621632000000ULL,1631734000000ULL,1641836000000ULL,1651938000000ULL,1662014000000ULL,1672116000000ULL,1682218000000ULL,1692320000000ULL,1702422000000ULL,1712524000000ULL,172};173174static uint64_t cpu_max_freq[] = {1752014500000ULL,1762320500000ULL,1772116500000ULL,1782524500000ULL,179};180181struct tegra124_cpufreq_softc {182device_t dev;183phandle_t node;184185regulator_t supply_vdd_cpu;186clk_t clk_cpu_g;187clk_t clk_cpu_lp;188clk_t clk_pll_x;189clk_t clk_pll_p;190clk_t clk_dfll;191192int process_id;193int speedo_id;194int speedo_value;195196uint64_t cpu_max_freq;197struct cpu_volt_def *cpu_def;198struct cpu_speed_point *speed_points;199int nspeed_points;200201struct cpu_speed_point *act_speed_point;202203int latency;204};205206static int cpufreq_lowest_freq = 1;207TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);208209#define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div))210211#define ROUND_UP(val, div) roundup(val, div)212#define ROUND_DOWN(val, div) rounddown(val, div)213214/*215* Compute requesetd voltage for given frequency and SoC process variations,216* - compute base voltage from speedo value using speedo table217* - round up voltage to next regulator step218* - clamp it to regulator limits219*/220static int221freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)222{223int uv, scale, min_uvolt, max_uvolt, step_uvolt;224struct speedo_entry *ent;225int i;226227/* Get speedo entry with higher frequency */228ent = NULL;229for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {230if (sc->cpu_def->speedo_tbl[i].freq >= freq) {231ent = &sc->cpu_def->speedo_tbl[i];232break;233}234}235if (ent == NULL)236ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];237scale = sc->cpu_def->speedo_scale;238239/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */240uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);241uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +242ent->c0;243step_uvolt = sc->cpu_def->step_uvolt;244/* Round up it to next regulator step */245uv = ROUND_UP(uv, step_uvolt);246247/* Clamp result */248min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);249max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);250if (uv < min_uvolt)251uv = min_uvolt;252if (uv > max_uvolt)253uv = max_uvolt;254return (uv);255256}257258static void259build_speed_points(struct tegra124_cpufreq_softc *sc) {260int i;261262sc->nspeed_points = nitems(cpu_freq_tbl);263sc->speed_points = malloc(sizeof(struct cpu_speed_point) *264sc->nspeed_points, M_DEVBUF, M_NOWAIT);265for (i = 0; i < sc->nspeed_points; i++) {266sc->speed_points[i].freq = cpu_freq_tbl[i];267sc->speed_points[i].uvolt = freq_to_voltage(sc,268cpu_freq_tbl[i]);269}270}271272static struct cpu_speed_point *273get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)274{275int i;276277if (sc->speed_points[0].freq >= freq)278return (sc->speed_points + 0);279280for (i = 0; i < sc->nspeed_points - 1; i++) {281if (sc->speed_points[i + 1].freq > freq)282return (sc->speed_points + i);283}284285return (sc->speed_points + sc->nspeed_points - 1);286}287288static int289tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)290{291struct tegra124_cpufreq_softc *sc;292int i, j;293294if (sets == NULL || count == NULL)295return (EINVAL);296297sc = device_get_softc(dev);298memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));299300for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {301if (sc->cpu_max_freq < sc->speed_points[j].freq)302continue;303sets[i].freq = sc->speed_points[j].freq / 1000000;304sets[i].volts = sc->speed_points[j].uvolt / 1000;305sets[i].lat = sc->latency;306sets[i].dev = dev;307i++;308}309*count = i;310311return (0);312}313314static int315set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)316{317struct cpu_speed_point *point;318int rv;319320point = get_speed_point(sc, freq);321322if (sc->act_speed_point->uvolt < point->uvolt) {323/* set cpu voltage */324rv = regulator_set_voltage(sc->supply_vdd_cpu,325point->uvolt, point->uvolt);326DELAY(10000);327if (rv != 0)328return (rv);329}330331/* Switch supermux to PLLP first */332rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);333if (rv != 0) {334device_printf(sc->dev, "Can't set parent to PLLP\n");335return (rv);336}337338/* Set PLLX frequency */339rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);340if (rv != 0) {341device_printf(sc->dev, "Can't set CPU clock frequency\n");342return (rv);343}344345rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);346if (rv != 0) {347device_printf(sc->dev, "Can't set parent to PLLX\n");348return (rv);349}350351if (sc->act_speed_point->uvolt > point->uvolt) {352/* set cpu voltage */353rv = regulator_set_voltage(sc->supply_vdd_cpu,354point->uvolt, point->uvolt);355if (rv != 0)356return (rv);357}358359sc->act_speed_point = point;360361return (0);362}363364static int365tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)366{367struct tegra124_cpufreq_softc *sc;368uint64_t freq;369int rv;370371if (cf == NULL || cf->freq < 0)372return (EINVAL);373374sc = device_get_softc(dev);375376freq = cf->freq;377if (freq < cpufreq_lowest_freq)378freq = cpufreq_lowest_freq;379freq *= 1000000;380if (freq >= sc->cpu_max_freq)381freq = sc->cpu_max_freq;382rv = set_cpu_freq(sc, freq);383384return (rv);385}386387static int388tegra124_cpufreq_get(device_t dev, struct cf_setting *cf)389{390struct tegra124_cpufreq_softc *sc;391392if (cf == NULL)393return (EINVAL);394395sc = device_get_softc(dev);396memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));397cf->dev = NULL;398cf->freq = sc->act_speed_point->freq / 1000000;399cf->volts = sc->act_speed_point->uvolt / 1000;400/* Transition latency in us. */401cf->lat = sc->latency;402/* Driver providing this setting. */403cf->dev = dev;404405return (0);406}407408static int409tegra124_cpufreq_type(device_t dev, int *type)410{411412if (type == NULL)413return (EINVAL);414*type = CPUFREQ_TYPE_ABSOLUTE;415416return (0);417}418419static int420get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)421{422int rv;423device_t parent_dev;424425parent_dev = device_get_parent(sc->dev);426rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply",427&sc->supply_vdd_cpu);428if (rv != 0) {429device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");430return (rv);431}432433rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);434if (rv != 0) {435device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);436return (ENXIO);437}438439rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp);440if (rv != 0) {441device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");442return (ENXIO);443}444445rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);446if (rv != 0) {447device_printf(sc->dev, "Cannot get 'pll_x' clock\n");448return (ENXIO);449}450rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);451if (rv != 0) {452device_printf(parent_dev, "Cannot get 'pll_p' clock\n");453return (ENXIO);454}455rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);456if (rv != 0) {457/* XXX DPLL is not implemented yet */458/*459device_printf(sc->dev, "Cannot get 'dfll' clock\n");460return (ENXIO);461*/462}463return (0);464}465466static void467tegra124_cpufreq_identify(driver_t *driver, device_t parent)468{469phandle_t root;470471root = OF_finddevice("/");472if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))473return;474475if (device_get_unit(parent) != 0)476return;477if (device_find_child(parent, "tegra124_cpufreq", DEVICE_UNIT_ANY) != NULL)478return;479if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", DEVICE_UNIT_ANY) == NULL)480device_printf(parent, "add child failed\n");481}482483static int484tegra124_cpufreq_probe(device_t dev)485{486487device_set_desc(dev, "CPU Frequency Control");488489return (0);490}491492static int493tegra124_cpufreq_attach(device_t dev)494{495struct tegra124_cpufreq_softc *sc;496uint64_t freq;497int rv;498499sc = device_get_softc(dev);500sc->dev = dev;501sc->node = ofw_bus_get_node(device_get_parent(dev));502503sc->process_id = tegra_sku_info.cpu_process_id;504sc->speedo_id = tegra_sku_info.cpu_speedo_id;505sc->speedo_value = tegra_sku_info.cpu_speedo_value;506507/* Tegra 124 */508/* XXX DPLL is not implemented yet */509if (1)510sc->cpu_def = &tegra124_cpu_volt_pllx_def;511else512sc->cpu_def = &tegra124_cpu_volt_dpll_def;513514rv = get_fdt_resources(sc, sc->node);515if (rv != 0) {516return (rv);517}518519build_speed_points(sc);520521rv = clk_get_freq(sc->clk_cpu_g, &freq);522if (rv != 0) {523device_printf(dev, "Can't get CPU clock frequency\n");524return (rv);525}526if (sc->speedo_id < nitems(cpu_max_freq))527sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];528else529sc->cpu_max_freq = cpu_max_freq[0];530sc->act_speed_point = get_speed_point(sc, freq);531532/* Set safe startup CPU frequency. */533rv = set_cpu_freq(sc, 1632000000);534if (rv != 0) {535device_printf(dev, "Can't set initial CPU clock frequency\n");536return (rv);537}538539/* This device is controlled by cpufreq(4). */540cpufreq_register(dev);541542return (0);543}544545static int546tegra124_cpufreq_detach(device_t dev)547{548struct tegra124_cpufreq_softc *sc;549550sc = device_get_softc(dev);551cpufreq_unregister(dev);552553if (sc->supply_vdd_cpu != NULL)554regulator_release(sc->supply_vdd_cpu);555556if (sc->clk_cpu_g != NULL)557clk_release(sc->clk_cpu_g);558if (sc->clk_cpu_lp != NULL)559clk_release(sc->clk_cpu_lp);560if (sc->clk_pll_x != NULL)561clk_release(sc->clk_pll_x);562if (sc->clk_pll_p != NULL)563clk_release(sc->clk_pll_p);564if (sc->clk_dfll != NULL)565clk_release(sc->clk_dfll);566return (0);567}568569static device_method_t tegra124_cpufreq_methods[] = {570/* Device interface */571DEVMETHOD(device_identify, tegra124_cpufreq_identify),572DEVMETHOD(device_probe, tegra124_cpufreq_probe),573DEVMETHOD(device_attach, tegra124_cpufreq_attach),574DEVMETHOD(device_detach, tegra124_cpufreq_detach),575576/* cpufreq interface */577DEVMETHOD(cpufreq_drv_set, tegra124_cpufreq_set),578DEVMETHOD(cpufreq_drv_get, tegra124_cpufreq_get),579DEVMETHOD(cpufreq_drv_settings, tegra124_cpufreq_settings),580DEVMETHOD(cpufreq_drv_type, tegra124_cpufreq_type),581582DEVMETHOD_END583};584585static DEFINE_CLASS_0(tegra124_cpufreq, tegra124_cpufreq_driver,586tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc));587DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, NULL, NULL);588589590