Commit 87d7426f authored by Thierry Reding's avatar Thierry Reding
Browse files

Merge branch 'for-5.14/soc' into for-5.14/memory

parents dd44ca51 30b44e81
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -4105,6 +4105,29 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel);

int regulator_sync_voltage_rdev(struct regulator_dev *rdev)
{
	int ret;

	regulator_lock(rdev);

	if (!rdev->desc->ops->set_voltage &&
	    !rdev->desc->ops->set_voltage_sel) {
		ret = -EINVAL;
		goto out;
	}

	/* balance only, if regulator is coupled */
	if (rdev->coupling_desc.n_coupled > 1)
		ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON);
	else
		ret = -EOPNOTSUPP;

out:
	regulator_unlock(rdev);
	return ret;
}

/**
 * regulator_sync_voltage - re-apply last regulator output voltage
 * @regulator: regulator source
+97 −0
Original line number Diff line number Diff line
@@ -3,9 +3,16 @@
 * Copyright (C) 2014 NVIDIA CORPORATION.  All rights reserved.
 */

#define dev_fmt(fmt)	"tegra-soc: " fmt

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/of.h>
#include <linux/pm_opp.h>

#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>

static const struct of_device_id tegra_machine_match[] = {
	{ .compatible = "nvidia,tegra20", },
@@ -31,3 +38,93 @@ bool soc_is_tegra(void)

	return match != NULL;
}

static int tegra_core_dev_init_opp_state(struct device *dev)
{
	unsigned long rate;
	struct clk *clk;
	int err;

	clk = devm_clk_get(dev, NULL);
	if (IS_ERR(clk)) {
		dev_err(dev, "failed to get clk: %pe\n", clk);
		return PTR_ERR(clk);
	}

	rate = clk_get_rate(clk);
	if (!rate) {
		dev_err(dev, "failed to get clk rate\n");
		return -EINVAL;
	}

	/* first dummy rate-setting initializes voltage vote */
	err = dev_pm_opp_set_rate(dev, rate);
	if (err) {
		dev_err(dev, "failed to initialize OPP clock: %d\n", err);
		return err;
	}

	return 0;
}

/**
 * devm_tegra_core_dev_init_opp_table() - initialize OPP table
 * @dev: device for which OPP table is initialized
 * @params: pointer to the OPP table configuration
 *
 * This function will initialize OPP table and sync OPP state of a Tegra SoC
 * core device.
 *
 * Return: 0 on success or errorno.
 */
int devm_tegra_core_dev_init_opp_table(struct device *dev,
				       struct tegra_core_opp_params *params)
{
	u32 hw_version;
	int err;

	err = devm_pm_opp_set_clkname(dev, NULL);
	if (err) {
		dev_err(dev, "failed to set OPP clk: %d\n", err);
		return err;
	}

	/* Tegra114+ doesn't support OPP yet */
	if (!of_machine_is_compatible("nvidia,tegra20") &&
	    !of_machine_is_compatible("nvidia,tegra30"))
		return -ENODEV;

	if (of_machine_is_compatible("nvidia,tegra20"))
		hw_version = BIT(tegra_sku_info.soc_process_id);
	else
		hw_version = BIT(tegra_sku_info.soc_speedo_id);

	err = devm_pm_opp_set_supported_hw(dev, &hw_version, 1);
	if (err) {
		dev_err(dev, "failed to set OPP supported HW: %d\n", err);
		return err;
	}

	/*
	 * Older device-trees have an empty OPP table, we will get
	 * -ENODEV from devm_pm_opp_of_add_table() in this case.
	 */
	err = devm_pm_opp_of_add_table(dev);
	if (err) {
		if (err == -ENODEV)
			dev_err_once(dev, "OPP table not found, please update device-tree\n");
		else
			dev_err(dev, "failed to add OPP table: %d\n", err);

		return err;
	}

	if (params->init_state) {
		err = tegra_core_dev_init_opp_state(dev);
		if (err)
			return err;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(devm_tegra_core_dev_init_opp_table);
+74 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/reboot.h>
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
@@ -21,7 +22,10 @@ struct tegra_regulator_coupler {
	struct regulator_dev *core_rdev;
	struct regulator_dev *cpu_rdev;
	struct regulator_dev *rtc_rdev;
	int core_min_uV;
	struct notifier_block reboot_notifier;
	int core_min_uV, cpu_min_uV;
	bool sys_reboot_mode_req;
	bool sys_reboot_mode;
};

static inline struct tegra_regulator_coupler *
@@ -242,6 +246,10 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra,
	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
@@ -250,6 +258,10 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra,
	if (!cpu_min_uV_consumers)
		cpu_min_uV = cpu_uV;

	/* restore boot voltage level */
	if (tegra->sys_reboot_mode)
		cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);

	if (cpu_min_uV > cpu_uV) {
		err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
					      cpu_uV, cpu_min_uV);
@@ -290,6 +302,8 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
		return -EINVAL;
	}

	tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);

	if (rdev == cpu_rdev)
		return tegra20_cpu_voltage_update(tegra, cpu_rdev,
						  core_rdev, rtc_rdev);
@@ -303,6 +317,51 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
	return -EPERM;
}

static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
					    bool sys_reboot_mode)
{
	int err;

	if (!tegra->core_rdev || !tegra->rtc_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;

	WRITE_ONCE(tegra->sys_reboot_mode_req, sys_reboot_mode);

	return 0;
}

static int tegra20_regulator_reboot(struct notifier_block *notifier,
				    unsigned long event, void *cmd)
{
	struct tegra_regulator_coupler *tegra;
	int ret;

	if (event != SYS_RESTART)
		return NOTIFY_DONE;

	tegra = container_of(notifier, struct tegra_regulator_coupler,
			     reboot_notifier);

	ret = tegra20_regulator_prepare_reboot(tegra, true);

	return notifier_from_errno(ret);
}

static int tegra20_regulator_attach(struct regulator_coupler *coupler,
				    struct regulator_dev *rdev)
{
@@ -335,6 +394,14 @@ static int tegra20_regulator_detach(struct regulator_coupler *coupler,
{
	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);

	/*
	 * 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;

	if (tegra->core_rdev == rdev) {
		tegra->core_rdev = NULL;
		return 0;
@@ -359,13 +426,19 @@ static struct tegra_regulator_coupler tegra20_coupler = {
		.detach_regulator = tegra20_regulator_detach,
		.balance_voltage = tegra20_regulator_balance_voltage,
	},
	.reboot_notifier.notifier_call = tegra20_regulator_reboot,
};

static int __init tegra_regulator_coupler_init(void)
{
	int err;

	if (!of_machine_is_compatible("nvidia,tegra20"))
		return 0;

	err = register_reboot_notifier(&tegra20_coupler.reboot_notifier);
	WARN_ON(err);

	return regulator_coupler_register(&tegra20_coupler.coupler);
}
arch_initcall(tegra_regulator_coupler_init);
+74 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/reboot.h>
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
@@ -22,7 +23,10 @@ struct tegra_regulator_coupler {
	struct regulator_coupler coupler;
	struct regulator_dev *core_rdev;
	struct regulator_dev *cpu_rdev;
	int core_min_uV;
	struct notifier_block reboot_notifier;
	int core_min_uV, cpu_min_uV;
	bool sys_reboot_mode_req;
	bool sys_reboot_mode;
};

static inline struct tegra_regulator_coupler *
@@ -172,6 +176,10 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
	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
@@ -195,6 +203,10 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
	if (err)
		return err;

	/* restore boot voltage level */
	if (tegra->sys_reboot_mode)
		cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);

	if (core_min_limited_uV > core_uV) {
		pr_err("core voltage constraint violated: %d %d %d\n",
		       core_uV, core_min_limited_uV, cpu_uV);
@@ -263,9 +275,56 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler,
		return -EINVAL;
	}

	tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);

	return tegra30_voltage_update(tegra, cpu_rdev, core_rdev);
}

static int 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;

	WRITE_ONCE(tegra->sys_reboot_mode_req, sys_reboot_mode);

	return 0;
}

static int tegra30_regulator_reboot(struct notifier_block *notifier,
				    unsigned long event, void *cmd)
{
	struct tegra_regulator_coupler *tegra;
	int ret;

	if (event != SYS_RESTART)
		return NOTIFY_DONE;

	tegra = container_of(notifier, struct tegra_regulator_coupler,
			     reboot_notifier);

	ret = tegra30_regulator_prepare_reboot(tegra, true);

	return notifier_from_errno(ret);
}

static int tegra30_regulator_attach(struct regulator_coupler *coupler,
				    struct regulator_dev *rdev)
{
@@ -292,6 +351,14 @@ static int tegra30_regulator_detach(struct regulator_coupler *coupler,
{
	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);

	/*
	 * 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;

	if (tegra->core_rdev == rdev) {
		tegra->core_rdev = NULL;
		return 0;
@@ -311,13 +378,19 @@ static struct tegra_regulator_coupler tegra30_coupler = {
		.detach_regulator = tegra30_regulator_detach,
		.balance_voltage = tegra30_regulator_balance_voltage,
	},
	.reboot_notifier.notifier_call = tegra30_regulator_reboot,
};

static int __init tegra_regulator_coupler_init(void)
{
	int err;

	if (!of_machine_is_compatible("nvidia,tegra30"))
		return 0;

	err = register_reboot_notifier(&tegra30_coupler.reboot_notifier);
	WARN_ON(err);

	return regulator_coupler_register(&tegra30_coupler.coupler);
}
arch_initcall(tegra_regulator_coupler_init);
+1 −0
Original line number Diff line number Diff line
@@ -540,6 +540,7 @@ int regulator_set_current_limit_regmap(struct regulator_dev *rdev,
int regulator_get_current_limit_regmap(struct regulator_dev *rdev);
void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
int regulator_set_ramp_delay_regmap(struct regulator_dev *rdev, int ramp_delay);
int regulator_sync_voltage_rdev(struct regulator_dev *rdev);

/*
 * Helper functions intended to be used by regulator drivers prior registering
Loading