aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorJarkko Lavinen <jarkko.lavinen@nokia.com>2009-09-22 19:44:53 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:35 -0400
commit13189e78caa824f0285b1823519c020591641207 (patch)
tree772d20a61b63540ae2154d2e3bfb9af55147527b /drivers/mmc/host
parent623821f71bb40f9972630eb1f779817d462ef0ee (diff)
omap_hsmmc: add mmc card sleep and awake support
After 1 second of inactivity, put card and/or regulator to sleep. After 8 seconds of inactivity, turn off the power. Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com> Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com> Acked-by: Matt Fleming <matt@console-pimps.org> Cc: Ian Molton <ian@mnementh.co.uk> Cc: "Roberto A. Foglietta" <roberto.foglietta@gmail.com> Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com> Cc: Denis Karpov <ext-denis.2.karpov@nokia.com> Cc: Pierre Ossman <pierre@ossman.eu> Cc: Philip Langdale <philipl@overt.org> Cc: "Madhusudhan" <madhu.cr@ti.com> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/omap_hsmmc.c162
1 files changed, 88 insertions, 74 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c82ec7f4e1a7..b83f7a4d8e28 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -27,6 +27,7 @@
27#include <linux/timer.h> 27#include <linux/timer.h>
28#include <linux/clk.h> 28#include <linux/clk.h>
29#include <linux/mmc/host.h> 29#include <linux/mmc/host.h>
30#include <linux/mmc/core.h>
30#include <linux/io.h> 31#include <linux/io.h>
31#include <linux/semaphore.h> 32#include <linux/semaphore.h>
32#include <mach/dma.h> 33#include <mach/dma.h>
@@ -115,7 +116,8 @@
115 116
116/* Timeouts for entering power saving states on inactivity, msec */ 117/* Timeouts for entering power saving states on inactivity, msec */
117#define OMAP_MMC_DISABLED_TIMEOUT 100 118#define OMAP_MMC_DISABLED_TIMEOUT 100
118#define OMAP_MMC_OFF_TIMEOUT 1000 119#define OMAP_MMC_SLEEP_TIMEOUT 1000
120#define OMAP_MMC_OFF_TIMEOUT 8000
119 121
120/* 122/*
121 * One controller can have multiple slots, like on some omap boards using 123 * One controller can have multiple slots, like on some omap boards using
@@ -1182,20 +1184,21 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
1182 1184
1183/* 1185/*
1184 * Dynamic power saving handling, FSM: 1186 * Dynamic power saving handling, FSM:
1185 * ENABLED -> DISABLED -> OFF / REGSLEEP 1187 * ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
1186 * ^___________| | 1188 * ^___________| | |
1187 * |______________________| 1189 * |______________________|______________________|
1188 * 1190 *
1189 * ENABLED: mmc host is fully functional 1191 * ENABLED: mmc host is fully functional
1190 * DISABLED: fclk is off 1192 * DISABLED: fclk is off
1191 * OFF: fclk is off,voltage regulator is off 1193 * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep
1192 * REGSLEEP: fclk is off,voltage regulator is asleep 1194 * REGSLEEP: fclk is off, voltage regulator is asleep
1195 * OFF: fclk is off, voltage regulator is off
1193 * 1196 *
1194 * Transition handlers return the timeout for the next state transition 1197 * Transition handlers return the timeout for the next state transition
1195 * or negative error. 1198 * or negative error.
1196 */ 1199 */
1197 1200
1198enum {ENABLED = 0, DISABLED, REGSLEEP, OFF}; 1201enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};
1199 1202
1200/* Handler for [ENABLED -> DISABLED] transition */ 1203/* Handler for [ENABLED -> DISABLED] transition */
1201static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host) 1204static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
@@ -1209,46 +1212,72 @@ static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
1209 if (host->power_mode == MMC_POWER_OFF) 1212 if (host->power_mode == MMC_POWER_OFF)
1210 return 0; 1213 return 0;
1211 1214
1212 return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT); 1215 return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT);
1213} 1216}
1214 1217
1215/* Handler for [DISABLED -> OFF] transition */ 1218/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
1216static int omap_mmc_disabled_to_off(struct mmc_omap_host *host) 1219static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
1217{ 1220{
1218 int new_state; 1221 int err, new_state;
1219
1220 dev_dbg(mmc_dev(host->mmc), "DISABLED -> OFF\n");
1221 1222
1222 if (!mmc_try_claim_host(host->mmc)) 1223 if (!mmc_try_claim_host(host->mmc))
1223 return 0; 1224 return 0;
1224 1225
1225 clk_enable(host->fclk); 1226 clk_enable(host->fclk);
1226
1227 omap_mmc_restore_ctx(host); 1227 omap_mmc_restore_ctx(host);
1228 if (mmc_card_can_sleep(host->mmc)) {
1229 err = mmc_card_sleep(host->mmc);
1230 if (err < 0) {
1231 clk_disable(host->fclk);
1232 mmc_release_host(host->mmc);
1233 return err;
1234 }
1235 new_state = CARDSLEEP;
1236 } else
1237 new_state = REGSLEEP;
1238 if (mmc_slot(host).set_sleep)
1239 mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
1240 new_state == CARDSLEEP);
1241 /* FIXME: turn off bus power and perhaps interrupts too */
1242 clk_disable(host->fclk);
1243 host->dpm_state = new_state;
1244
1245 mmc_release_host(host->mmc);
1246
1247 dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n",
1248 host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
1228 1249
1229 if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || 1250 if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
1230 mmc_slot(host).card_detect || 1251 mmc_slot(host).card_detect ||
1231 (mmc_slot(host).get_cover_state && 1252 (mmc_slot(host).get_cover_state &&
1232 mmc_slot(host).get_cover_state(host->dev, host->slot_id))) { 1253 mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
1233 mmc_power_save_host(host->mmc); 1254 return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
1234 new_state = OFF; 1255
1235 } else { 1256 return 0;
1236 if (mmc_slot(host).set_sleep) 1257}
1237 mmc_slot(host).set_sleep(host->dev, host->slot_id, 1258
1238 1, 0, 0); 1259/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
1239 new_state = REGSLEEP; 1260static int omap_mmc_sleep_to_off(struct mmc_omap_host *host)
1261{
1262 if (!mmc_try_claim_host(host->mmc))
1263 return 0;
1264
1265 if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
1266 mmc_slot(host).card_detect ||
1267 (mmc_slot(host).get_cover_state &&
1268 mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
1269 mmc_release_host(host->mmc);
1270 return 0;
1240 } 1271 }
1241 1272
1242 OMAP_HSMMC_WRITE(host->base, ISE, 0); 1273 mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
1243 OMAP_HSMMC_WRITE(host->base, IE, 0); 1274 host->vdd = 0;
1244 OMAP_HSMMC_WRITE(host->base, HCTL, 1275 host->power_mode = MMC_POWER_OFF;
1245 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
1246 1276
1247 clk_disable(host->fclk); 1277 dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
1248 clk_disable(host->iclk); 1278 host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
1249 clk_disable(host->dbclk);
1250 1279
1251 host->dpm_state = new_state; 1280 host->dpm_state = OFF;
1252 1281
1253 mmc_release_host(host->mmc); 1282 mmc_release_host(host->mmc);
1254 1283
@@ -1273,62 +1302,43 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
1273 return 0; 1302 return 0;
1274} 1303}
1275 1304
1276/* Handler for [OFF -> ENABLED] transition */ 1305/* Handler for [SLEEP -> ENABLED] transition */
1277static int omap_mmc_off_to_enabled(struct mmc_omap_host *host) 1306static int omap_mmc_sleep_to_enabled(struct mmc_omap_host *host)
1278{ 1307{
1279 clk_enable(host->fclk); 1308 if (!mmc_try_claim_host(host->mmc))
1280 clk_enable(host->iclk); 1309 return 0;
1281
1282 if (clk_enable(host->dbclk))
1283 dev_dbg(mmc_dev(host->mmc),
1284 "Enabling debounce clk failed\n");
1285 1310
1311 clk_enable(host->fclk);
1286 omap_mmc_restore_ctx(host); 1312 omap_mmc_restore_ctx(host);
1287 omap_hsmmc_init(host); 1313 if (mmc_slot(host).set_sleep)
1288 mmc_power_restore_host(host->mmc); 1314 mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
1315 host->vdd, host->dpm_state == CARDSLEEP);
1316 if (mmc_card_can_sleep(host->mmc))
1317 mmc_card_awake(host->mmc);
1318
1319 dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
1320 host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
1289 1321
1290 host->dpm_state = ENABLED; 1322 host->dpm_state = ENABLED;
1291 1323
1292 dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n"); 1324 mmc_release_host(host->mmc);
1293 1325
1294 return 0; 1326 return 0;
1295} 1327}
1296 1328
1297/* Handler for [REGSLEEP -> ENABLED] transition */ 1329/* Handler for [OFF -> ENABLED] transition */
1298static int omap_mmc_regsleep_to_enabled(struct mmc_omap_host *host) 1330static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
1299{ 1331{
1300 unsigned long timeout;
1301
1302 dev_dbg(mmc_dev(host->mmc), "REGSLEEP -> ENABLED\n");
1303
1304 clk_enable(host->fclk); 1332 clk_enable(host->fclk);
1305 clk_enable(host->iclk);
1306
1307 if (clk_enable(host->dbclk))
1308 dev_dbg(mmc_dev(host->mmc),
1309 "Enabling debounce clk failed\n");
1310 1333
1311 omap_mmc_restore_ctx(host); 1334 omap_mmc_restore_ctx(host);
1312 1335 omap_hsmmc_init(host);
1313 /* 1336 mmc_power_restore_host(host->mmc);
1314 * We turned off interrupts and bus power. Interrupts
1315 * are turned on by 'mmc_omap_start_command()' so we
1316 * just need to turn on the bus power here.
1317 */
1318 OMAP_HSMMC_WRITE(host->base, HCTL,
1319 OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
1320
1321 timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
1322 while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP &&
1323 time_before(jiffies, timeout))
1324 ;
1325
1326 if (mmc_slot(host).set_sleep)
1327 mmc_slot(host).set_sleep(host->dev, host->slot_id,
1328 0, host->vdd, 0);
1329 1337
1330 host->dpm_state = ENABLED; 1338 host->dpm_state = ENABLED;
1331 1339
1340 dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
1341
1332 return 0; 1342 return 0;
1333} 1343}
1334 1344
@@ -1342,8 +1352,9 @@ static int omap_mmc_enable(struct mmc_host *mmc)
1342 switch (host->dpm_state) { 1352 switch (host->dpm_state) {
1343 case DISABLED: 1353 case DISABLED:
1344 return omap_mmc_disabled_to_enabled(host); 1354 return omap_mmc_disabled_to_enabled(host);
1355 case CARDSLEEP:
1345 case REGSLEEP: 1356 case REGSLEEP:
1346 return omap_mmc_regsleep_to_enabled(host); 1357 return omap_mmc_sleep_to_enabled(host);
1347 case OFF: 1358 case OFF:
1348 return omap_mmc_off_to_enabled(host); 1359 return omap_mmc_off_to_enabled(host);
1349 default: 1360 default:
@@ -1369,7 +1380,10 @@ static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
1369 return 0; 1380 return 0;
1370 } 1381 }
1371 case DISABLED: 1382 case DISABLED:
1372 return omap_mmc_disabled_to_off(host); 1383 return omap_mmc_disabled_to_sleep(host);
1384 case CARDSLEEP:
1385 case REGSLEEP:
1386 return omap_mmc_sleep_to_off(host);
1373 default: 1387 default:
1374 dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); 1388 dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
1375 return -EINVAL; 1389 return -EINVAL;
@@ -1441,8 +1455,7 @@ static int mmc_regs_show(struct seq_file *s, void *data)
1441 host->dpm_state, mmc->nesting_cnt, 1455 host->dpm_state, mmc->nesting_cnt,
1442 host->context_loss, context_loss); 1456 host->context_loss, context_loss);
1443 1457
1444 if (host->suspended || host->dpm_state == OFF || 1458 if (host->suspended || host->dpm_state == OFF) {
1445 host->dpm_state == REGSLEEP) {
1446 seq_printf(s, "host suspended, can't read registers\n"); 1459 seq_printf(s, "host suspended, can't read registers\n");
1447 return 0; 1460 return 0;
1448 } 1461 }
@@ -1617,7 +1630,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
1617 mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 1630 mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
1618 mmc->max_seg_size = mmc->max_req_size; 1631 mmc->max_seg_size = mmc->max_req_size;
1619 1632
1620 mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; 1633 mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
1634 MMC_CAP_WAIT_WHILE_BUSY;
1621 1635
1622 if (pdata->slots[host->slot_id].wires >= 8) 1636 if (pdata->slots[host->slot_id].wires >= 8)
1623 mmc->caps |= MMC_CAP_8_BIT_DATA; 1637 mmc->caps |= MMC_CAP_8_BIT_DATA;