diff options
author | Balaji T K <balajitk@ti.com> | 2011-07-01 12:39:35 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-07-20 17:21:17 -0400 |
commit | fa4aa2d48dabed9d1288d235524cb8d0a8e81c00 (patch) | |
tree | 5aeadac6fca6521d511dd59c5a9917208c9dec64 /drivers/mmc | |
parent | 7a8c2cef3dd6ffc586dfc5e3f63b73b4be2662e7 (diff) |
mmc: omap_hsmmc: add runtime pm support
* Add runtime pm support to HSMMC host controller.
* Use runtime pm API to enable/disable HSMMC clock.
* Use runtime autosuspend APIs to enable auto suspend delay.
Based on OMAP HSMMC runtime implementation by Kevin Hilman and
Kishore Kadiyala.
Signed-off-by: Balaji T K <balajitk@ti.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 111 |
1 files changed, 56 insertions, 55 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index c7de6d62b943..10122b1cc424 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/semaphore.h> | 33 | #include <linux/semaphore.h> |
34 | #include <linux/gpio.h> | 34 | #include <linux/gpio.h> |
35 | #include <linux/regulator/consumer.h> | 35 | #include <linux/regulator/consumer.h> |
36 | #include <linux/pm_runtime.h> | ||
36 | #include <plat/dma.h> | 37 | #include <plat/dma.h> |
37 | #include <mach/hardware.h> | 38 | #include <mach/hardware.h> |
38 | #include <plat/board.h> | 39 | #include <plat/board.h> |
@@ -116,6 +117,7 @@ | |||
116 | #define OMAP_MMC4_DEVID 3 | 117 | #define OMAP_MMC4_DEVID 3 |
117 | #define OMAP_MMC5_DEVID 4 | 118 | #define OMAP_MMC5_DEVID 4 |
118 | 119 | ||
120 | #define MMC_AUTOSUSPEND_DELAY 100 | ||
119 | #define MMC_TIMEOUT_MS 20 | 121 | #define MMC_TIMEOUT_MS 20 |
120 | #define OMAP_MMC_MASTER_CLOCK 96000000 | 122 | #define OMAP_MMC_MASTER_CLOCK 96000000 |
121 | #define DRIVER_NAME "omap_hsmmc" | 123 | #define DRIVER_NAME "omap_hsmmc" |
@@ -1156,8 +1158,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) | |||
1156 | int ret; | 1158 | int ret; |
1157 | 1159 | ||
1158 | /* Disable the clocks */ | 1160 | /* Disable the clocks */ |
1159 | clk_disable(host->fclk); | 1161 | pm_runtime_put_sync(host->dev); |
1160 | clk_disable(host->iclk); | ||
1161 | if (host->got_dbclk) | 1162 | if (host->got_dbclk) |
1162 | clk_disable(host->dbclk); | 1163 | clk_disable(host->dbclk); |
1163 | 1164 | ||
@@ -1168,8 +1169,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) | |||
1168 | if (!ret) | 1169 | if (!ret) |
1169 | ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, | 1170 | ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, |
1170 | vdd); | 1171 | vdd); |
1171 | clk_enable(host->iclk); | 1172 | pm_runtime_get_sync(host->dev); |
1172 | clk_enable(host->fclk); | ||
1173 | if (host->got_dbclk) | 1173 | if (host->got_dbclk) |
1174 | clk_enable(host->dbclk); | 1174 | clk_enable(host->dbclk); |
1175 | 1175 | ||
@@ -1605,7 +1605,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1605 | u32 con; | 1605 | u32 con; |
1606 | int do_send_init_stream = 0; | 1606 | int do_send_init_stream = 0; |
1607 | 1607 | ||
1608 | mmc_host_enable(host->mmc); | 1608 | pm_runtime_get_sync(host->dev); |
1609 | 1609 | ||
1610 | if (ios->power_mode != host->power_mode) { | 1610 | if (ios->power_mode != host->power_mode) { |
1611 | switch (ios->power_mode) { | 1611 | switch (ios->power_mode) { |
@@ -1700,8 +1700,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1700 | else | 1700 | else |
1701 | OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); | 1701 | OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); |
1702 | 1702 | ||
1703 | if (host->power_mode == MMC_POWER_OFF) | 1703 | pm_runtime_put_autosuspend(host->dev); |
1704 | mmc_host_disable(host->mmc); | ||
1705 | } | 1704 | } |
1706 | 1705 | ||
1707 | static int omap_hsmmc_get_cd(struct mmc_host *mmc) | 1706 | static int omap_hsmmc_get_cd(struct mmc_host *mmc) |
@@ -1760,13 +1759,9 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) | |||
1760 | static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) | 1759 | static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) |
1761 | { | 1760 | { |
1762 | struct omap_hsmmc_host *host = mmc_priv(mmc); | 1761 | struct omap_hsmmc_host *host = mmc_priv(mmc); |
1763 | int err; | ||
1764 | 1762 | ||
1765 | err = clk_enable(host->fclk); | 1763 | pm_runtime_get_sync(host->dev); |
1766 | if (err) | 1764 | |
1767 | return err; | ||
1768 | dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); | ||
1769 | omap_hsmmc_context_restore(host); | ||
1770 | return 0; | 1765 | return 0; |
1771 | } | 1766 | } |
1772 | 1767 | ||
@@ -1774,9 +1769,9 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy) | |||
1774 | { | 1769 | { |
1775 | struct omap_hsmmc_host *host = mmc_priv(mmc); | 1770 | struct omap_hsmmc_host *host = mmc_priv(mmc); |
1776 | 1771 | ||
1777 | omap_hsmmc_context_save(host); | 1772 | pm_runtime_mark_last_busy(host->dev); |
1778 | clk_disable(host->fclk); | 1773 | pm_runtime_put_autosuspend(host->dev); |
1779 | dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n"); | 1774 | |
1780 | return 0; | 1775 | return 0; |
1781 | } | 1776 | } |
1782 | 1777 | ||
@@ -1819,10 +1814,7 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) | |||
1819 | return 0; | 1814 | return 0; |
1820 | } | 1815 | } |
1821 | 1816 | ||
1822 | if (clk_enable(host->fclk) != 0) { | 1817 | pm_runtime_get_sync(host->dev); |
1823 | seq_printf(s, "can't read the regs\n"); | ||
1824 | return 0; | ||
1825 | } | ||
1826 | 1818 | ||
1827 | seq_printf(s, "SYSCONFIG:\t0x%08x\n", | 1819 | seq_printf(s, "SYSCONFIG:\t0x%08x\n", |
1828 | OMAP_HSMMC_READ(host->base, SYSCONFIG)); | 1820 | OMAP_HSMMC_READ(host->base, SYSCONFIG)); |
@@ -1839,7 +1831,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) | |||
1839 | seq_printf(s, "CAPA:\t\t0x%08x\n", | 1831 | seq_printf(s, "CAPA:\t\t0x%08x\n", |
1840 | OMAP_HSMMC_READ(host->base, CAPA)); | 1832 | OMAP_HSMMC_READ(host->base, CAPA)); |
1841 | 1833 | ||
1842 | clk_disable(host->fclk); | 1834 | pm_runtime_mark_last_busy(host->dev); |
1835 | pm_runtime_put_autosuspend(host->dev); | ||
1843 | 1836 | ||
1844 | return 0; | 1837 | return 0; |
1845 | } | 1838 | } |
@@ -1960,18 +1953,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
1960 | 1953 | ||
1961 | mmc->caps |= MMC_CAP_DISABLE; | 1954 | mmc->caps |= MMC_CAP_DISABLE; |
1962 | 1955 | ||
1963 | if (clk_enable(host->iclk) != 0) { | 1956 | pm_runtime_enable(host->dev); |
1964 | clk_put(host->iclk); | 1957 | pm_runtime_get_sync(host->dev); |
1965 | clk_put(host->fclk); | 1958 | pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY); |
1966 | goto err1; | 1959 | pm_runtime_use_autosuspend(host->dev); |
1967 | } | ||
1968 | |||
1969 | if (mmc_host_enable(host->mmc) != 0) { | ||
1970 | clk_disable(host->iclk); | ||
1971 | clk_put(host->iclk); | ||
1972 | clk_put(host->fclk); | ||
1973 | goto err1; | ||
1974 | } | ||
1975 | 1960 | ||
1976 | if (cpu_is_omap2430()) { | 1961 | if (cpu_is_omap2430()) { |
1977 | host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); | 1962 | host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); |
@@ -2098,6 +2083,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
2098 | } | 2083 | } |
2099 | 2084 | ||
2100 | omap_hsmmc_debugfs(mmc); | 2085 | omap_hsmmc_debugfs(mmc); |
2086 | pm_runtime_mark_last_busy(host->dev); | ||
2087 | pm_runtime_put_autosuspend(host->dev); | ||
2101 | 2088 | ||
2102 | return 0; | 2089 | return 0; |
2103 | 2090 | ||
@@ -2113,8 +2100,8 @@ err_reg: | |||
2113 | err_irq_cd_init: | 2100 | err_irq_cd_init: |
2114 | free_irq(host->irq, host); | 2101 | free_irq(host->irq, host); |
2115 | err_irq: | 2102 | err_irq: |
2116 | mmc_host_disable(host->mmc); | 2103 | pm_runtime_mark_last_busy(host->dev); |
2117 | clk_disable(host->iclk); | 2104 | pm_runtime_put_autosuspend(host->dev); |
2118 | clk_put(host->fclk); | 2105 | clk_put(host->fclk); |
2119 | clk_put(host->iclk); | 2106 | clk_put(host->iclk); |
2120 | if (host->got_dbclk) { | 2107 | if (host->got_dbclk) { |
@@ -2138,7 +2125,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) | |||
2138 | struct resource *res; | 2125 | struct resource *res; |
2139 | 2126 | ||
2140 | if (host) { | 2127 | if (host) { |
2141 | mmc_host_enable(host->mmc); | 2128 | pm_runtime_get_sync(host->dev); |
2142 | mmc_remove_host(host->mmc); | 2129 | mmc_remove_host(host->mmc); |
2143 | if (host->use_reg) | 2130 | if (host->use_reg) |
2144 | omap_hsmmc_reg_put(host); | 2131 | omap_hsmmc_reg_put(host); |
@@ -2149,8 +2136,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev) | |||
2149 | free_irq(mmc_slot(host).card_detect_irq, host); | 2136 | free_irq(mmc_slot(host).card_detect_irq, host); |
2150 | flush_work_sync(&host->mmc_carddetect_work); | 2137 | flush_work_sync(&host->mmc_carddetect_work); |
2151 | 2138 | ||
2152 | mmc_host_disable(host->mmc); | 2139 | pm_runtime_put_sync(host->dev); |
2153 | clk_disable(host->iclk); | 2140 | pm_runtime_disable(host->dev); |
2154 | clk_put(host->fclk); | 2141 | clk_put(host->fclk); |
2155 | clk_put(host->iclk); | 2142 | clk_put(host->iclk); |
2156 | if (host->got_dbclk) { | 2143 | if (host->got_dbclk) { |
@@ -2182,6 +2169,7 @@ static int omap_hsmmc_suspend(struct device *dev) | |||
2182 | return 0; | 2169 | return 0; |
2183 | 2170 | ||
2184 | if (host) { | 2171 | if (host) { |
2172 | pm_runtime_get_sync(host->dev); | ||
2185 | host->suspended = 1; | 2173 | host->suspended = 1; |
2186 | if (host->pdata->suspend) { | 2174 | if (host->pdata->suspend) { |
2187 | ret = host->pdata->suspend(&pdev->dev, | 2175 | ret = host->pdata->suspend(&pdev->dev, |
@@ -2196,13 +2184,11 @@ static int omap_hsmmc_suspend(struct device *dev) | |||
2196 | } | 2184 | } |
2197 | cancel_work_sync(&host->mmc_carddetect_work); | 2185 | cancel_work_sync(&host->mmc_carddetect_work); |
2198 | ret = mmc_suspend_host(host->mmc); | 2186 | ret = mmc_suspend_host(host->mmc); |
2199 | mmc_host_enable(host->mmc); | 2187 | |
2200 | if (ret == 0) { | 2188 | if (ret == 0) { |
2201 | omap_hsmmc_disable_irq(host); | 2189 | omap_hsmmc_disable_irq(host); |
2202 | OMAP_HSMMC_WRITE(host->base, HCTL, | 2190 | OMAP_HSMMC_WRITE(host->base, HCTL, |
2203 | OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); | 2191 | OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); |
2204 | mmc_host_disable(host->mmc); | ||
2205 | clk_disable(host->iclk); | ||
2206 | if (host->got_dbclk) | 2192 | if (host->got_dbclk) |
2207 | clk_disable(host->dbclk); | 2193 | clk_disable(host->dbclk); |
2208 | } else { | 2194 | } else { |
@@ -2214,9 +2200,8 @@ static int omap_hsmmc_suspend(struct device *dev) | |||
2214 | dev_dbg(mmc_dev(host->mmc), | 2200 | dev_dbg(mmc_dev(host->mmc), |
2215 | "Unmask interrupt failed\n"); | 2201 | "Unmask interrupt failed\n"); |
2216 | } | 2202 | } |
2217 | mmc_host_disable(host->mmc); | ||
2218 | } | 2203 | } |
2219 | 2204 | pm_runtime_put_sync(host->dev); | |
2220 | } | 2205 | } |
2221 | return ret; | 2206 | return ret; |
2222 | } | 2207 | } |
@@ -2232,14 +2217,7 @@ static int omap_hsmmc_resume(struct device *dev) | |||
2232 | return 0; | 2217 | return 0; |
2233 | 2218 | ||
2234 | if (host) { | 2219 | if (host) { |
2235 | ret = clk_enable(host->iclk); | 2220 | pm_runtime_get_sync(host->dev); |
2236 | if (ret) | ||
2237 | goto clk_en_err; | ||
2238 | |||
2239 | if (mmc_host_enable(host->mmc) != 0) { | ||
2240 | clk_disable(host->iclk); | ||
2241 | goto clk_en_err; | ||
2242 | } | ||
2243 | 2221 | ||
2244 | if (host->got_dbclk) | 2222 | if (host->got_dbclk) |
2245 | clk_enable(host->dbclk); | 2223 | clk_enable(host->dbclk); |
@@ -2259,14 +2237,13 @@ static int omap_hsmmc_resume(struct device *dev) | |||
2259 | ret = mmc_resume_host(host->mmc); | 2237 | ret = mmc_resume_host(host->mmc); |
2260 | if (ret == 0) | 2238 | if (ret == 0) |
2261 | host->suspended = 0; | 2239 | host->suspended = 0; |
2240 | |||
2241 | pm_runtime_mark_last_busy(host->dev); | ||
2242 | pm_runtime_put_autosuspend(host->dev); | ||
2262 | } | 2243 | } |
2263 | 2244 | ||
2264 | return ret; | 2245 | return ret; |
2265 | 2246 | ||
2266 | clk_en_err: | ||
2267 | dev_dbg(mmc_dev(host->mmc), | ||
2268 | "Failed to enable MMC clocks during resume\n"); | ||
2269 | return ret; | ||
2270 | } | 2247 | } |
2271 | 2248 | ||
2272 | #else | 2249 | #else |
@@ -2274,9 +2251,33 @@ clk_en_err: | |||
2274 | #define omap_hsmmc_resume NULL | 2251 | #define omap_hsmmc_resume NULL |
2275 | #endif | 2252 | #endif |
2276 | 2253 | ||
2254 | static int omap_hsmmc_runtime_suspend(struct device *dev) | ||
2255 | { | ||
2256 | struct omap_hsmmc_host *host; | ||
2257 | |||
2258 | host = platform_get_drvdata(to_platform_device(dev)); | ||
2259 | omap_hsmmc_context_save(host); | ||
2260 | dev_dbg(mmc_dev(host->mmc), "disabled\n"); | ||
2261 | |||
2262 | return 0; | ||
2263 | } | ||
2264 | |||
2265 | static int omap_hsmmc_runtime_resume(struct device *dev) | ||
2266 | { | ||
2267 | struct omap_hsmmc_host *host; | ||
2268 | |||
2269 | host = platform_get_drvdata(to_platform_device(dev)); | ||
2270 | omap_hsmmc_context_restore(host); | ||
2271 | dev_dbg(mmc_dev(host->mmc), "enabled\n"); | ||
2272 | |||
2273 | return 0; | ||
2274 | } | ||
2275 | |||
2277 | static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { | 2276 | static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { |
2278 | .suspend = omap_hsmmc_suspend, | 2277 | .suspend = omap_hsmmc_suspend, |
2279 | .resume = omap_hsmmc_resume, | 2278 | .resume = omap_hsmmc_resume, |
2279 | .runtime_suspend = omap_hsmmc_runtime_suspend, | ||
2280 | .runtime_resume = omap_hsmmc_runtime_resume, | ||
2280 | }; | 2281 | }; |
2281 | 2282 | ||
2282 | static struct platform_driver omap_hsmmc_driver = { | 2283 | static struct platform_driver omap_hsmmc_driver = { |