/* * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #define KHZ 1000 #define MHZ 1000000 #define VDD_SAFE_STEP 100 static bool tegra_dvfs_cpu_disabled; static bool tegra_dvfs_core_disabled; static int cpu_millivolts[MAX_DVFS_FREQS]; static int cpu_dfll_millivolts[MAX_DVFS_FREQS]; static int vdd_core_therm_trips_table[MAX_THERMAL_LIMITS] = { 20, }; static int vdd_core_therm_floors_table[MAX_THERMAL_LIMITS] = { 900, }; static struct tegra_cooling_device core_vmin_cdev = { .cdev_type = "core_cold", }; static struct dvfs_rail tegra124_dvfs_rail_vdd_cpu = { .reg_id = "vdd_cpu", .max_millivolts = 1300, .min_millivolts = 700, .step = VDD_SAFE_STEP, .jmp_to_zero = true, .alignment = { .step_uv = 10000, /* 10mV */ }, }; static struct dvfs_rail tegra124_dvfs_rail_vdd_core = { .reg_id = "vdd_core", .max_millivolts = 1400, .min_millivolts = 800, .step = VDD_SAFE_STEP, .step_up = 1400, .vmin_cdev = &core_vmin_cdev, }; static struct dvfs_rail *tegra124_dvfs_rails[] = { &tegra124_dvfs_rail_vdd_cpu, &tegra124_dvfs_rail_vdd_core, }; static struct dvfs cpu_dvfs = { .clk_name = "cclk_g", .millivolts = cpu_millivolts, .dfll_millivolts = cpu_dfll_millivolts, .auto_dvfs = true, .dvfs_rail = &tegra124_dvfs_rail_vdd_cpu, }; /* CPU DVFS tables */ static unsigned long cpu_max_freq[] = { /* speedo_id 0 1 2 3 4 5 */ 2014500, 2320500, 2116500, 2524500, 1811000, 2218500, }; static struct cpu_dvfs cpu_fv_dvfs_table[] = { { .speedo_id = -1, .process_id = -1, .min_mv = 720, .max_mv = 1260, .fv_table = { {204000, 800}, {306000, 800}, {408000, 800}, {510000, 800}, {612000, 800}, {714000, 800}, {816000, 820}, {918000, 840}, {1020000, 880}, {1122000, 900}, {1224000, 930}, {1326000, 960}, {1428000, 990}, {1530000, 1020}, {1632000, 1070}, {1734000, 1100}, {1836000, 1140}, {1938000, 1180}, {2014500, 1220}, {2116500, 1260}, {2218500, 1310}, {2320500, 1360}, {2422500, 1400}, {2524500, 1400}, }, }, }; /* Core DVFS tables */ static const int core_millivolts[MAX_DVFS_FREQS] = { 800, 850, 900, 950, 1000, 1050, 1100, 1150}; #define CORE_DVFS(_clk_name, _speedo_id, _process_id, _auto, _mult, _freqs...) \ { \ .clk_name = _clk_name, \ .speedo_id = _speedo_id, \ .process_id = _process_id, \ .freqs = {_freqs}, \ .freqs_mult = _mult, \ .millivolts = core_millivolts, \ .auto_dvfs = _auto, \ .dvfs_rail = &tegra124_dvfs_rail_vdd_core, \ } static struct dvfs core_dvfs_table[] = { /* Core voltages (mV): 800, 850, 900, 950, 1000, 1050, 1100, 1150 */ /* Clock limits for internal blocks, PLLs */ CORE_DVFS("emc", -1, -1, 1, KHZ, 264000, 348000, 384000, 384000, 528000, 528000, 1200000, 1200000), CORE_DVFS("cclk_lp", 0, 0, 1, KHZ, 312000, 528000, 660000, 804000, 912000, 1044000, 1044000, 1044000), CORE_DVFS("cclk_lp", 0, 1, 1, KHZ, 312000, 564000, 696000, 828000, 960000, 1044000, 1044000, 1044000), CORE_DVFS("cclk_lp", 1, -1, 1, KHZ, 312000, 564000, 696000, 828000, 960000, 1092000, 1092000, 1092000), CORE_DVFS("sbus", 0, 0, 1, KHZ, 120000, 192000, 228000, 264000, 312000, 348000, 372000, 372000), CORE_DVFS("sbus", 0, 1, 1, KHZ, 120000, 204000, 252000, 288000, 324000, 360000, 372000, 372000), CORE_DVFS("sbus", 1, -1, 1, KHZ, 120000, 204000, 252000, 288000, 324000, 360000, 384000, 384000), CORE_DVFS("vic03", 0, 0, 1, KHZ, 180000, 324000, 408000, 492000, 588000, 660000, 708000, 756000), CORE_DVFS("vic03", 0, 1, 1, KHZ, 180000, 336000, 420000, 504000, 600000, 684000, 756000, 756000), CORE_DVFS("vic03", 1, -1, 1, KHZ, 180000, 336000, 420000, 504000, 600000, 684000, 756000, 828000), CORE_DVFS("tsec", 0, 0, 1, KHZ, 180000, 324000, 408000, 492000, 588000, 660000, 708000, 756000), CORE_DVFS("tsec", 0, 1, 1, KHZ, 180000, 336000, 420000, 504000, 600000, 684000, 756000, 756000), CORE_DVFS("tsec", 1, -1, 1, KHZ, 180000, 336000, 420000, 504000, 600000, 684000, 756000, 828000), CORE_DVFS("msenc", 0, 0, 1, KHZ, 120000, 216000, 288000, 336000, 384000, 432000, 456000, 480000), CORE_DVFS("msenc", 0, 1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 480000), CORE_DVFS("msenc", 1, -1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 528000), CORE_DVFS("se", 0, 0, 1, KHZ, 120000, 216000, 288000, 336000, 384000, 432000, 456000, 480000), CORE_DVFS("se", 0, 1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 480000), CORE_DVFS("se", 1, -1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 528000), CORE_DVFS("vde", 0, 0, 1, KHZ, 120000, 216000, 288000, 336000, 384000, 432000, 456000, 480000), CORE_DVFS("vde", 0, 1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 480000), CORE_DVFS("vde", 1, -1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 528000), CORE_DVFS("host1x", 0, 0, 1, KHZ, 108000, 156000, 204000, 240000, 348000, 372000, 408000, 408000), CORE_DVFS("host1x", 0, 1, 1, KHZ, 108000, 156000, 204000, 252000, 348000, 384000, 408000, 408000), CORE_DVFS("host1x", 1, -1, 1, KHZ, 108000, 156000, 204000, 252000, 348000, 384000, 444000, 444000), CORE_DVFS("vi", 0, 0, 1, KHZ, 228000, 408000, 480000, 600000, 600000, 600000, 600000, 600000), CORE_DVFS("vi", 0, 1, 1, KHZ, 228000, 420000, 480000, 600000, 600000, 600000, 600000, 600000), CORE_DVFS("vi", 1, -1, 1, KHZ, 228000, 420000, 480000, 600000, 600000, 600000, 600000, 600000), CORE_DVFS("isp", 0, 0, 1, KHZ, 228000, 408000, 480000, 600000, 600000, 600000, 600000, 600000), CORE_DVFS("isp", 0, 1, 1, KHZ, 228000, 420000, 480000, 600000, 600000, 600000, 600000, 600000), CORE_DVFS("isp", 1, -1, 1, KHZ, 228000, 420000, 480000, 600000, 600000, 600000, 600000, 600000), CORE_DVFS("c2bus", 0, 0, 1, KHZ, 120000, 216000, 288000, 336000, 384000, 432000, 456000, 480000), CORE_DVFS("c2bus", 0, 1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 480000), CORE_DVFS("c2bus", 1, -1, 1, KHZ, 120000, 228000, 276000, 348000, 396000, 444000, 480000, 528000), CORE_DVFS("c3bus", 0, 0, 1, KHZ, 180000, 324000, 408000, 492000, 588000, 660000, 708000, 756000), CORE_DVFS("c3bus", 0, 1, 1, KHZ, 180000, 336000, 420000, 504000, 600000, 684000, 756000, 756000), CORE_DVFS("c3bus", 1, -1, 1, KHZ, 180000, 336000, 420000, 504000, 600000, 684000, 756000, 828000), CORE_DVFS("pll_m", -1, -1, 1, KHZ, 800000, 800000, 1066000, 1066000, 1066000, 1066000, 1200000, 1200000), CORE_DVFS("pll_c", -1, -1, 1, KHZ, 800000, 800000, 1066000, 1066000, 1066000, 1066000, 1066000, 1066000), CORE_DVFS("pll_c2", -1, -1, 1, KHZ, 800000, 800000, 1066000, 1066000, 1066000, 1066000, 1066000, 1066000), CORE_DVFS("pll_c3", -1, -1, 1, KHZ, 800000, 800000, 1066000, 1066000, 1066000, 1066000, 1066000, 1066000), /* Clock limits for I/O peripherals */ CORE_DVFS("sbc1", -1, -1, 1, KHZ, 33000, 33000, 33000, 33000, 33000, 33000, 51000, 51000), CORE_DVFS("sbc2", -1, -1, 1, KHZ, 33000, 33000, 33000, 33000, 33000, 33000, 51000, 51000), CORE_DVFS("sbc3", -1, -1, 1, KHZ, 33000, 33000, 33000, 33000, 33000, 33000, 51000, 51000), CORE_DVFS("sbc4", -1, -1, 1, KHZ, 33000, 33000, 33000, 33000, 33000, 33000, 51000, 51000), CORE_DVFS("sbc5", -1, -1, 1, KHZ, 33000, 33000, 33000, 33000, 33000, 33000, 51000, 51000), CORE_DVFS("sbc6", -1, -1, 1, KHZ, 33000, 33000, 33000, 33000, 33000, 33000, 51000, 51000), CORE_DVFS("sdmmc1", -1, -1, 1, KHZ, 1, 1, 82000, 82000, 136000, 136000, 136000, 204000), CORE_DVFS("sdmmc3", -1, -1, 1, KHZ, 1, 1, 82000, 82000, 136000, 136000, 136000, 204000), CORE_DVFS("sdmmc4", -1, -1, 1, KHZ, 1, 1, 82000, 82000, 136000, 136000, 136000, 204000), CORE_DVFS("hdmi", -1, -1, 1, KHZ, 1, 148500, 148500, 297000, 297000, 297000, 297000, 297000), CORE_DVFS("nor", -1, -1, 1, KHZ, 102000, 102000, 102000, 102000, 102000, 102000, 102000, 102000), CORE_DVFS("pciex", -1, -1, 1, KHZ, 1, 250000, 250000, 500000, 500000, 500000, 500000, 500000), CORE_DVFS("mselect", -1, -1, 1, KHZ, 102000, 102000, 204000, 204000, 204000, 204000, 408000, 408000), CORE_DVFS("xusb_falcon_src", -1, -1, 1, KHZ, 1, 336000, 336000, 336000, 336000, 336000, 336000, 336000), CORE_DVFS("xusb_host_src", -1, -1, 1, KHZ, 1, 112000, 112000, 112000, 112000, 112000, 112000, 112000), CORE_DVFS("xusb_dev_src", -1, -1, 1, KHZ, 1, 58300, 58300, 58300, 112000, 112000, 112000, 112000), CORE_DVFS("xusb_ss_src", -1, -1, 1, KHZ, 1, 120000, 120000, 120000, 120000, 120000, 120000, 120000), CORE_DVFS("xusb_fs_src", -1, -1, 1, KHZ, 1, 48000, 48000, 48000, 48000, 48000, 48000, 48000), CORE_DVFS("xusb_hs_src", -1, -1, 1, KHZ, 1, 60000, 60000, 60000, 60000, 60000, 60000, 60000), CORE_DVFS("hda", -1, -1, 1, KHZ, 1, 108000, 108000, 108000, 108000, 108000, 108000, 108000), CORE_DVFS("hda2codec_2x", -1, -1, 1, KHZ, 1, 48000, 48000, 48000, 48000, 48000, 48000, 48000), CORE_DVFS("sor0", -1, -1, 1, KHZ, 162000, 270000, 540000, 540000, 540000, 540000, 540000, 540000), /* * The clock rate for the display controllers that determines the * necessary core voltage depends on a divider that is internal * to the display block. Disable auto-dvfs on the display clocks, * and let the display driver call tegra_dvfs_set_rate manually */ CORE_DVFS("disp1", 0, 0, 0, KHZ, 148500, 241000, 297000, 297000, 297000, 474000, 474000, 474000), CORE_DVFS("disp1", 0, 1, 0, KHZ, 148500, 241000, 297000, 297000, 474000, 474000, 474000, 474000), CORE_DVFS("disp1", 1, -1, 0, KHZ, 148500, 241000, 297000, 297000, 474000, 474000, 474000, 533000), CORE_DVFS("disp2", 0, 0, 0, KHZ, 148500, 241000, 297000, 297000, 297000, 474000, 474000, 474000), CORE_DVFS("disp2", 0, 1, 0, KHZ, 148500, 241000, 297000, 297000, 474000, 474000, 474000, 474000), CORE_DVFS("disp2", 1, -1, 0, KHZ, 148500, 241000, 297000, 297000, 474000, 474000, 474000, 533000), }; int tegra_dvfs_disable_core_set(const char *arg, const struct kernel_param *kp) { int ret; ret = param_set_bool(arg, kp); if (ret) return ret; if (tegra_dvfs_core_disabled) tegra_dvfs_rail_disable(&tegra124_dvfs_rail_vdd_core); else tegra_dvfs_rail_enable(&tegra124_dvfs_rail_vdd_core); return 0; } int tegra_dvfs_disable_cpu_set(const char *arg, const struct kernel_param *kp) { int ret; ret = param_set_bool(arg, kp); if (ret) return ret; if (tegra_dvfs_cpu_disabled) tegra_dvfs_rail_disable(&tegra124_dvfs_rail_vdd_cpu); else tegra_dvfs_rail_enable(&tegra124_dvfs_rail_vdd_cpu); return 0; } int tegra_dvfs_disable_get(char *buffer, const struct kernel_param *kp) { return param_get_bool(buffer, kp); } static struct kernel_param_ops tegra_dvfs_disable_core_ops = { .set = tegra_dvfs_disable_core_set, .get = tegra_dvfs_disable_get, }; static struct kernel_param_ops tegra_dvfs_disable_cpu_ops = { .set = tegra_dvfs_disable_cpu_set, .get = tegra_dvfs_disable_get, }; module_param_cb(disable_core, &tegra_dvfs_disable_core_ops, &tegra_dvfs_core_disabled, 0644); module_param_cb(disable_cpu, &tegra_dvfs_disable_cpu_ops, &tegra_dvfs_cpu_disabled, 0644); static void init_dvfs_one(struct dvfs *d, int max_freq_index) { int ret; struct clk *c = clk_get_sys(d->clk_name, d->clk_name); if (IS_ERR(c)) { pr_debug("tegra124_dvfs: no clock found for %s\n", d->clk_name); return; } d->max_millivolts = d->dvfs_rail->nominal_millivolts; ret = tegra_setup_dvfs(c, d); if (ret) pr_err("tegra124_dvfs: failed to enable dvfs on %s\n", __clk_get_name(c)); } static bool match_dvfs_one(const char *name, int dvfs_speedo_id, int dvfs_process_id, int speedo_id, int process_id) { if ((dvfs_process_id != -1 && dvfs_process_id != process_id) || (dvfs_speedo_id != -1 && dvfs_speedo_id != speedo_id)) { pr_debug("tegra124_dvfs: rejected %s speedo %d, process %d\n", name, dvfs_speedo_id, dvfs_process_id); return false; } return true; } static int round_voltage(int mv, struct rail_alignment *align, bool up) { if (align->step_uv) { int uv = max(mv * 1000, align->offset_uv) - align->offset_uv; uv = (uv + (up ? align->step_uv - 1 : 0)) / align->step_uv; return (uv * align->step_uv + align->offset_uv) / 1000; } return mv; } static int set_cpu_dvfs_data(unsigned long max_freq, struct cpu_dvfs *d, struct dvfs *cpu_dvfs, int *max_freq_index) { int i, mv, dfll_mv, min_dfll_mv, num_freqs; unsigned long fmax_at_vmin = 0; unsigned long fmin_use_dfll = 0; unsigned long *freqs; int *dfll_millivolts; struct rail_alignment *align = &tegra124_dvfs_rail_vdd_cpu.alignment; min_dfll_mv = d->min_mv; min_dfll_mv = round_voltage(min_dfll_mv, align, true); d->max_mv = round_voltage(d->max_mv, align, false); BUG_ON(min_dfll_mv < tegra124_dvfs_rail_vdd_cpu.min_millivolts); if (tegra124_dfll_get_fv_table(&num_freqs, &freqs, &dfll_millivolts)) return -EPROBE_DEFER; for (i = 0; i < num_freqs; i++) { if (freqs[i] != d->fv_table[i].freq * 1000) { pr_err("Err: DFLL freq ladder does not match PLL's\n"); return -EINVAL; } if (d->fv_table[i].freq > max_freq) break; mv = d->fv_table[i].volt; /* * Check maximum frequency at minimum voltage for dfll source; * round down unless all table entries are above Vmin, then use * the 1st entry as is. */ dfll_mv = max(dfll_millivolts[i], min_dfll_mv); if (dfll_mv > min_dfll_mv) { if (!i) fmax_at_vmin = freqs[i]; if (!fmax_at_vmin) fmax_at_vmin = freqs[i - 1]; } /* Clip maximum frequency at maximum voltage for pll source */ if ((mv > d->max_mv) && !i) { pr_err("Err: volt of 1st entry is higher than Vmax\n"); return -EINVAL; } /* Minimum rate with pll source voltage above dfll Vmin */ if ((mv >= min_dfll_mv) && !fmin_use_dfll) fmin_use_dfll = freqs[i]; /* fill in dvfs tables */ cpu_dvfs->freqs[i] = freqs[i]; cpu_millivolts[i] = mv; cpu_dfll_millivolts[i] = min(dfll_mv, d->max_mv); } /* * In the dfll operating range dfll voltage at any rate should be * better (below) than pll voltage */ if (!fmin_use_dfll || (fmin_use_dfll > fmax_at_vmin)) fmin_use_dfll = fmax_at_vmin; /* dvfs tables are successfully populated - fill in the rest */ cpu_dvfs->speedo_id = d->speedo_id; cpu_dvfs->process_id = d->process_id; cpu_dvfs->dvfs_rail->nominal_millivolts = min(d->max_mv, max(cpu_millivolts[i - 1], cpu_dfll_millivolts[i - 1])); *max_freq_index = i - 1; cpu_dvfs->use_dfll_rate_min = fmin_use_dfll; return 0; } static int get_core_nominal_mv_index(int speedo_id) { int i; int mv = tegra124_get_core_speedo_mv(); if (mv < 0) { pr_err("Can't get Tegra124 speedo mv\n"); return -EINVAL; } /* Round nominal level down to the nearest core scaling step */ for (i = 0; i < MAX_DVFS_FREQS; i++) { if ((core_millivolts[i] == 0) || (mv < core_millivolts[i])) break; } if (i == 0) { pr_err("tegra124-dvfs: failed to get nominal idx at volt %d\n", mv); return -ENOSYS; } return i - 1; } static void adjust_emc_dvfs_table(struct dvfs *d) { unsigned long rate; int i; for (i = 0; i < ARRAY_SIZE(core_millivolts); i++) { if (core_millivolts[i] == 0) return; rate = tegra124_predict_emc_rate(core_millivolts[i]); if (rate < 0) return; d->freqs[i] = rate; } } int tegra124_init_dvfs(void) { int cpu_speedo_id = tegra_get_cpu_speedo_id(); int cpu_process_id = tegra_get_cpu_process_id(); int soc_speedo_id = tegra_get_soc_speedo_id(); int core_process_id = tegra_get_core_process_id(); int i, ret; int core_nominal_mv_index; int cpu_max_freq_index = 0; /* * Find nominal voltages for core (1st) and cpu rails before rail * init. Nominal voltage index in core scaling ladder can also be * used to determine max dvfs frequencies for all core clocks. In * case of error disable core scaling and set index to 0, so that * core clocks would not exceed rates allowed at minimum voltage. */ core_nominal_mv_index = get_core_nominal_mv_index(soc_speedo_id); if (core_nominal_mv_index < 0) { tegra124_dvfs_rail_vdd_core.disabled = true; tegra_dvfs_core_disabled = true; core_nominal_mv_index = 0; } tegra124_dvfs_rail_vdd_core.nominal_millivolts = core_millivolts[core_nominal_mv_index]; /* * Setup cpu dvfs and dfll tables from cvb data, determine nominal * voltage for cpu rail, and cpu maximum frequency. Note that entire * frequency range is guaranteed only when dfll is used as cpu clock * source. Reaching maximum frequency with pll as cpu clock source * may not be possible within nominal voltage range (dvfs mechanism * would automatically fail frequency request in this case, so that * voltage limit is not violated). Error when cpu dvfs table can not * be constructed must never happen. */ BUG_ON(cpu_speedo_id >= ARRAY_SIZE(cpu_max_freq)); for (ret = 0, i = 0; i < ARRAY_SIZE(cpu_fv_dvfs_table); i++) { struct cpu_dvfs *d = &cpu_fv_dvfs_table[i]; unsigned long max_freq = cpu_max_freq[cpu_speedo_id]; if (match_dvfs_one("cpu dvfs", d->speedo_id, d->process_id, cpu_speedo_id, cpu_process_id)) { ret = set_cpu_dvfs_data(max_freq, d, &cpu_dvfs, &cpu_max_freq_index); if (ret) goto out; break; } } BUG_ON(i == ARRAY_SIZE(cpu_fv_dvfs_table)); /* Init core thermal profile */ tegra_dvfs_rail_init_vmin_thermal_profile(vdd_core_therm_trips_table, vdd_core_therm_floors_table, &tegra124_dvfs_rail_vdd_core); /* Init rail structures and dependencies */ tegra_dvfs_init_rails(tegra124_dvfs_rails, ARRAY_SIZE(tegra124_dvfs_rails)); /* * Search core dvfs table for speedo/process matching entries and * initialize dvfs-ed clocks */ for (i = 0; i < ARRAY_SIZE(core_dvfs_table); i++) { struct dvfs *d = &core_dvfs_table[i]; if (!match_dvfs_one(d->clk_name, d->speedo_id, d->process_id, soc_speedo_id, core_process_id)) continue; init_dvfs_one(d, core_nominal_mv_index); /* * EMC dvfs is board dependent, the EMC scaling frequencies are * determined by the Tegra BCT and the board specific EMC DFS * table owned by EMC driver. */ if (!strcmp(d->clk_name, "emc") && tegra124_emc_is_ready()) adjust_emc_dvfs_table(d); } /* * Initialize matching cpu dvfs entry already found when nominal * voltage was determined */ init_dvfs_one(&cpu_dvfs, cpu_max_freq_index); pr_info("tegra dvfs: VDD_CPU nominal %dmV, scaling %s\n", tegra124_dvfs_rail_vdd_cpu.nominal_millivolts, tegra_dvfs_cpu_disabled ? "disabled" : "enabled"); pr_info("tegra dvfs: VDD_CORE nominal %dmV, scaling %s\n", tegra124_dvfs_rail_vdd_core.nominal_millivolts, tegra_dvfs_core_disabled ? "disabled" : "enabled"); return 0; out: return ret; }