mmc: tegra: Support sdhci rails off before reboot
Ken Chang [Tue, 12 Mar 2013 07:42:27 +0000 (15:42 +0800)]
Bug 1222606
Bug 1242658

Change-Id: I1f6aeab31d42d953fe73cecacb7a3d117054090c
Signed-off-by: Ken Chang <kenc@nvidia.com>
(cherry picked from commit b00f8fbe8a3ce474d6f64a9aed9ef7f592ea8e77)
Reviewed-on: http://git-master/r/208324
Reviewed-on: http://git-master/r/213886
GVS: Gerrit_Virtual_Submit
Reviewed-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>

arch/arm/mach-tegra/include/mach/sdhci.h
drivers/mmc/host/sdhci-tegra.c

index 7eb5b02..d382e44 100644 (file)
@@ -56,6 +56,7 @@ struct tegra_sdhci_platform_data {
        struct mmc_platform_data mmc_data;
        bool edp_support;
        unsigned int edp_states[SD_EDP_NUM_STATES];
+       bool power_off_rail;
 };
 
 #endif
index d94677d..1b03765 100644 (file)
@@ -35,6 +35,7 @@
 #include <asm/gpio.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/reboot.h>
 
 #include <mach/gpio-tegra.h>
 #include <mach/sdhci.h>
@@ -245,6 +246,7 @@ struct sdhci_tegra {
        /* Freq tuning information for each sampling clock freq */
        struct tegra_tuning_data tuning_data;
        bool is_parent_pllc;
+       struct notifier_block reboot_notify;
 };
 
 static struct clk *pll_c;
@@ -1801,6 +1803,32 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
        return plat;
 }
 
+static void tegra_sdhci_rail_off(struct sdhci_tegra *tegra_host)
+{
+       if (tegra_host->is_rail_enabled) {
+               if (tegra_host->vdd_slot_reg)
+                       regulator_disable(tegra_host->vdd_slot_reg);
+               if (tegra_host->vdd_io_reg)
+                       regulator_disable(tegra_host->vdd_io_reg);
+               tegra_host->is_rail_enabled = false;
+       }
+}
+
+static int tegra_sdhci_reboot_notify(struct notifier_block *nb,
+                               unsigned long event, void *data)
+{
+       struct sdhci_tegra *tegra_host =
+               container_of(nb, struct sdhci_tegra, reboot_notify);
+
+       switch (event) {
+       case SYS_RESTART:
+       case SYS_POWER_OFF:
+               tegra_sdhci_rail_off(tegra_host);
+               return NOTIFY_OK;
+       }
+       return NOTIFY_DONE;
+}
+
 static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match;
@@ -2066,6 +2094,11 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        /* Enable async suspend/resume to reduce LP0 latency */
        device_enable_async_suspend(&pdev->dev);
 
+       if (plat->power_off_rail) {
+               tegra_host->reboot_notify.notifier_call =
+                       tegra_sdhci_reboot_notify;
+               register_reboot_notifier(&tegra_host->reboot_notify);
+       }
        return 0;
 
 err_add_host:
@@ -2131,6 +2164,9 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
        }
        clk_put(pltfm_host->clk);
 
+       if (plat->power_off_rail)
+               unregister_reboot_notifier(&tegra_host->reboot_notify);
+
        sdhci_pltfm_free(pdev);
 
        return 0;