soc/tegra: Changes for v5.17-rc1
This set of changes contains some preparatory work that is shared by several branches and trees to support DVFS via power domains. There's also a bit of cleanup and improvements to reboot on chips that use PSCI. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAmG8sGYTHHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zocR8EACMoED5WAU1C+No9Bd9c7h2iru4LhUg wnobzGX0ZOPn/O3873rBqIiZDrJoVDJvqbh761pt35BigqJrAcegQaoMgBsvTl11 SAg6EpaQ2a/O7bPRj7V58XjxjlhoK+638PAuk7mzIDPibpnfZaNdJgI59/BEVVCd BQ7452z3LnPh9cFzCeqCMhvoJ2kTNgJvjGZ/EfOJE+tocwT+Fx83zzfM1kyflRr3 2yBJwSi3k/D5KK38OT+uPvHqduqsci/aTel2AlLtNvFJ2iPg7ax3G6ak2MHgq/ac noYL1kIiCBx3QQ3npiBKGnq2D1JBzX2mbbJxZrcGYZDW3D+2z1xPPtGNGCPiRMYQ DSnR3AoRegKQZn6Jy+b44kLn0NhrI58qR/PXCzpggl+yCnIAAaBObr+bSYYxlr/u sO189kVg1IYIILime9a5nxUvYalWSgXB/sUqQXjfJIMsDAHYmws3QqNw7Z628wxx 2ddWSKGrjkq+n3z0uR8xiQTdTGqeagYK+2yE4aVTfv65lsjBIlU75FhgsJUaGpBL 5Y2sSGB6ysPdxtoH0ol+vckZq3tSyZbqgoty5dOOHoEm+YeBSyXjaZwYTaLYIgd+ cUNKEsOBul1ZbT/ca2pEt+yTrKGrYgI7maa5ocwEYrvp4QFkHcNts5UmEwp+22pQ 8JqmzeJH92E4LA== =f+T3 -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmHAh0oACgkQmmx57+YA GNkwHhAAo+oITzjXFoHwCw97u6xOv9CxH6OFRSz2kbxBGHtniGx/lg7cxTIif4HX 4bnvUv3nQqoRMJ2hlGXp0QCjfXVTJXnk3Nduf74aEN2fkclFuR/PBOl4py0imubs 9ZVXpSSB4VSpXrtujAnlhO4nEEiFR0rXSTcJbUz3hqjVgatRm9pAGZmU0u5x/K0M idM46H+dDInWb53n/eJaj+GHoAJ18+yH62ck1OJqGSitaAfalGMFKRFHIqu8K95q ifadcrbFJo0711KKPRwgQPVgB1j7zFOjkaHDR7CjrsToRSVWbFwCgC6+nVg5X74h /wjvKq61pNvK7HavYIXWeUCzDqGbH404OjaksIYSw2Te2mJTOA0qOqh0edi6w6Y2 kVUcnqM5zt/TYaXsZdlxX1lTItAOQiV0yEuq/zPYBJ0ecgvhKOR0GHUseVsfVPrz NxEZGEAgkSrHbSITd1arPgOuvL9FwlqsmWi6ZV6n+o/hiYlz9G2YoHK2tcaKd8wX FqmgtKOPnoYg3h+7PoSahNLWEbEZEt8v6w6ezERm9U+ntl/sYISk1OCwwHCyqpfP hA7J9GlK3f2aYmE0eT828bov1PIFudpdgL0HFadEAKdpfn3G0kYf0wQ2UZ6J+XrW ZbcWhPzSTjKIDLWyrbRYu5Zo7I8uBVAA/eFopMRGbbn27ZgjrsM= =gToI -----END PGP SIGNATURE----- Merge tag 'tegra-for-5.17-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into arm/drivers soc/tegra: Changes for v5.17-rc1 This set of changes contains some preparatory work that is shared by several branches and trees to support DVFS via power domains. There's also a bit of cleanup and improvements to reboot on chips that use PSCI. * tag 'tegra-for-5.17-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: soc/tegra: pmc: Rename core power domain soc/tegra: pmc: Rename 3d power domains soc/tegra: regulators: Prepare for suspend soc/tegra: fuse: Use resource-managed helpers soc/tegra: fuse: Reset hardware soc/tegra: pmc: Add reboot notifier soc/tegra: Don't print error message when OPPs not available Link: https://lore.kernel.org/r/20211217162253.1801077-1-thierry.reding@gmail.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
b118863d2f
|
|
@ -136,9 +136,7 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev,
|
|||
*/
|
||||
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
|
||||
if (err != -ENODEV)
|
||||
dev_err(dev, "failed to add OPP table: %d\n", err);
|
||||
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
|
|
@ -181,6 +182,12 @@ static const struct nvmem_cell_info tegra_fuse_cells[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static void tegra_fuse_restore(void *base)
|
||||
{
|
||||
fuse->clk = NULL;
|
||||
fuse->base = base;
|
||||
}
|
||||
|
||||
static int tegra_fuse_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base = fuse->base;
|
||||
|
|
@ -188,13 +195,16 @@ static int tegra_fuse_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
int err;
|
||||
|
||||
err = devm_add_action(&pdev->dev, tegra_fuse_restore, base);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* take over the memory region from the early initialization */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
fuse->phys = res->start;
|
||||
fuse->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(fuse->base)) {
|
||||
err = PTR_ERR(fuse->base);
|
||||
fuse->base = base;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -204,19 +214,20 @@ static int tegra_fuse_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
|
||||
PTR_ERR(fuse->clk));
|
||||
|
||||
fuse->base = base;
|
||||
return PTR_ERR(fuse->clk);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, fuse);
|
||||
fuse->dev = &pdev->dev;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
err = devm_pm_runtime_enable(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fuse->soc->probe) {
|
||||
err = fuse->soc->probe(fuse);
|
||||
if (err < 0)
|
||||
goto restore;
|
||||
return err;
|
||||
}
|
||||
|
||||
memset(&nvmem, 0, sizeof(nvmem));
|
||||
|
|
@ -240,19 +251,37 @@ static int tegra_fuse_probe(struct platform_device *pdev)
|
|||
err = PTR_ERR(fuse->nvmem);
|
||||
dev_err(&pdev->dev, "failed to register NVMEM device: %d\n",
|
||||
err);
|
||||
goto restore;
|
||||
return err;
|
||||
}
|
||||
|
||||
fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse");
|
||||
if (IS_ERR(fuse->rst)) {
|
||||
err = PTR_ERR(fuse->rst);
|
||||
dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n",
|
||||
fuse->rst);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* FUSE clock is enabled at a boot time, hence this resume/suspend
|
||||
* disables the clock besides the h/w resetting.
|
||||
*/
|
||||
err = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_control_reset(fuse->rst);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to reset FUSE: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* release the early I/O memory mapping */
|
||||
iounmap(base);
|
||||
|
||||
return 0;
|
||||
|
||||
restore:
|
||||
fuse->clk = NULL;
|
||||
fuse->base = base;
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev)
|
||||
|
|
|
|||
|
|
@ -94,9 +94,28 @@ static bool dma_filter(struct dma_chan *chan, void *filter_param)
|
|||
return of_device_is_compatible(np, "nvidia,tegra20-apbdma");
|
||||
}
|
||||
|
||||
static void tegra20_fuse_release_channel(void *data)
|
||||
{
|
||||
struct tegra_fuse *fuse = data;
|
||||
|
||||
dma_release_channel(fuse->apbdma.chan);
|
||||
fuse->apbdma.chan = NULL;
|
||||
}
|
||||
|
||||
static void tegra20_fuse_free_coherent(void *data)
|
||||
{
|
||||
struct tegra_fuse *fuse = data;
|
||||
|
||||
dma_free_coherent(fuse->dev, sizeof(u32), fuse->apbdma.virt,
|
||||
fuse->apbdma.phys);
|
||||
fuse->apbdma.virt = NULL;
|
||||
fuse->apbdma.phys = 0x0;
|
||||
}
|
||||
|
||||
static int tegra20_fuse_probe(struct tegra_fuse *fuse)
|
||||
{
|
||||
dma_cap_mask_t mask;
|
||||
int err;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
|
@ -105,13 +124,21 @@ static int tegra20_fuse_probe(struct tegra_fuse *fuse)
|
|||
if (!fuse->apbdma.chan)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
err = devm_add_action_or_reset(fuse->dev, tegra20_fuse_release_channel,
|
||||
fuse);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fuse->apbdma.virt = dma_alloc_coherent(fuse->dev, sizeof(u32),
|
||||
&fuse->apbdma.phys,
|
||||
GFP_KERNEL);
|
||||
if (!fuse->apbdma.virt) {
|
||||
dma_release_channel(fuse->apbdma.chan);
|
||||
if (!fuse->apbdma.virt)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = devm_add_action_or_reset(fuse->dev, tegra20_fuse_free_coherent,
|
||||
fuse);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fuse->apbdma.config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
fuse->apbdma.config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ struct tegra_fuse {
|
|||
void __iomem *base;
|
||||
phys_addr_t phys;
|
||||
struct clk *clk;
|
||||
struct reset_control *rst;
|
||||
|
||||
u32 (*read_early)(struct tegra_fuse *fuse, unsigned int offset);
|
||||
u32 (*read)(struct tegra_fuse *fuse, unsigned int offset);
|
||||
|
|
|
|||
|
|
@ -1064,10 +1064,8 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
|
|||
return tegra_powergate_remove_clamping(id);
|
||||
}
|
||||
|
||||
static int tegra_pmc_restart_notify(struct notifier_block *this,
|
||||
unsigned long action, void *data)
|
||||
static void tegra_pmc_program_reboot_reason(const char *cmd)
|
||||
{
|
||||
const char *cmd = data;
|
||||
u32 value;
|
||||
|
||||
value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->scratch0);
|
||||
|
|
@ -1085,6 +1083,25 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
|
|||
}
|
||||
|
||||
tegra_pmc_scratch_writel(pmc, value, pmc->soc->regs->scratch0);
|
||||
}
|
||||
|
||||
static int tegra_pmc_reboot_notify(struct notifier_block *this,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
if (action == SYS_RESTART)
|
||||
tegra_pmc_program_reboot_reason(data);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block tegra_pmc_reboot_notifier = {
|
||||
.notifier_call = tegra_pmc_reboot_notify,
|
||||
};
|
||||
|
||||
static int tegra_pmc_restart_notify(struct notifier_block *this,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
|
||||
value = tegra_pmc_readl(pmc, PMC_CNTRL);
|
||||
|
|
@ -1353,7 +1370,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
|
|||
if (!genpd)
|
||||
return -ENOMEM;
|
||||
|
||||
genpd->name = np->name;
|
||||
genpd->name = "core";
|
||||
genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
|
||||
genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
|
||||
|
||||
|
|
@ -2890,6 +2907,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||
goto cleanup_sysfs;
|
||||
}
|
||||
|
||||
err = devm_register_reboot_notifier(&pdev->dev,
|
||||
&tegra_pmc_reboot_notifier);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
|
||||
err);
|
||||
goto cleanup_debugfs;
|
||||
}
|
||||
|
||||
err = register_restart_handler(&tegra_pmc_restart_handler);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to register restart handler, %d\n",
|
||||
|
|
@ -2963,7 +2988,7 @@ static SIMPLE_DEV_PM_OPS(tegra_pmc_pm_ops, tegra_pmc_suspend, tegra_pmc_resume);
|
|||
|
||||
static const char * const tegra20_powergates[] = {
|
||||
[TEGRA_POWERGATE_CPU] = "cpu",
|
||||
[TEGRA_POWERGATE_3D] = "3d",
|
||||
[TEGRA_POWERGATE_3D] = "td",
|
||||
[TEGRA_POWERGATE_VENC] = "venc",
|
||||
[TEGRA_POWERGATE_VDEC] = "vdec",
|
||||
[TEGRA_POWERGATE_PCIE] = "pcie",
|
||||
|
|
@ -3071,7 +3096,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
|
|||
|
||||
static const char * const tegra30_powergates[] = {
|
||||
[TEGRA_POWERGATE_CPU] = "cpu0",
|
||||
[TEGRA_POWERGATE_3D] = "3d0",
|
||||
[TEGRA_POWERGATE_3D] = "td",
|
||||
[TEGRA_POWERGATE_VENC] = "venc",
|
||||
[TEGRA_POWERGATE_VDEC] = "vdec",
|
||||
[TEGRA_POWERGATE_PCIE] = "pcie",
|
||||
|
|
@ -3083,7 +3108,7 @@ static const char * const tegra30_powergates[] = {
|
|||
[TEGRA_POWERGATE_CPU2] = "cpu2",
|
||||
[TEGRA_POWERGATE_CPU3] = "cpu3",
|
||||
[TEGRA_POWERGATE_CELP] = "celp",
|
||||
[TEGRA_POWERGATE_3D1] = "3d1",
|
||||
[TEGRA_POWERGATE_3D1] = "td2",
|
||||
};
|
||||
|
||||
static const u8 tegra30_cpu_powergates[] = {
|
||||
|
|
@ -3132,7 +3157,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
|
|||
|
||||
static const char * const tegra114_powergates[] = {
|
||||
[TEGRA_POWERGATE_CPU] = "crail",
|
||||
[TEGRA_POWERGATE_3D] = "3d",
|
||||
[TEGRA_POWERGATE_3D] = "td",
|
||||
[TEGRA_POWERGATE_VENC] = "venc",
|
||||
[TEGRA_POWERGATE_VDEC] = "vdec",
|
||||
[TEGRA_POWERGATE_MPE] = "mpe",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@
|
|||
#include <linux/regulator/coupler.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
struct tegra_regulator_coupler {
|
||||
|
|
@ -25,9 +27,12 @@ struct tegra_regulator_coupler {
|
|||
struct regulator_dev *cpu_rdev;
|
||||
struct regulator_dev *rtc_rdev;
|
||||
struct notifier_block reboot_notifier;
|
||||
struct notifier_block suspend_notifier;
|
||||
int core_min_uV, cpu_min_uV;
|
||||
bool sys_reboot_mode_req;
|
||||
bool sys_reboot_mode;
|
||||
bool sys_suspend_mode_req;
|
||||
bool sys_suspend_mode;
|
||||
};
|
||||
|
||||
static inline struct tegra_regulator_coupler *
|
||||
|
|
@ -105,6 +110,28 @@ static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev,
|
|||
return 150000;
|
||||
}
|
||||
|
||||
static int tegra20_cpu_nominal_uV(void)
|
||||
{
|
||||
switch (tegra_sku_info.soc_speedo_id) {
|
||||
case 0:
|
||||
return 1100000;
|
||||
case 1:
|
||||
return 1025000;
|
||||
default:
|
||||
return 1125000;
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra20_core_nominal_uV(void)
|
||||
{
|
||||
switch (tegra_sku_info.soc_speedo_id) {
|
||||
default:
|
||||
return 1225000;
|
||||
case 2:
|
||||
return 1300000;
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
|
||||
struct regulator_dev *core_rdev,
|
||||
struct regulator_dev *rtc_rdev,
|
||||
|
|
@ -144,6 +171,11 @@ static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
|
|||
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;
|
||||
|
|
@ -279,6 +311,11 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra,
|
|||
if (tegra->sys_reboot_mode)
|
||||
cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
|
||||
|
||||
/* prepare voltage level for suspend */
|
||||
if (tegra->sys_suspend_mode)
|
||||
cpu_min_uV = clamp(tegra20_cpu_nominal_uV(),
|
||||
cpu_min_uV, cpu_max_uV);
|
||||
|
||||
if (cpu_min_uV > cpu_uV) {
|
||||
err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
|
||||
cpu_uV, cpu_min_uV);
|
||||
|
|
@ -320,6 +357,7 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
|
|||
}
|
||||
|
||||
tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
|
||||
tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req);
|
||||
|
||||
if (rdev == cpu_rdev)
|
||||
return tegra20_cpu_voltage_update(tegra, cpu_rdev,
|
||||
|
|
@ -334,6 +372,63 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
|
|||
return -EPERM;
|
||||
}
|
||||
|
||||
static int tegra20_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra,
|
||||
bool sys_suspend_mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!tegra->core_rdev || !tegra->rtc_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.
|
||||
*/
|
||||
|
||||
WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode);
|
||||
|
||||
err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = regulator_sync_voltage_rdev(tegra->core_rdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra20_regulator_suspend(struct notifier_block *notifier,
|
||||
unsigned long mode, void *arg)
|
||||
{
|
||||
struct tegra_regulator_coupler *tegra;
|
||||
int ret = 0;
|
||||
|
||||
tegra = container_of(notifier, struct tegra_regulator_coupler,
|
||||
suspend_notifier);
|
||||
|
||||
switch (mode) {
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_RESTORE_PREPARE:
|
||||
case PM_SUSPEND_PREPARE:
|
||||
ret = tegra20_regulator_prepare_suspend(tegra, true);
|
||||
break;
|
||||
|
||||
case PM_POST_HIBERNATION:
|
||||
case PM_POST_RESTORE:
|
||||
case PM_POST_SUSPEND:
|
||||
ret = tegra20_regulator_prepare_suspend(tegra, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
pr_err("failed to prepare regulators: %d\n", ret);
|
||||
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
|
||||
bool sys_reboot_mode)
|
||||
{
|
||||
|
|
@ -444,6 +539,7 @@ static struct tegra_regulator_coupler tegra20_coupler = {
|
|||
.balance_voltage = tegra20_regulator_balance_voltage,
|
||||
},
|
||||
.reboot_notifier.notifier_call = tegra20_regulator_reboot,
|
||||
.suspend_notifier.notifier_call = tegra20_regulator_suspend,
|
||||
};
|
||||
|
||||
static int __init tegra_regulator_coupler_init(void)
|
||||
|
|
@ -456,6 +552,9 @@ static int __init tegra_regulator_coupler_init(void)
|
|||
err = register_reboot_notifier(&tegra20_coupler.reboot_notifier);
|
||||
WARN_ON(err);
|
||||
|
||||
err = register_pm_notifier(&tegra20_coupler.suspend_notifier);
|
||||
WARN_ON(err);
|
||||
|
||||
return regulator_coupler_register(&tegra20_coupler.coupler);
|
||||
}
|
||||
arch_initcall(tegra_regulator_coupler_init);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/regulator/coupler.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
|
@ -25,9 +26,12 @@ struct tegra_regulator_coupler {
|
|||
struct regulator_dev *core_rdev;
|
||||
struct regulator_dev *cpu_rdev;
|
||||
struct notifier_block reboot_notifier;
|
||||
struct notifier_block suspend_notifier;
|
||||
int core_min_uV, cpu_min_uV;
|
||||
bool sys_reboot_mode_req;
|
||||
bool sys_reboot_mode;
|
||||
bool sys_suspend_mode_req;
|
||||
bool sys_suspend_mode;
|
||||
};
|
||||
|
||||
static inline struct tegra_regulator_coupler *
|
||||
|
|
@ -113,6 +117,52 @@ static int tegra30_core_cpu_limit(int cpu_uV)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tegra30_cpu_nominal_uV(void)
|
||||
{
|
||||
switch (tegra_sku_info.cpu_speedo_id) {
|
||||
case 10 ... 11:
|
||||
return 850000;
|
||||
|
||||
case 9:
|
||||
return 912000;
|
||||
|
||||
case 1 ... 3:
|
||||
case 7 ... 8:
|
||||
return 1050000;
|
||||
|
||||
default:
|
||||
return 1125000;
|
||||
|
||||
case 4 ... 6:
|
||||
case 12 ... 13:
|
||||
return 1237000;
|
||||
}
|
||||
}
|
||||
|
||||
static int 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;
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
|
||||
struct regulator_dev *cpu_rdev,
|
||||
struct regulator_dev *core_rdev)
|
||||
|
|
@ -168,6 +218,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
|
|||
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;
|
||||
|
|
@ -223,6 +278,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
|
|||
if (tegra->sys_reboot_mode)
|
||||
cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
|
||||
|
||||
/* prepare voltage level for suspend */
|
||||
if (tegra->sys_suspend_mode)
|
||||
cpu_min_uV = clamp(tegra30_cpu_nominal_uV(),
|
||||
cpu_min_uV, cpu_max_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);
|
||||
|
|
@ -292,10 +352,68 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler,
|
|||
}
|
||||
|
||||
tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
|
||||
tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req);
|
||||
|
||||
return tegra30_voltage_update(tegra, cpu_rdev, core_rdev);
|
||||
}
|
||||
|
||||
static int 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.
|
||||
*/
|
||||
|
||||
WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode);
|
||||
|
||||
err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = regulator_sync_voltage_rdev(tegra->core_rdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_regulator_suspend(struct notifier_block *notifier,
|
||||
unsigned long mode, void *arg)
|
||||
{
|
||||
struct tegra_regulator_coupler *tegra;
|
||||
int ret = 0;
|
||||
|
||||
tegra = container_of(notifier, struct tegra_regulator_coupler,
|
||||
suspend_notifier);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
|
||||
bool sys_reboot_mode)
|
||||
{
|
||||
|
|
@ -395,6 +513,7 @@ static struct tegra_regulator_coupler tegra30_coupler = {
|
|||
.balance_voltage = tegra30_regulator_balance_voltage,
|
||||
},
|
||||
.reboot_notifier.notifier_call = tegra30_regulator_reboot,
|
||||
.suspend_notifier.notifier_call = tegra30_regulator_suspend,
|
||||
};
|
||||
|
||||
static int __init tegra_regulator_coupler_init(void)
|
||||
|
|
@ -407,6 +526,9 @@ static int __init tegra_regulator_coupler_init(void)
|
|||
err = register_reboot_notifier(&tegra30_coupler.reboot_notifier);
|
||||
WARN_ON(err);
|
||||
|
||||
err = register_pm_notifier(&tegra30_coupler.suspend_notifier);
|
||||
WARN_ON(err);
|
||||
|
||||
return regulator_coupler_register(&tegra30_coupler.coupler);
|
||||
}
|
||||
arch_initcall(tegra_regulator_coupler_init);
|
||||
|
|
|
|||
Loading…
Reference in New Issue