forked from Minki/linux
Merge branch 'for-5.14/soc' into for-5.14/memory
This commit is contained in:
commit
87d7426f16
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -6,6 +6,37 @@
|
||||
#ifndef __SOC_TEGRA_COMMON_H__
|
||||
#define __SOC_TEGRA_COMMON_H__
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device;
|
||||
|
||||
/**
|
||||
* Tegra SoC core device OPP table configuration
|
||||
*
|
||||
* @init_state: pre-initialize OPP state of a device
|
||||
*/
|
||||
struct tegra_core_opp_params {
|
||||
bool init_state;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA
|
||||
bool soc_is_tegra(void);
|
||||
|
||||
int devm_tegra_core_dev_init_opp_table(struct device *dev,
|
||||
struct tegra_core_opp_params *params);
|
||||
#else
|
||||
static inline bool soc_is_tegra(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int
|
||||
devm_tegra_core_dev_init_opp_table(struct device *dev,
|
||||
struct tegra_core_opp_params *params)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SOC_TEGRA_COMMON_H__ */
|
||||
|
@ -52,14 +52,28 @@ struct tegra_sku_info {
|
||||
enum tegra_revision revision;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA
|
||||
extern struct tegra_sku_info tegra_sku_info;
|
||||
u32 tegra_read_straps(void);
|
||||
u32 tegra_read_ram_code(void);
|
||||
int tegra_fuse_readl(unsigned long offset, u32 *value);
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA
|
||||
extern struct tegra_sku_info tegra_sku_info;
|
||||
#else
|
||||
static struct tegra_sku_info tegra_sku_info __maybe_unused;
|
||||
|
||||
static inline u32 tegra_read_straps(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 tegra_read_ram_code(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tegra_fuse_readl(unsigned long offset, u32 *value)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct device *tegra_soc_device_register(void);
|
||||
|
Loading…
Reference in New Issue
Block a user