Merge tag 'tegra-for-5.17-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra...
authorArnd Bergmann <arnd@arndb.de>
Mon, 20 Dec 2021 13:38:17 +0000 (14:38 +0100)
committerArnd Bergmann <arnd@arndb.de>
Mon, 20 Dec 2021 13:38:18 +0000 (14:38 +0100)
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>
drivers/soc/tegra/common.c
drivers/soc/tegra/fuse/fuse-tegra.c
drivers/soc/tegra/fuse/fuse-tegra20.c
drivers/soc/tegra/fuse/fuse.h
drivers/soc/tegra/pmc.c
drivers/soc/tegra/regulators-tegra20.c
drivers/soc/tegra/regulators-tegra30.c

index 35c882da55fcd8a1586e3a848e75975091b11849..32c346b72635a83987d15569fc9937073e2febc2 100644 (file)
@@ -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;
index f2151815db585b3f22bd968f0f1e984069b3b3fb..fe4f935ce73ad4450f19252aada5539918566f72 100644 (file)
@@ -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)
index 8ec9fc5e5e4b5875016675773465d4b55a7a1c9b..12503f563e3600a2718861c8f056354b9c4c0380 100644 (file)
@@ -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;
index de58feba0435015fecd06f20acdcbd911f52f464..1b719d85bd045f3f8616c9fee981d16ea7e9f4a5 100644 (file)
@@ -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);
index 575d6d5b42941b410e47d111b70a3d4a21ba82ff..5aceacbd8ce040a5d0591d027ba3febe301690f0 100644 (file)
@@ -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",
index b8ce9fd0650ddc223134e29b31606cf9a899feb0..6a2f90ab9d3eb7658cbbee8ea99119301e660e33 100644 (file)
@@ -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);
index e74bbc9c78597f3425820b1760ada7efc37d6f91..8fd43c68913499ce42a6eea619ff76dd21142fdd 100644 (file)
@@ -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);