lkml.org 
[lkml]   [2021]   [Aug]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v8 28/34] soc/tegra: regulators: Prepare for suspend
    Date
    Depending on hardware version, Tegra SoC may require a higher voltages
    during resume from system suspend, otherwise hardware will crash. Set
    SoC voltages to a nominal levels during suspend.

    Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
    ---
    drivers/soc/tegra/regulators-tegra20.c | 99 ++++++++++++++++++++
    drivers/soc/tegra/regulators-tegra30.c | 122 +++++++++++++++++++++++++
    2 files changed, 221 insertions(+)

    diff --git a/drivers/soc/tegra/regulators-tegra20.c b/drivers/soc/tegra/regulators-tegra20.c
    index b8ce9fd0650d..6a2f90ab9d3e 100644
    --- a/drivers/soc/tegra/regulators-tegra20.c
    +++ b/drivers/soc/tegra/regulators-tegra20.c
    @@ -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);
    diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c
    index e74bbc9c7859..8fd43c689134 100644
    --- a/drivers/soc/tegra/regulators-tegra30.c
    +++ b/drivers/soc/tegra/regulators-tegra30.c
    @@ -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);
    --
    2.32.0
    \
     
     \ /
      Last update: 2021-08-17 03:32    [W:8.739 / U:0.532 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site