staticint tegra30_core_limit(struct tegra_regulator_coupler *tegra, struct regulator_dev *core_rdev)
{ int core_min_uV = 0; int core_max_uV; int core_cur_uV; int err;
/* * Tegra30 SoC has critical DVFS-capable devices that are * permanently-active or active at a boot time, like EMC * (DRAM controller) or Display controller for example. * * The voltage of a CORE SoC power domain shall not be dropped below * a minimum level, which is determined by device's clock rate. * This means that we can't fully allow CORE voltage scaling until * the state of all DVFS-critical CORE devices is synced.
*/ if (tegra_pmc_core_domain_state_synced() && !tegra->sys_reboot_mode) {
pr_info_once("voltage state synced\n"); return 0;
}
if (tegra->core_min_uV > 0) return tegra->core_min_uV;
core_cur_uV = regulator_get_voltage_rdev(core_rdev); if (core_cur_uV < 0) return core_cur_uV;
core_max_uV = max(core_cur_uV, 1200000);
err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); if (err) return err;
/* * Limit minimum CORE voltage to a value left from bootloader or, * if it's unreasonably low value, to the most common 1.2v or to * whatever maximum value defined via board's device-tree.
*/
tegra->core_min_uV = core_max_uV;
pr_info("core voltage initialized to %duV\n", tegra->core_min_uV);
return tegra->core_min_uV;
}
staticint tegra30_core_cpu_limit(int cpu_uV)
{ if (cpu_uV < 800000) return 950000;
if (cpu_uV < 900000) return 1000000;
if (cpu_uV < 1000000) return 1100000;
if (cpu_uV < 1100000) return 1200000;
if (cpu_uV < 1250000) { switch (tegra_sku_info.cpu_speedo_id) { case 0 ... 1: case 4: case 7 ... 8: return 1200000;
staticint tegra30_core_nominal_uV(void)
{ switch (tegra_sku_info.soc_speedo_id) { case 0: return 1200000;
case 1: if (tegra_sku_info.cpu_speedo_id != 7 &&
tegra_sku_info.cpu_speedo_id != 8) return 1200000;
fallthrough;
case 2: if (tegra_sku_info.cpu_speedo_id != 13) return 1300000;
return 1350000;
default: return 1250000;
}
}
staticint tegra30_voltage_update(struct tegra_regulator_coupler *tegra, struct regulator_dev *cpu_rdev, struct regulator_dev *core_rdev)
{ int core_min_uV, core_max_uV = INT_MAX; int cpu_min_uV, cpu_max_uV = INT_MAX; int cpu_min_uV_consumers = 0; int core_min_limited_uV; int core_target_uV; int cpu_target_uV; int core_max_step; int cpu_max_step; int max_spread; int core_uV; int cpu_uV; int err;
/* * CPU voltage should not got lower than 300mV from the CORE. * CPU voltage should stay below the CORE by 100mV+, depending * by the CORE voltage. This applies to all Tegra30 SoC's.
*/
max_spread = cpu_rdev->constraints->max_spread[0];
cpu_max_step = cpu_rdev->constraints->max_uV_step;
core_max_step = core_rdev->constraints->max_uV_step;
if (!max_spread) {
pr_err_once("cpu-core max-spread is undefined in device-tree\n");
max_spread = 300000;
}
if (!cpu_max_step) {
pr_err_once("cpu max-step is undefined in device-tree\n");
cpu_max_step = 150000;
}
if (!core_max_step) {
pr_err_once("core max-step is undefined in device-tree\n");
core_max_step = 150000;
}
/* * The CORE voltage scaling is currently not hooked up in drivers, * hence we will limit the minimum CORE voltage to a reasonable value. * This should be good enough for the time being.
*/
core_min_uV = tegra30_core_limit(tegra, core_rdev); if (core_min_uV < 0) return core_min_uV;
err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV,
PM_SUSPEND_ON); if (err) return err;
/* prepare voltage level for suspend */ if (tegra->sys_suspend_mode)
core_min_uV = clamp(tegra30_core_nominal_uV(),
core_min_uV, core_max_uV);
core_uV = regulator_get_voltage_rdev(core_rdev); if (core_uV < 0) return core_uV;
cpu_min_uV = core_min_uV - max_spread;
err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV,
PM_SUSPEND_ON); if (err) return err;
err = regulator_check_consumers(cpu_rdev, &cpu_min_uV_consumers,
&cpu_max_uV, PM_SUSPEND_ON); if (err) return err;
err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV); if (err) return err;
cpu_uV = regulator_get_voltage_rdev(cpu_rdev); if (cpu_uV < 0) return cpu_uV;
/* store boot voltage level */ if (!tegra->cpu_min_uV)
tegra->cpu_min_uV = cpu_uV;
/* * CPU's regulator may not have any consumers, hence the voltage * must not be changed in that case because CPU simply won't * survive the voltage drop if it's running on a higher frequency.
*/ if (!cpu_min_uV_consumers)
cpu_min_uV = max(cpu_uV, cpu_min_uV);
/* * Bootloader shall set up voltages correctly, but if it * happens that there is a violation, then try to fix it * at first.
*/
core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV); if (core_min_limited_uV < 0) return core_min_limited_uV;
staticint tegra30_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra, bool sys_suspend_mode)
{ int err;
if (!tegra->core_rdev || !tegra->cpu_rdev) return 0;
/* * All power domains are enabled early during resume from suspend * by GENPD core. Domains like VENC may require a higher voltage * when enabled during resume from suspend. This also prepares * hardware for resuming from LP0.
*/
switch (mode) { case PM_HIBERNATION_PREPARE: case PM_RESTORE_PREPARE: case PM_SUSPEND_PREPARE:
ret = tegra30_regulator_prepare_suspend(tegra, true); break;
case PM_POST_HIBERNATION: case PM_POST_RESTORE: case PM_POST_SUSPEND:
ret = tegra30_regulator_prepare_suspend(tegra, false); break;
}
if (ret)
pr_err("failed to prepare regulators: %d\n", ret);
return notifier_from_errno(ret);
}
staticint tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, bool sys_reboot_mode)
{ int err;
if (!tegra->core_rdev || !tegra->cpu_rdev) return 0;
WRITE_ONCE(tegra->sys_reboot_mode_req, true);
/* * Some devices use CPU soft-reboot method and in this case we * should ensure that voltages are sane for the reboot by restoring * the minimum boot levels.
*/
err = regulator_sync_voltage_rdev(tegra->cpu_rdev); if (err) return err;
err = regulator_sync_voltage_rdev(tegra->core_rdev); if (err) return err;
/* * We don't expect regulators to be decoupled during reboot, * this may race with the reboot handler and shouldn't ever * happen in practice.
*/ if (WARN_ON_ONCE(system_state > SYSTEM_RUNNING)) return -EPERM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.