1e9034789SMichal Meloun /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3e9034789SMichal Meloun * 4e9034789SMichal Meloun * Copyright 2020 Michal Meloun <mmel@FreeBSD.org> 5e9034789SMichal Meloun * 6e9034789SMichal Meloun * Redistribution and use in source and binary forms, with or without 7e9034789SMichal Meloun * modification, are permitted provided that the following conditions 8e9034789SMichal Meloun * are met: 9e9034789SMichal Meloun * 1. Redistributions of source code must retain the above copyright 10e9034789SMichal Meloun * notice, this list of conditions and the following disclaimer. 11e9034789SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 12e9034789SMichal Meloun * notice, this list of conditions and the following disclaimer in the 13e9034789SMichal Meloun * documentation and/or other materials provided with the distribution. 14e9034789SMichal Meloun * 15e9034789SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16e9034789SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17e9034789SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18e9034789SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19e9034789SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20e9034789SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21e9034789SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22e9034789SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23e9034789SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24e9034789SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25e9034789SMichal Meloun * SUCH DAMAGE. 26e9034789SMichal Meloun */ 27e9034789SMichal Meloun 28e9034789SMichal Meloun #include <sys/param.h> 29e9034789SMichal Meloun #include <sys/systm.h> 30e9034789SMichal Meloun #include <sys/bus.h> 31e9034789SMichal Meloun #include <sys/cpu.h> 32e9034789SMichal Meloun #include <sys/kernel.h> 33e9034789SMichal Meloun #include <sys/lock.h> 34e9034789SMichal Meloun #include <sys/malloc.h> 35e9034789SMichal Meloun #include <sys/module.h> 36e9034789SMichal Meloun 37e9034789SMichal Meloun #include <machine/bus.h> 38e9034789SMichal Meloun #include <machine/cpu.h> 39e9034789SMichal Meloun 40be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 41*b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h> 42e9034789SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 43e9034789SMichal Meloun 44e9034789SMichal Meloun #include <arm/nvidia/tegra_efuse.h> 45e9034789SMichal Meloun 46e9034789SMichal Meloun #include "cpufreq_if.h" 47e9034789SMichal Meloun 48e9034789SMichal Meloun /* CPU voltage table entry */ 49e9034789SMichal Meloun struct speedo_entry { 50e9034789SMichal Meloun uint64_t freq; /* Frequency point */ 51e9034789SMichal Meloun int c0; /* Coeeficient values for */ 52e9034789SMichal Meloun int c1; /* quadratic equation: */ 53e9034789SMichal Meloun int c2; /* c2 * speedo^2 + c1 * speedo + c0 */ 54e9034789SMichal Meloun }; 55e9034789SMichal Meloun 56e9034789SMichal Meloun struct cpu_volt_def { 57e9034789SMichal Meloun int min_uvolt; /* Min allowed CPU voltage */ 58e9034789SMichal Meloun int max_uvolt; /* Max allowed CPU voltage */ 59e9034789SMichal Meloun int step_uvolt; /* Step of CPU voltage */ 60e9034789SMichal Meloun int speedo_scale; /* Scaling factor for cvt */ 61e9034789SMichal Meloun int speedo_nitems; /* Size of speedo table */ 62e9034789SMichal Meloun struct speedo_entry *speedo_tbl; /* CPU voltage table */ 63e9034789SMichal Meloun }; 64e9034789SMichal Meloun 65e9034789SMichal Meloun struct cpu_speed_point { 66e9034789SMichal Meloun uint64_t freq; /* Frequecy */ 67e9034789SMichal Meloun int uvolt; /* Requested voltage */ 68e9034789SMichal Meloun }; 69e9034789SMichal Meloun 70e9034789SMichal Meloun static struct speedo_entry tegra210_speedo_tbl[] = 71e9034789SMichal Meloun { 72e9034789SMichal Meloun {204000000UL, 1007452, -23865, 370}, 73e9034789SMichal Meloun {306000000UL, 1052709, -24875, 370}, 74e9034789SMichal Meloun {408000000UL, 1099069, -25895, 370}, 75e9034789SMichal Meloun {510000000UL, 1146534, -26905, 370}, 76e9034789SMichal Meloun {612000000UL, 1195102, -27915, 370}, 77e9034789SMichal Meloun {714000000UL, 1244773, -28925, 370}, 78e9034789SMichal Meloun {816000000UL, 1295549, -29935, 370}, 79e9034789SMichal Meloun {918000000UL, 1347428, -30955, 370}, 80e9034789SMichal Meloun {1020000000UL, 1400411, -31965, 370}, 81e9034789SMichal Meloun {1122000000UL, 1454497, -32975, 370}, 82e9034789SMichal Meloun {1224000000UL, 1509687, -33985, 370}, 83e9034789SMichal Meloun {1326000000UL, 1565981, -35005, 370}, 84e9034789SMichal Meloun {1428000000UL, 1623379, -36015, 370}, 85e9034789SMichal Meloun {1530000000UL, 1681880, -37025, 370}, 86e9034789SMichal Meloun {1632000000UL, 1741485, -38035, 370}, 87e9034789SMichal Meloun {1734000000UL, 1802194, -39055, 370}, 88e9034789SMichal Meloun {1836000000UL, 1864006, -40065, 370}, 89e9034789SMichal Meloun {1912500000UL, 1910780, -40815, 370}, 90e9034789SMichal Meloun {2014500000UL, 1227000, 0, 0}, 91e9034789SMichal Meloun {2218500000UL, 1227000, 0, 0}, 92e9034789SMichal Meloun }; 93e9034789SMichal Meloun 94e9034789SMichal Meloun static struct cpu_volt_def tegra210_cpu_volt_def = 95e9034789SMichal Meloun { 96e9034789SMichal Meloun .min_uvolt = 900000, /* 0.9 V */ 97e9034789SMichal Meloun .max_uvolt = 1227000, /* 1.227 */ 98e9034789SMichal Meloun .step_uvolt = 10000, /* 10 mV */ 99e9034789SMichal Meloun .speedo_scale = 100, 100e9034789SMichal Meloun .speedo_nitems = nitems(tegra210_speedo_tbl), 101e9034789SMichal Meloun .speedo_tbl = tegra210_speedo_tbl, 102e9034789SMichal Meloun }; 103e9034789SMichal Meloun 104e9034789SMichal Meloun static uint64_t cpu_max_freq[] = { 105e9034789SMichal Meloun 1912500000UL, 106e9034789SMichal Meloun 1912500000UL, 107e9034789SMichal Meloun 2218500000UL, 108e9034789SMichal Meloun 1785000000UL, 109e9034789SMichal Meloun 1632000000UL, 110e9034789SMichal Meloun 1912500000UL, 111e9034789SMichal Meloun 2014500000UL, 112e9034789SMichal Meloun 1734000000UL, 113e9034789SMichal Meloun 1683000000UL, 114e9034789SMichal Meloun 1555500000UL, 115e9034789SMichal Meloun 1504500000UL, 116e9034789SMichal Meloun }; 117e9034789SMichal Meloun 118e9034789SMichal Meloun static uint64_t cpu_freq_tbl[] = { 119e9034789SMichal Meloun 204000000UL, 120e9034789SMichal Meloun 306000000UL, 121e9034789SMichal Meloun 408000000UL, 122e9034789SMichal Meloun 510000000UL, 123e9034789SMichal Meloun 612000000UL, 124e9034789SMichal Meloun 714000000UL, 125e9034789SMichal Meloun 816000000UL, 126e9034789SMichal Meloun 918000000UL, 127e9034789SMichal Meloun 1020000000UL, 128e9034789SMichal Meloun 1122000000UL, 129e9034789SMichal Meloun 1224000000UL, 130e9034789SMichal Meloun 1326000000UL, 131e9034789SMichal Meloun 1428000000UL, 132e9034789SMichal Meloun 1530000000UL, 133e9034789SMichal Meloun 1632000000UL, 134e9034789SMichal Meloun 1734000000UL, 135e9034789SMichal Meloun 1836000000UL, 136e9034789SMichal Meloun 1912500000UL, 137e9034789SMichal Meloun 2014500000UL, 138e9034789SMichal Meloun 2218500000UL, 139e9034789SMichal Meloun }; 140e9034789SMichal Meloun 141e9034789SMichal Meloun struct tegra210_cpufreq_softc { 142e9034789SMichal Meloun device_t dev; 143e9034789SMichal Meloun phandle_t node; 144e9034789SMichal Meloun 145e9034789SMichal Meloun clk_t clk_cpu_g; 146e9034789SMichal Meloun clk_t clk_pll_x; 147e9034789SMichal Meloun clk_t clk_pll_p; 148e9034789SMichal Meloun clk_t clk_dfll; 149e9034789SMichal Meloun 150e9034789SMichal Meloun int process_id; 151e9034789SMichal Meloun int speedo_id; 152e9034789SMichal Meloun int speedo_value; 153e9034789SMichal Meloun 154e9034789SMichal Meloun uint64_t cpu_max_freq; 155e9034789SMichal Meloun struct cpu_volt_def *cpu_def; 156e9034789SMichal Meloun struct cpu_speed_point *speed_points; 157e9034789SMichal Meloun int nspeed_points; 158e9034789SMichal Meloun 159e9034789SMichal Meloun struct cpu_speed_point *act_speed_point; 160e9034789SMichal Meloun 161e9034789SMichal Meloun int latency; 162e9034789SMichal Meloun }; 163e9034789SMichal Meloun 164e9034789SMichal Meloun static int cpufreq_lowest_freq = 1; 165e9034789SMichal Meloun TUNABLE_INT("hw.tegra210.cpufreq.lowest_freq", &cpufreq_lowest_freq); 166e9034789SMichal Meloun 167e9034789SMichal Meloun #define DIV_ROUND_CLOSEST(val, div) (((val) + ((div) / 2)) / (div)) 168e9034789SMichal Meloun 169e9034789SMichal Meloun #define ROUND_UP(val, div) roundup(val, div) 170e9034789SMichal Meloun #define ROUND_DOWN(val, div) rounddown(val, div) 171e9034789SMichal Meloun 172e9034789SMichal Meloun /* 173e9034789SMichal Meloun * Compute requesetd voltage for given frequency and SoC process variations, 174e9034789SMichal Meloun * - compute base voltage from speedo value using speedo table 175e9034789SMichal Meloun * - round up voltage to next regulator step 176e9034789SMichal Meloun * - clamp it to regulator limits 177e9034789SMichal Meloun */ 178e9034789SMichal Meloun static int 179e9034789SMichal Meloun freq_to_voltage(struct tegra210_cpufreq_softc *sc, uint64_t freq) 180e9034789SMichal Meloun { 181e9034789SMichal Meloun int uv, scale, min_uvolt, max_uvolt, step_uvolt; 182e9034789SMichal Meloun struct speedo_entry *ent; 183e9034789SMichal Meloun int i; 184e9034789SMichal Meloun 185e9034789SMichal Meloun /* Get speedo entry with higher frequency */ 186e9034789SMichal Meloun ent = NULL; 187e9034789SMichal Meloun for (i = 0; i < sc->cpu_def->speedo_nitems; i++) { 188e9034789SMichal Meloun if (sc->cpu_def->speedo_tbl[i].freq >= freq) { 189e9034789SMichal Meloun ent = &sc->cpu_def->speedo_tbl[i]; 190e9034789SMichal Meloun break; 191e9034789SMichal Meloun } 192e9034789SMichal Meloun } 193e9034789SMichal Meloun if (ent == NULL) 194e9034789SMichal Meloun ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1]; 195e9034789SMichal Meloun scale = sc->cpu_def->speedo_scale; 196e9034789SMichal Meloun 197e9034789SMichal Meloun 198e9034789SMichal Meloun /* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */ 199e9034789SMichal Meloun uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale); 200e9034789SMichal Meloun uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) + 201e9034789SMichal Meloun ent->c0; 202e9034789SMichal Meloun step_uvolt = sc->cpu_def->step_uvolt; 203e9034789SMichal Meloun /* Round up it to next regulator step */ 204e9034789SMichal Meloun uv = ROUND_UP(uv, step_uvolt); 205e9034789SMichal Meloun 206e9034789SMichal Meloun /* Clamp result */ 207e9034789SMichal Meloun min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt); 208e9034789SMichal Meloun max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt); 209e9034789SMichal Meloun if (uv < min_uvolt) 210e9034789SMichal Meloun uv = min_uvolt; 211e9034789SMichal Meloun if (uv > max_uvolt) 212e9034789SMichal Meloun uv = max_uvolt; 213e9034789SMichal Meloun return (uv); 214e9034789SMichal Meloun 215e9034789SMichal Meloun } 216e9034789SMichal Meloun 217e9034789SMichal Meloun static void 218e9034789SMichal Meloun build_speed_points(struct tegra210_cpufreq_softc *sc) { 219e9034789SMichal Meloun int i; 220e9034789SMichal Meloun 221e9034789SMichal Meloun sc->nspeed_points = nitems(cpu_freq_tbl); 222e9034789SMichal Meloun sc->speed_points = malloc(sizeof(struct cpu_speed_point) * 223e9034789SMichal Meloun sc->nspeed_points, M_DEVBUF, M_NOWAIT); 224e9034789SMichal Meloun for (i = 0; i < sc->nspeed_points; i++) { 225e9034789SMichal Meloun sc->speed_points[i].freq = cpu_freq_tbl[i]; 226e9034789SMichal Meloun sc->speed_points[i].uvolt = freq_to_voltage(sc, 227e9034789SMichal Meloun cpu_freq_tbl[i]); 228e9034789SMichal Meloun } 229e9034789SMichal Meloun } 230e9034789SMichal Meloun 231e9034789SMichal Meloun static struct cpu_speed_point * 232e9034789SMichal Meloun get_speed_point(struct tegra210_cpufreq_softc *sc, uint64_t freq) 233e9034789SMichal Meloun { 234e9034789SMichal Meloun int i; 235e9034789SMichal Meloun 236e9034789SMichal Meloun if (sc->speed_points[0].freq >= freq) 237e9034789SMichal Meloun return (sc->speed_points + 0); 238e9034789SMichal Meloun 239e9034789SMichal Meloun for (i = 0; i < sc->nspeed_points - 1; i++) { 240e9034789SMichal Meloun if (sc->speed_points[i + 1].freq > freq) 241e9034789SMichal Meloun return (sc->speed_points + i); 242e9034789SMichal Meloun } 243e9034789SMichal Meloun 244e9034789SMichal Meloun return (sc->speed_points + sc->nspeed_points - 1); 245e9034789SMichal Meloun } 246e9034789SMichal Meloun 247e9034789SMichal Meloun static int 248e9034789SMichal Meloun tegra210_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count) 249e9034789SMichal Meloun { 250e9034789SMichal Meloun struct tegra210_cpufreq_softc *sc; 2514b9b6a50SJohn Baldwin int i, j; 252e9034789SMichal Meloun 253e9034789SMichal Meloun if (sets == NULL || count == NULL) 254e9034789SMichal Meloun return (EINVAL); 255e9034789SMichal Meloun 256e9034789SMichal Meloun sc = device_get_softc(dev); 257e9034789SMichal Meloun memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count)); 258e9034789SMichal Meloun 259e9034789SMichal Meloun for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) { 260e9034789SMichal Meloun if (sc->cpu_max_freq < sc->speed_points[j].freq) 261e9034789SMichal Meloun continue; 262e9034789SMichal Meloun sets[i].freq = sc->speed_points[j].freq / 1000000; 263e9034789SMichal Meloun sets[i].volts = sc->speed_points[j].uvolt / 1000; 264e9034789SMichal Meloun sets[i].lat = sc->latency; 265e9034789SMichal Meloun sets[i].dev = dev; 266e9034789SMichal Meloun i++; 267e9034789SMichal Meloun } 268e9034789SMichal Meloun *count = i; 269e9034789SMichal Meloun 270e9034789SMichal Meloun return (0); 271e9034789SMichal Meloun } 272e9034789SMichal Meloun 273e9034789SMichal Meloun static int 274e9034789SMichal Meloun set_cpu_freq(struct tegra210_cpufreq_softc *sc, uint64_t freq) 275e9034789SMichal Meloun { 276e9034789SMichal Meloun struct cpu_speed_point *point; 277e9034789SMichal Meloun int rv; 278e9034789SMichal Meloun 279e9034789SMichal Meloun point = get_speed_point(sc, freq); 280e9034789SMichal Meloun 281e9034789SMichal Meloun /* Set PLLX frequency */ 282e9034789SMichal Meloun rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN); 283e9034789SMichal Meloun if (rv != 0) { 284e9034789SMichal Meloun device_printf(sc->dev, "Can't set CPU clock frequency\n"); 285e9034789SMichal Meloun return (rv); 286e9034789SMichal Meloun } 287e9034789SMichal Meloun 288e9034789SMichal Meloun sc->act_speed_point = point; 289e9034789SMichal Meloun 290e9034789SMichal Meloun return (0); 291e9034789SMichal Meloun } 292e9034789SMichal Meloun 293e9034789SMichal Meloun static int 294e9034789SMichal Meloun tegra210_cpufreq_set(device_t dev, const struct cf_setting *cf) 295e9034789SMichal Meloun { 296e9034789SMichal Meloun struct tegra210_cpufreq_softc *sc; 297e9034789SMichal Meloun uint64_t freq; 298e9034789SMichal Meloun int rv; 299e9034789SMichal Meloun 300e9034789SMichal Meloun if (cf == NULL || cf->freq < 0) 301e9034789SMichal Meloun return (EINVAL); 302e9034789SMichal Meloun 303e9034789SMichal Meloun sc = device_get_softc(dev); 304e9034789SMichal Meloun 305e9034789SMichal Meloun freq = cf->freq; 306e9034789SMichal Meloun if (freq < cpufreq_lowest_freq) 307e9034789SMichal Meloun freq = cpufreq_lowest_freq; 308e9034789SMichal Meloun freq *= 1000000; 309e9034789SMichal Meloun if (freq >= sc->cpu_max_freq) 310e9034789SMichal Meloun freq = sc->cpu_max_freq; 311e9034789SMichal Meloun rv = set_cpu_freq(sc, freq); 312e9034789SMichal Meloun 313e9034789SMichal Meloun return (rv); 314e9034789SMichal Meloun } 315e9034789SMichal Meloun 316e9034789SMichal Meloun static int 317e9034789SMichal Meloun tegra210_cpufreq_get(device_t dev, struct cf_setting *cf) 318e9034789SMichal Meloun { 319e9034789SMichal Meloun struct tegra210_cpufreq_softc *sc; 320e9034789SMichal Meloun 321e9034789SMichal Meloun if (cf == NULL) 322e9034789SMichal Meloun return (EINVAL); 323e9034789SMichal Meloun 324e9034789SMichal Meloun sc = device_get_softc(dev); 325e9034789SMichal Meloun memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf)); 326e9034789SMichal Meloun cf->dev = NULL; 327e9034789SMichal Meloun cf->freq = sc->act_speed_point->freq / 1000000; 328e9034789SMichal Meloun cf->volts = sc->act_speed_point->uvolt / 1000; 329e9034789SMichal Meloun /* Transition latency in us. */ 330e9034789SMichal Meloun cf->lat = sc->latency; 331e9034789SMichal Meloun /* Driver providing this setting. */ 332e9034789SMichal Meloun cf->dev = dev; 333e9034789SMichal Meloun 334e9034789SMichal Meloun return (0); 335e9034789SMichal Meloun } 336e9034789SMichal Meloun 337e9034789SMichal Meloun 338e9034789SMichal Meloun static int 339e9034789SMichal Meloun tegra210_cpufreq_type(device_t dev, int *type) 340e9034789SMichal Meloun { 341e9034789SMichal Meloun 342e9034789SMichal Meloun if (type == NULL) 343e9034789SMichal Meloun return (EINVAL); 344e9034789SMichal Meloun *type = CPUFREQ_TYPE_ABSOLUTE; 345e9034789SMichal Meloun 346e9034789SMichal Meloun return (0); 347e9034789SMichal Meloun } 348e9034789SMichal Meloun 349e9034789SMichal Meloun static int 350e9034789SMichal Meloun get_fdt_resources(struct tegra210_cpufreq_softc *sc, phandle_t node) 351e9034789SMichal Meloun { 352e9034789SMichal Meloun int rv; 353e9034789SMichal Meloun device_t parent_dev; 354e9034789SMichal Meloun 355e9034789SMichal Meloun parent_dev = device_get_parent(sc->dev); 356e9034789SMichal Meloun 357e9034789SMichal Meloun rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g); 358e9034789SMichal Meloun if (rv != 0) { 359e9034789SMichal Meloun device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv); 360e9034789SMichal Meloun return (ENXIO); 361e9034789SMichal Meloun } 362e9034789SMichal Meloun 363e9034789SMichal Meloun rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x); 364e9034789SMichal Meloun if (rv != 0) { 365e9034789SMichal Meloun device_printf(sc->dev, "Cannot get 'pll_x' clock\n"); 366e9034789SMichal Meloun return (ENXIO); 367e9034789SMichal Meloun } 368e9034789SMichal Meloun rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p); 369e9034789SMichal Meloun if (rv != 0) { 370e9034789SMichal Meloun device_printf(parent_dev, "Cannot get 'pll_p' clock\n"); 371e9034789SMichal Meloun return (ENXIO); 372e9034789SMichal Meloun } 373e9034789SMichal Meloun rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll); 374e9034789SMichal Meloun 375e9034789SMichal Meloun /* XXX DPLL is not implemented yet */ 376e9034789SMichal Meloun #if 0 377e9034789SMichal Meloun if (rv != 0) { 378e9034789SMichal Meloun device_printf(sc->dev, "Cannot get 'dfll' clock\n"); 379e9034789SMichal Meloun return (ENXIO); 380e9034789SMichal Meloun } 381e9034789SMichal Meloun #endif 382e9034789SMichal Meloun return (0); 383e9034789SMichal Meloun } 384e9034789SMichal Meloun 385e9034789SMichal Meloun static void 386e9034789SMichal Meloun tegra210_cpufreq_identify(driver_t *driver, device_t parent) 387e9034789SMichal Meloun { 388e9034789SMichal Meloun phandle_t root; 389e9034789SMichal Meloun 390e9034789SMichal Meloun root = OF_finddevice("/"); 391e9034789SMichal Meloun if (!ofw_bus_node_is_compatible(root, "nvidia,tegra210")) 392e9034789SMichal Meloun return; 393e9034789SMichal Meloun 394e9034789SMichal Meloun if (device_get_unit(parent) != 0) 395e9034789SMichal Meloun return; 396e9034789SMichal Meloun if (device_find_child(parent, "tegra210_cpufreq", -1) != NULL) 397e9034789SMichal Meloun return; 398e9034789SMichal Meloun if (BUS_ADD_CHILD(parent, 0, "tegra210_cpufreq", -1) == NULL) 399e9034789SMichal Meloun device_printf(parent, "add child failed\n"); 400e9034789SMichal Meloun } 401e9034789SMichal Meloun 402e9034789SMichal Meloun static int 403e9034789SMichal Meloun tegra210_cpufreq_probe(device_t dev) 404e9034789SMichal Meloun { 405e9034789SMichal Meloun 406e9034789SMichal Meloun device_set_desc(dev, "CPU Frequency Control"); 407e9034789SMichal Meloun 408e9034789SMichal Meloun return (0); 409e9034789SMichal Meloun } 410e9034789SMichal Meloun 411e9034789SMichal Meloun static int 412e9034789SMichal Meloun tegra210_cpufreq_attach(device_t dev) 413e9034789SMichal Meloun { 414e9034789SMichal Meloun struct tegra210_cpufreq_softc *sc; 415e9034789SMichal Meloun uint64_t freq; 416e9034789SMichal Meloun int rv; 417e9034789SMichal Meloun 418e9034789SMichal Meloun sc = device_get_softc(dev); 419e9034789SMichal Meloun sc->dev = dev; 420e9034789SMichal Meloun sc->node = ofw_bus_get_node(device_get_parent(dev)); 421e9034789SMichal Meloun 422e9034789SMichal Meloun sc->process_id = tegra_sku_info.cpu_process_id; 423e9034789SMichal Meloun sc->speedo_id = tegra_sku_info.cpu_speedo_id; 424e9034789SMichal Meloun sc->speedo_value = tegra_sku_info.cpu_speedo_value; 425e9034789SMichal Meloun 426e9034789SMichal Meloun sc->cpu_def = &tegra210_cpu_volt_def; 427e9034789SMichal Meloun 428e9034789SMichal Meloun rv = get_fdt_resources(sc, sc->node); 429e9034789SMichal Meloun if (rv != 0) { 430e9034789SMichal Meloun return (rv); 431e9034789SMichal Meloun } 432e9034789SMichal Meloun 433e9034789SMichal Meloun build_speed_points(sc); 434e9034789SMichal Meloun 435e9034789SMichal Meloun rv = clk_get_freq(sc->clk_cpu_g, &freq); 436e9034789SMichal Meloun if (rv != 0) { 437e9034789SMichal Meloun device_printf(dev, "Can't get CPU clock frequency\n"); 438e9034789SMichal Meloun return (rv); 439e9034789SMichal Meloun } 440e9034789SMichal Meloun if (sc->speedo_id < nitems(cpu_max_freq)) 441e9034789SMichal Meloun sc->cpu_max_freq = cpu_max_freq[sc->speedo_id]; 442e9034789SMichal Meloun else 443e9034789SMichal Meloun sc->cpu_max_freq = cpu_max_freq[0]; 444e9034789SMichal Meloun sc->act_speed_point = get_speed_point(sc, freq); 445e9034789SMichal Meloun 446e9034789SMichal Meloun /* Set safe startup CPU frequency. */ 447e9034789SMichal Meloun rv = set_cpu_freq(sc, 1632000000); 448e9034789SMichal Meloun if (rv != 0) { 449e9034789SMichal Meloun device_printf(dev, "Can't set initial CPU clock frequency\n"); 450e9034789SMichal Meloun return (rv); 451e9034789SMichal Meloun } 452e9034789SMichal Meloun 453e9034789SMichal Meloun /* This device is controlled by cpufreq(4). */ 454e9034789SMichal Meloun cpufreq_register(dev); 455e9034789SMichal Meloun 456e9034789SMichal Meloun return (0); 457e9034789SMichal Meloun } 458e9034789SMichal Meloun 459e9034789SMichal Meloun static int 460e9034789SMichal Meloun tegra210_cpufreq_detach(device_t dev) 461e9034789SMichal Meloun { 462e9034789SMichal Meloun struct tegra210_cpufreq_softc *sc; 463e9034789SMichal Meloun 464e9034789SMichal Meloun sc = device_get_softc(dev); 465e9034789SMichal Meloun cpufreq_unregister(dev); 466e9034789SMichal Meloun 467e9034789SMichal Meloun if (sc->clk_cpu_g != NULL) 468e9034789SMichal Meloun clk_release(sc->clk_cpu_g); 469e9034789SMichal Meloun if (sc->clk_pll_x != NULL) 470e9034789SMichal Meloun clk_release(sc->clk_pll_x); 471e9034789SMichal Meloun if (sc->clk_pll_p != NULL) 472e9034789SMichal Meloun clk_release(sc->clk_pll_p); 473e9034789SMichal Meloun if (sc->clk_dfll != NULL) 474e9034789SMichal Meloun clk_release(sc->clk_dfll); 475e9034789SMichal Meloun return (0); 476e9034789SMichal Meloun } 477e9034789SMichal Meloun 478e9034789SMichal Meloun static device_method_t tegra210_cpufreq_methods[] = { 479e9034789SMichal Meloun /* Device interface */ 480e9034789SMichal Meloun DEVMETHOD(device_identify, tegra210_cpufreq_identify), 481e9034789SMichal Meloun DEVMETHOD(device_probe, tegra210_cpufreq_probe), 482e9034789SMichal Meloun DEVMETHOD(device_attach, tegra210_cpufreq_attach), 483e9034789SMichal Meloun DEVMETHOD(device_detach, tegra210_cpufreq_detach), 484e9034789SMichal Meloun 485e9034789SMichal Meloun /* cpufreq interface */ 486e9034789SMichal Meloun DEVMETHOD(cpufreq_drv_set, tegra210_cpufreq_set), 487e9034789SMichal Meloun DEVMETHOD(cpufreq_drv_get, tegra210_cpufreq_get), 488e9034789SMichal Meloun DEVMETHOD(cpufreq_drv_settings, tegra210_cpufreq_settings), 489e9034789SMichal Meloun DEVMETHOD(cpufreq_drv_type, tegra210_cpufreq_type), 490e9034789SMichal Meloun 491e9034789SMichal Meloun DEVMETHOD_END 492e9034789SMichal Meloun }; 493e9034789SMichal Meloun 494e9034789SMichal Meloun static DEFINE_CLASS_0(tegra210_cpufreq, tegra210_cpufreq_driver, 495e9034789SMichal Meloun tegra210_cpufreq_methods, sizeof(struct tegra210_cpufreq_softc)); 496289f133bSJohn Baldwin DRIVER_MODULE(tegra210_cpufreq, cpu, tegra210_cpufreq_driver, NULL, NULL); 497