aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/omap_hsmmc.c59
1 files changed, 54 insertions, 5 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 016914c2c4e7..c82ec7f4e1a7 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -162,6 +162,7 @@ struct mmc_omap_host {
162 int response_busy; 162 int response_busy;
163 int context_loss; 163 int context_loss;
164 int dpm_state; 164 int dpm_state;
165 int vdd;
165 166
166 struct omap_mmc_platform_data *pdata; 167 struct omap_mmc_platform_data *pdata;
167}; 168};
@@ -1038,10 +1039,12 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
1038 case MMC_POWER_OFF: 1039 case MMC_POWER_OFF:
1039 mmc_slot(host).set_power(host->dev, host->slot_id, 1040 mmc_slot(host).set_power(host->dev, host->slot_id,
1040 0, 0); 1041 0, 0);
1042 host->vdd = 0;
1041 break; 1043 break;
1042 case MMC_POWER_UP: 1044 case MMC_POWER_UP:
1043 mmc_slot(host).set_power(host->dev, host->slot_id, 1045 mmc_slot(host).set_power(host->dev, host->slot_id,
1044 1, ios->vdd); 1046 1, ios->vdd);
1047 host->vdd = ios->vdd;
1045 break; 1048 break;
1046 case MMC_POWER_ON: 1049 case MMC_POWER_ON:
1047 do_send_init_stream = 1; 1050 do_send_init_stream = 1;
@@ -1179,19 +1182,20 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
1179 1182
1180/* 1183/*
1181 * Dynamic power saving handling, FSM: 1184 * Dynamic power saving handling, FSM:
1182 * ENABLED -> DISABLED -> OFF 1185 * ENABLED -> DISABLED -> OFF / REGSLEEP
1183 * ^___________| | 1186 * ^___________| |
1184 * |______________________| 1187 * |______________________|
1185 * 1188 *
1186 * ENABLED: mmc host is fully functional 1189 * ENABLED: mmc host is fully functional
1187 * DISABLED: fclk is off 1190 * DISABLED: fclk is off
1188 * OFF: fclk is off,voltage regulator is off 1191 * OFF: fclk is off,voltage regulator is off
1192 * REGSLEEP: fclk is off,voltage regulator is asleep
1189 * 1193 *
1190 * Transition handlers return the timeout for the next state transition 1194 * Transition handlers return the timeout for the next state transition
1191 * or negative error. 1195 * or negative error.
1192 */ 1196 */
1193 1197
1194enum {ENABLED = 0, DISABLED, OFF}; 1198enum {ENABLED = 0, DISABLED, REGSLEEP, OFF};
1195 1199
1196/* Handler for [ENABLED -> DISABLED] transition */ 1200/* Handler for [ENABLED -> DISABLED] transition */
1197static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host) 1201static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
@@ -1228,8 +1232,12 @@ static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
1228 mmc_slot(host).get_cover_state(host->dev, host->slot_id))) { 1232 mmc_slot(host).get_cover_state(host->dev, host->slot_id))) {
1229 mmc_power_save_host(host->mmc); 1233 mmc_power_save_host(host->mmc);
1230 new_state = OFF; 1234 new_state = OFF;
1231 } else 1235 } else {
1232 new_state = DISABLED; 1236 if (mmc_slot(host).set_sleep)
1237 mmc_slot(host).set_sleep(host->dev, host->slot_id,
1238 1, 0, 0);
1239 new_state = REGSLEEP;
1240 }
1233 1241
1234 OMAP_HSMMC_WRITE(host->base, ISE, 0); 1242 OMAP_HSMMC_WRITE(host->base, ISE, 0);
1235 OMAP_HSMMC_WRITE(host->base, IE, 0); 1243 OMAP_HSMMC_WRITE(host->base, IE, 0);
@@ -1286,6 +1294,44 @@ static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
1286 return 0; 1294 return 0;
1287} 1295}
1288 1296
1297/* Handler for [REGSLEEP -> ENABLED] transition */
1298static int omap_mmc_regsleep_to_enabled(struct mmc_omap_host *host)
1299{
1300 unsigned long timeout;
1301
1302 dev_dbg(mmc_dev(host->mmc), "REGSLEEP -> ENABLED\n");
1303
1304 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
1311 omap_mmc_restore_ctx(host);
1312
1313 /*
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
1330 host->dpm_state = ENABLED;
1331
1332 return 0;
1333}
1334
1289/* 1335/*
1290 * Bring MMC host to ENABLED from any other PM state. 1336 * Bring MMC host to ENABLED from any other PM state.
1291 */ 1337 */
@@ -1296,6 +1342,8 @@ static int omap_mmc_enable(struct mmc_host *mmc)
1296 switch (host->dpm_state) { 1342 switch (host->dpm_state) {
1297 case DISABLED: 1343 case DISABLED:
1298 return omap_mmc_disabled_to_enabled(host); 1344 return omap_mmc_disabled_to_enabled(host);
1345 case REGSLEEP:
1346 return omap_mmc_regsleep_to_enabled(host);
1299 case OFF: 1347 case OFF:
1300 return omap_mmc_off_to_enabled(host); 1348 return omap_mmc_off_to_enabled(host);
1301 default: 1349 default:
@@ -1393,7 +1441,8 @@ static int mmc_regs_show(struct seq_file *s, void *data)
1393 host->dpm_state, mmc->nesting_cnt, 1441 host->dpm_state, mmc->nesting_cnt,
1394 host->context_loss, context_loss); 1442 host->context_loss, context_loss);
1395 1443
1396 if (host->suspended || host->dpm_state == OFF) { 1444 if (host->suspended || host->dpm_state == OFF ||
1445 host->dpm_state == REGSLEEP) {
1397 seq_printf(s, "host suspended, can't read registers\n"); 1446 seq_printf(s, "host suspended, can't read registers\n");
1398 return 0; 1447 return 0;
1399 } 1448 }