aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2011-05-05 12:20:48 -0400
committerChris Ball <cjb@laptop.org>2011-05-24 23:53:51 -0400
commitfaca6648e6f9659400bdafb985b50ba41f1a23b5 (patch)
tree741cc5ac765e8ce564d4ef1d4b04ca725d4cd5aa
parente6ee7182c3b22afe0b983eac89dc020a93a13179 (diff)
mmc: add runtime and system power-management support to the MMCIF driver
Adding support for runtime power-management to the MMCIF driver allows it to save power as long as no card is present. To also allow to turn off the power domain at that time, we release DMA channels during that time, since on some sh-mobile systems the DMA controller(s) and the MMCIF block belong to the same power domain. System-wide power management has been tested with experimental PM patches on AP4-based systems. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r--drivers/mmc/host/sh_mmcif.c78
1 files changed, 69 insertions, 9 deletions
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index d3871b67f77b..14f8edbaa195 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -29,6 +29,7 @@
29#include <linux/mmc/sh_mmcif.h> 29#include <linux/mmc/sh_mmcif.h>
30#include <linux/pagemap.h> 30#include <linux/pagemap.h>
31#include <linux/platform_device.h> 31#include <linux/platform_device.h>
32#include <linux/pm_runtime.h>
32#include <linux/spinlock.h> 33#include <linux/spinlock.h>
33 34
34#define DRIVER_NAME "sh_mmcif" 35#define DRIVER_NAME "sh_mmcif"
@@ -173,6 +174,7 @@ struct sh_mmcif_host {
173 struct completion intr_wait; 174 struct completion intr_wait;
174 enum mmcif_state state; 175 enum mmcif_state state;
175 spinlock_t lock; 176 spinlock_t lock;
177 bool power;
176 178
177 /* DMA support */ 179 /* DMA support */
178 struct dma_chan *chan_rx; 180 struct dma_chan *chan_rx;
@@ -877,11 +879,24 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
877 if (ios->power_mode == MMC_POWER_UP) { 879 if (ios->power_mode == MMC_POWER_UP) {
878 if (p->set_pwr) 880 if (p->set_pwr)
879 p->set_pwr(host->pd, ios->power_mode); 881 p->set_pwr(host->pd, ios->power_mode);
882 if (!host->power) {
883 /* See if we also get DMA */
884 sh_mmcif_request_dma(host, host->pd->dev.platform_data);
885 pm_runtime_get_sync(&host->pd->dev);
886 host->power = true;
887 }
880 } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) { 888 } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
881 /* clock stop */ 889 /* clock stop */
882 sh_mmcif_clock_control(host, 0); 890 sh_mmcif_clock_control(host, 0);
883 if (ios->power_mode == MMC_POWER_OFF && p->down_pwr) 891 if (ios->power_mode == MMC_POWER_OFF) {
884 p->down_pwr(host->pd); 892 if (host->power) {
893 pm_runtime_put(&host->pd->dev);
894 sh_mmcif_release_dma(host);
895 host->power = false;
896 }
897 if (p->down_pwr)
898 p->down_pwr(host->pd);
899 }
885 host->state = STATE_IDLE; 900 host->state = STATE_IDLE;
886 return; 901 return;
887 } 902 }
@@ -957,7 +972,7 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
957 sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); 972 sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
958 err = 1; 973 err = 1;
959 } else { 974 } else {
960 dev_dbg(&host->pd->dev, "Not support int\n"); 975 dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state);
961 sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); 976 sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
962 sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); 977 sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
963 err = 1; 978 err = 1;
@@ -1053,8 +1068,12 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
1053 sh_mmcif_sync_reset(host); 1068 sh_mmcif_sync_reset(host);
1054 platform_set_drvdata(pdev, host); 1069 platform_set_drvdata(pdev, host);
1055 1070
1056 /* See if we also get DMA */ 1071 pm_runtime_enable(&pdev->dev);
1057 sh_mmcif_request_dma(host, pd); 1072 host->power = false;
1073
1074 ret = pm_runtime_resume(&pdev->dev);
1075 if (ret < 0)
1076 goto clean_up2;
1058 1077
1059 mmc_add_host(mmc); 1078 mmc_add_host(mmc);
1060 1079
@@ -1063,13 +1082,13 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
1063 ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); 1082 ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
1064 if (ret) { 1083 if (ret) {
1065 dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); 1084 dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
1066 goto clean_up2; 1085 goto clean_up3;
1067 } 1086 }
1068 ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host); 1087 ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host);
1069 if (ret) { 1088 if (ret) {
1070 free_irq(irq[0], host); 1089 free_irq(irq[0], host);
1071 dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); 1090 dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
1072 goto clean_up2; 1091 goto clean_up3;
1073 } 1092 }
1074 1093
1075 sh_mmcif_detect(host->mmc); 1094 sh_mmcif_detect(host->mmc);
@@ -1079,7 +1098,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
1079 sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); 1098 sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
1080 return ret; 1099 return ret;
1081 1100
1101clean_up3:
1102 mmc_remove_host(mmc);
1103 pm_runtime_suspend(&pdev->dev);
1082clean_up2: 1104clean_up2:
1105 pm_runtime_disable(&pdev->dev);
1083 clk_disable(host->hclk); 1106 clk_disable(host->hclk);
1084clean_up1: 1107clean_up1:
1085 mmc_free_host(mmc); 1108 mmc_free_host(mmc);
@@ -1094,9 +1117,9 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
1094 struct sh_mmcif_host *host = platform_get_drvdata(pdev); 1117 struct sh_mmcif_host *host = platform_get_drvdata(pdev);
1095 int irq[2]; 1118 int irq[2];
1096 1119
1097 mmc_remove_host(host->mmc); 1120 pm_runtime_get_sync(&pdev->dev);
1098 sh_mmcif_release_dma(host);
1099 1121
1122 mmc_remove_host(host->mmc);
1100 sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); 1123 sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
1101 1124
1102 if (host->addr) 1125 if (host->addr)
@@ -1112,15 +1135,52 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
1112 1135
1113 clk_disable(host->hclk); 1136 clk_disable(host->hclk);
1114 mmc_free_host(host->mmc); 1137 mmc_free_host(host->mmc);
1138 pm_runtime_put_sync(&pdev->dev);
1139 pm_runtime_disable(&pdev->dev);
1115 1140
1116 return 0; 1141 return 0;
1117} 1142}
1118 1143
1144#ifdef CONFIG_PM
1145static int sh_mmcif_suspend(struct device *dev)
1146{
1147 struct platform_device *pdev = to_platform_device(dev);
1148 struct sh_mmcif_host *host = platform_get_drvdata(pdev);
1149 int ret = mmc_suspend_host(host->mmc);
1150
1151 if (!ret) {
1152 sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
1153 clk_disable(host->hclk);
1154 }
1155
1156 return ret;
1157}
1158
1159static int sh_mmcif_resume(struct device *dev)
1160{
1161 struct platform_device *pdev = to_platform_device(dev);
1162 struct sh_mmcif_host *host = platform_get_drvdata(pdev);
1163
1164 clk_enable(host->hclk);
1165
1166 return mmc_resume_host(host->mmc);
1167}
1168#else
1169#define sh_mmcif_suspend NULL
1170#define sh_mmcif_resume NULL
1171#endif /* CONFIG_PM */
1172
1173static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
1174 .suspend = sh_mmcif_suspend,
1175 .resume = sh_mmcif_resume,
1176};
1177
1119static struct platform_driver sh_mmcif_driver = { 1178static struct platform_driver sh_mmcif_driver = {
1120 .probe = sh_mmcif_probe, 1179 .probe = sh_mmcif_probe,
1121 .remove = sh_mmcif_remove, 1180 .remove = sh_mmcif_remove,
1122 .driver = { 1181 .driver = {
1123 .name = DRIVER_NAME, 1182 .name = DRIVER_NAME,
1183 .pm = &sh_mmcif_dev_pm_ops,
1124 }, 1184 },
1125}; 1185};
1126 1186