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