diff options
author | Chaotian Jing <chaotian.jing@mediatek.com> | 2015-06-15 07:20:49 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2015-06-18 03:21:04 -0400 |
commit | 4b8a43e90ccf88c91475b802d1388c0779be2bda (patch) | |
tree | 14769199867583dde3bfd25d6b290903a773a222 | |
parent | 208489032bdd8d4a7de50f3057c175058f271956 (diff) |
mmc: mediatek: Add PM support for MMC driver
Add PM support for Mediatek MMC driver
Save/restore registers when PM
Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r-- | drivers/mmc/host/mtk-sd.c | 89 |
1 files changed, 86 insertions, 3 deletions
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index fd965cb81133..7153500dd007 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/of_gpio.h> | 23 | #include <linux/of_gpio.h> |
24 | #include <linux/pinctrl/consumer.h> | 24 | #include <linux/pinctrl/consumer.h> |
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/pm.h> | ||
27 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/regulator/consumer.h> | 28 | #include <linux/regulator/consumer.h> |
27 | #include <linux/spinlock.h> | 29 | #include <linux/spinlock.h> |
28 | 30 | ||
@@ -213,6 +215,7 @@ | |||
213 | #define MSDC_ASYNC_FLAG (0x1 << 1) | 215 | #define MSDC_ASYNC_FLAG (0x1 << 1) |
214 | #define MSDC_MMAP_FLAG (0x1 << 2) | 216 | #define MSDC_MMAP_FLAG (0x1 << 2) |
215 | 217 | ||
218 | #define MTK_MMC_AUTOSUSPEND_DELAY 50 | ||
216 | #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */ | 219 | #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */ |
217 | #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */ | 220 | #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */ |
218 | 221 | ||
@@ -255,6 +258,15 @@ struct msdc_dma { | |||
255 | dma_addr_t bd_addr; /* the physical address of bd array */ | 258 | dma_addr_t bd_addr; /* the physical address of bd array */ |
256 | }; | 259 | }; |
257 | 260 | ||
261 | struct msdc_save_para { | ||
262 | u32 msdc_cfg; | ||
263 | u32 iocon; | ||
264 | u32 sdc_cfg; | ||
265 | u32 pad_tune; | ||
266 | u32 patch_bit0; | ||
267 | u32 patch_bit1; | ||
268 | }; | ||
269 | |||
258 | struct msdc_host { | 270 | struct msdc_host { |
259 | struct device *dev; | 271 | struct device *dev; |
260 | struct mmc_host *mmc; /* mmc structure */ | 272 | struct mmc_host *mmc; /* mmc structure */ |
@@ -287,6 +299,7 @@ struct msdc_host { | |||
287 | u32 sclk; /* SD/MS bus clock frequency */ | 299 | u32 sclk; /* SD/MS bus clock frequency */ |
288 | bool ddr; | 300 | bool ddr; |
289 | bool vqmmc_enabled; | 301 | bool vqmmc_enabled; |
302 | struct msdc_save_para save_para; /* used when gate HCLK */ | ||
290 | }; | 303 | }; |
291 | 304 | ||
292 | static void sdr_set_bits(void __iomem *reg, u32 bs) | 305 | static void sdr_set_bits(void __iomem *reg, u32 bs) |
@@ -678,6 +691,9 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) | |||
678 | if (mrq->data) | 691 | if (mrq->data) |
679 | msdc_unprepare_data(host, mrq); | 692 | msdc_unprepare_data(host, mrq); |
680 | mmc_request_done(host->mmc, mrq); | 693 | mmc_request_done(host->mmc, mrq); |
694 | |||
695 | pm_runtime_mark_last_busy(host->dev); | ||
696 | pm_runtime_put_autosuspend(host->dev); | ||
681 | } | 697 | } |
682 | 698 | ||
683 | /* returns true if command is fully handled; returns false otherwise */ | 699 | /* returns true if command is fully handled; returns false otherwise */ |
@@ -832,6 +848,8 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
832 | WARN_ON(host->mrq); | 848 | WARN_ON(host->mrq); |
833 | host->mrq = mrq; | 849 | host->mrq = mrq; |
834 | 850 | ||
851 | pm_runtime_get_sync(host->dev); | ||
852 | |||
835 | if (mrq->data) | 853 | if (mrq->data) |
836 | msdc_prepare_data(host, mrq); | 854 | msdc_prepare_data(host, mrq); |
837 | 855 | ||
@@ -1146,6 +1164,8 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1146 | int ret; | 1164 | int ret; |
1147 | u32 ddr = 0; | 1165 | u32 ddr = 0; |
1148 | 1166 | ||
1167 | pm_runtime_get_sync(host->dev); | ||
1168 | |||
1149 | if (ios->timing == MMC_TIMING_UHS_DDR50 || | 1169 | if (ios->timing == MMC_TIMING_UHS_DDR50 || |
1150 | ios->timing == MMC_TIMING_MMC_DDR52) | 1170 | ios->timing == MMC_TIMING_MMC_DDR52) |
1151 | ddr = 1; | 1171 | ddr = 1; |
@@ -1160,7 +1180,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1160 | ios->vdd); | 1180 | ios->vdd); |
1161 | if (ret) { | 1181 | if (ret) { |
1162 | dev_err(host->dev, "Failed to set vmmc power!\n"); | 1182 | dev_err(host->dev, "Failed to set vmmc power!\n"); |
1163 | return; | 1183 | goto end; |
1164 | } | 1184 | } |
1165 | } | 1185 | } |
1166 | break; | 1186 | break; |
@@ -1188,6 +1208,10 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1188 | 1208 | ||
1189 | if (host->mclk != ios->clock || host->ddr != ddr) | 1209 | if (host->mclk != ios->clock || host->ddr != ddr) |
1190 | msdc_set_mclk(host, ddr, ios->clock); | 1210 | msdc_set_mclk(host, ddr, ios->clock); |
1211 | |||
1212 | end: | ||
1213 | pm_runtime_mark_last_busy(host->dev); | ||
1214 | pm_runtime_put_autosuspend(host->dev); | ||
1191 | } | 1215 | } |
1192 | 1216 | ||
1193 | static struct mmc_host_ops mt_msdc_ops = { | 1217 | static struct mmc_host_ops mt_msdc_ops = { |
@@ -1311,12 +1335,18 @@ static int msdc_drv_probe(struct platform_device *pdev) | |||
1311 | if (ret) | 1335 | if (ret) |
1312 | goto release; | 1336 | goto release; |
1313 | 1337 | ||
1338 | pm_runtime_set_active(host->dev); | ||
1339 | pm_runtime_set_autosuspend_delay(host->dev, MTK_MMC_AUTOSUSPEND_DELAY); | ||
1340 | pm_runtime_use_autosuspend(host->dev); | ||
1341 | pm_runtime_enable(host->dev); | ||
1314 | ret = mmc_add_host(mmc); | 1342 | ret = mmc_add_host(mmc); |
1343 | |||
1315 | if (ret) | 1344 | if (ret) |
1316 | goto release; | 1345 | goto end; |
1317 | 1346 | ||
1318 | return 0; | 1347 | return 0; |
1319 | 1348 | end: | |
1349 | pm_runtime_disable(host->dev); | ||
1320 | release: | 1350 | release: |
1321 | platform_set_drvdata(pdev, NULL); | 1351 | platform_set_drvdata(pdev, NULL); |
1322 | msdc_deinit_hw(host); | 1352 | msdc_deinit_hw(host); |
@@ -1344,11 +1374,15 @@ static int msdc_drv_remove(struct platform_device *pdev) | |||
1344 | mmc = platform_get_drvdata(pdev); | 1374 | mmc = platform_get_drvdata(pdev); |
1345 | host = mmc_priv(mmc); | 1375 | host = mmc_priv(mmc); |
1346 | 1376 | ||
1377 | pm_runtime_get_sync(host->dev); | ||
1378 | |||
1347 | platform_set_drvdata(pdev, NULL); | 1379 | platform_set_drvdata(pdev, NULL); |
1348 | mmc_remove_host(host->mmc); | 1380 | mmc_remove_host(host->mmc); |
1349 | msdc_deinit_hw(host); | 1381 | msdc_deinit_hw(host); |
1350 | msdc_gate_clock(host); | 1382 | msdc_gate_clock(host); |
1351 | 1383 | ||
1384 | pm_runtime_disable(host->dev); | ||
1385 | pm_runtime_put_noidle(host->dev); | ||
1352 | dma_free_coherent(&pdev->dev, | 1386 | dma_free_coherent(&pdev->dev, |
1353 | sizeof(struct mt_gpdma_desc), | 1387 | sizeof(struct mt_gpdma_desc), |
1354 | host->dma.gpd, host->dma.gpd_addr); | 1388 | host->dma.gpd, host->dma.gpd_addr); |
@@ -1360,6 +1394,54 @@ static int msdc_drv_remove(struct platform_device *pdev) | |||
1360 | return 0; | 1394 | return 0; |
1361 | } | 1395 | } |
1362 | 1396 | ||
1397 | #ifdef CONFIG_PM | ||
1398 | static void msdc_save_reg(struct msdc_host *host) | ||
1399 | { | ||
1400 | host->save_para.msdc_cfg = readl(host->base + MSDC_CFG); | ||
1401 | host->save_para.iocon = readl(host->base + MSDC_IOCON); | ||
1402 | host->save_para.sdc_cfg = readl(host->base + SDC_CFG); | ||
1403 | host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE); | ||
1404 | host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT); | ||
1405 | host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1); | ||
1406 | } | ||
1407 | |||
1408 | static void msdc_restore_reg(struct msdc_host *host) | ||
1409 | { | ||
1410 | writel(host->save_para.msdc_cfg, host->base + MSDC_CFG); | ||
1411 | writel(host->save_para.iocon, host->base + MSDC_IOCON); | ||
1412 | writel(host->save_para.sdc_cfg, host->base + SDC_CFG); | ||
1413 | writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE); | ||
1414 | writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT); | ||
1415 | writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1); | ||
1416 | } | ||
1417 | |||
1418 | static int msdc_runtime_suspend(struct device *dev) | ||
1419 | { | ||
1420 | struct mmc_host *mmc = dev_get_drvdata(dev); | ||
1421 | struct msdc_host *host = mmc_priv(mmc); | ||
1422 | |||
1423 | msdc_save_reg(host); | ||
1424 | msdc_gate_clock(host); | ||
1425 | return 0; | ||
1426 | } | ||
1427 | |||
1428 | static int msdc_runtime_resume(struct device *dev) | ||
1429 | { | ||
1430 | struct mmc_host *mmc = dev_get_drvdata(dev); | ||
1431 | struct msdc_host *host = mmc_priv(mmc); | ||
1432 | |||
1433 | msdc_ungate_clock(host); | ||
1434 | msdc_restore_reg(host); | ||
1435 | return 0; | ||
1436 | } | ||
1437 | #endif | ||
1438 | |||
1439 | static const struct dev_pm_ops msdc_dev_pm_ops = { | ||
1440 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | ||
1441 | pm_runtime_force_resume) | ||
1442 | SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL) | ||
1443 | }; | ||
1444 | |||
1363 | static const struct of_device_id msdc_of_ids[] = { | 1445 | static const struct of_device_id msdc_of_ids[] = { |
1364 | { .compatible = "mediatek,mt8135-mmc", }, | 1446 | { .compatible = "mediatek,mt8135-mmc", }, |
1365 | {} | 1447 | {} |
@@ -1371,6 +1453,7 @@ static struct platform_driver mt_msdc_driver = { | |||
1371 | .driver = { | 1453 | .driver = { |
1372 | .name = "mtk-msdc", | 1454 | .name = "mtk-msdc", |
1373 | .of_match_table = msdc_of_ids, | 1455 | .of_match_table = msdc_of_ids, |
1456 | .pm = &msdc_dev_pm_ops, | ||
1374 | }, | 1457 | }, |
1375 | }; | 1458 | }; |
1376 | 1459 | ||