diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-pxav3.c')
-rw-r--r-- | drivers/mmc/host/sdhci-pxav3.c | 117 |
1 files changed, 103 insertions, 14 deletions
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index fad0966427fd..a0cdbc570a83 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -32,10 +32,14 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include "sdhci.h" #include "sdhci-pltfm.h" +#define PXAV3_RPM_DELAY_MS 50 + #define SD_CLOCK_BURST_SIZE_SETUP 0x10A #define SDCLK_SEL 0x100 #define SDCLK_DELAY_SHIFT 9 @@ -163,18 +167,11 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) return 0; } -static u32 pxav3_get_max_clock(struct sdhci_host *host) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - - return clk_get_rate(pltfm_host->clk); -} - static struct sdhci_ops pxav3_sdhci_ops = { .platform_reset_exit = pxav3_set_private_registers, .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, - .get_max_clock = pxav3_get_max_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, }; #ifdef CONFIG_OF @@ -303,20 +300,37 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); + pm_runtime_use_autosuspend(&pdev->dev); + pm_suspend_ignore_children(&pdev->dev, 1); + pm_runtime_get_noresume(&pdev->dev); + ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "failed to add host\n"); + pm_runtime_forbid(&pdev->dev); + pm_runtime_disable(&pdev->dev); goto err_add_host; } platform_set_drvdata(pdev, host); + if (pdata->pm_caps & MMC_PM_KEEP_POWER) { + device_init_wakeup(&pdev->dev, 1); + host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ; + } else { + device_init_wakeup(&pdev->dev, 0); + } + + pm_runtime_put_autosuspend(&pdev->dev); + return 0; err_add_host: clk_disable_unprepare(clk); clk_put(clk); - mmc_gpio_free_cd(host->mmc); err_cd_req: err_clk_get: sdhci_pltfm_free(pdev); @@ -329,16 +343,14 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + pm_runtime_get_sync(&pdev->dev); sdhci_remove_host(host, 1); + pm_runtime_disable(&pdev->dev); clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); - if (gpio_is_valid(pdata->ext_cd_gpio)) - mmc_gpio_free_cd(host->mmc); - sdhci_pltfm_free(pdev); kfree(pxa); @@ -347,6 +359,83 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int sdhci_pxav3_suspend(struct device *dev) +{ + int ret; + struct sdhci_host *host = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + ret = sdhci_suspend_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int sdhci_pxav3_resume(struct device *dev) +{ + int ret; + struct sdhci_host *host = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + ret = sdhci_resume_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int sdhci_pxav3_runtime_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + unsigned long flags; + + if (pltfm_host->clk) { + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = true; + spin_unlock_irqrestore(&host->lock, flags); + + clk_disable_unprepare(pltfm_host->clk); + } + + return 0; +} + +static int sdhci_pxav3_runtime_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + unsigned long flags; + + if (pltfm_host->clk) { + clk_prepare_enable(pltfm_host->clk); + + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = false; + spin_unlock_irqrestore(&host->lock, flags); + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +static const struct dev_pm_ops sdhci_pxav3_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume) + SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, + sdhci_pxav3_runtime_resume, NULL) +}; + +#define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops) + +#else +#define SDHCI_PXAV3_PMOPS NULL +#endif + static struct platform_driver sdhci_pxav3_driver = { .driver = { .name = "sdhci-pxav3", @@ -354,7 +443,7 @@ static struct platform_driver sdhci_pxav3_driver = { .of_match_table = sdhci_pxav3_of_match, #endif .owner = THIS_MODULE, - .pm = SDHCI_PLTFM_PMOPS, + .pm = SDHCI_PXAV3_PMOPS, }, .probe = sdhci_pxav3_probe, .remove = sdhci_pxav3_remove, |