static int tegra20_core_rtc_update()

in tegra/regulators-tegra20.c [135:252]


static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
				   struct regulator_dev *core_rdev,
				   struct regulator_dev *rtc_rdev,
				   int cpu_uV, int cpu_min_uV)
{
	int core_min_uV, core_max_uV = INT_MAX;
	int rtc_min_uV, rtc_max_uV = INT_MAX;
	int core_target_uV;
	int rtc_target_uV;
	int max_spread;
	int core_uV;
	int rtc_uV;
	int err;

	/*
	 * RTC and CORE voltages should be no more than 170mV from each other,
	 * CPU should be below RTC and CORE by at least 120mV. This applies
	 * to all Tegra20 SoC's.
	 */
	max_spread = tegra20_core_rtc_max_spread(core_rdev, rtc_rdev);

	/*
	 * 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 = tegra20_core_limit(tegra, core_rdev);
	if (core_min_uV < 0)
		return core_min_uV;

	err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV);
	if (err)
		return err;

	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(tegra20_core_nominal_uV(),
				    core_min_uV, core_max_uV);

	core_uV = regulator_get_voltage_rdev(core_rdev);
	if (core_uV < 0)
		return core_uV;

	core_min_uV = max(cpu_min_uV + 125000, core_min_uV);
	if (core_min_uV > core_max_uV)
		return -EINVAL;

	if (cpu_uV + 120000 > core_uV)
		pr_err("core-cpu voltage constraint violated: %d %d\n",
		       core_uV, cpu_uV + 120000);

	rtc_uV = regulator_get_voltage_rdev(rtc_rdev);
	if (rtc_uV < 0)
		return rtc_uV;

	if (cpu_uV + 120000 > rtc_uV)
		pr_err("rtc-cpu voltage constraint violated: %d %d\n",
		       rtc_uV, cpu_uV + 120000);

	if (abs(core_uV - rtc_uV) > 170000)
		pr_err("core-rtc voltage constraint violated: %d %d\n",
		       core_uV, rtc_uV);

	rtc_min_uV = max(cpu_min_uV + 125000, core_min_uV - max_spread);

	err = regulator_check_voltage(rtc_rdev, &rtc_min_uV, &rtc_max_uV);
	if (err)
		return err;

	while (core_uV != core_min_uV || rtc_uV != rtc_min_uV) {
		if (core_uV < core_min_uV) {
			core_target_uV = min(core_uV + max_spread, core_min_uV);
			core_target_uV = min(rtc_uV + max_spread, core_target_uV);
		} else {
			core_target_uV = max(core_uV - max_spread, core_min_uV);
			core_target_uV = max(rtc_uV - max_spread, core_target_uV);
		}

		if (core_uV == core_target_uV)
			goto update_rtc;

		err = regulator_set_voltage_rdev(core_rdev,
						 core_target_uV,
						 core_max_uV,
						 PM_SUSPEND_ON);
		if (err)
			return err;

		core_uV = core_target_uV;
update_rtc:
		if (rtc_uV < rtc_min_uV) {
			rtc_target_uV = min(rtc_uV + max_spread, rtc_min_uV);
			rtc_target_uV = min(core_uV + max_spread, rtc_target_uV);
		} else {
			rtc_target_uV = max(rtc_uV - max_spread, rtc_min_uV);
			rtc_target_uV = max(core_uV - max_spread, rtc_target_uV);
		}

		if (rtc_uV == rtc_target_uV)
			continue;

		err = regulator_set_voltage_rdev(rtc_rdev,
						 rtc_target_uV,
						 rtc_max_uV,
						 PM_SUSPEND_ON);
		if (err)
			return err;

		rtc_uV = rtc_target_uV;
	}

	return 0;
}