diff options
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 59 |
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 | ||
1194 | enum {ENABLED = 0, DISABLED, OFF}; | 1198 | enum {ENABLED = 0, DISABLED, REGSLEEP, OFF}; |
1195 | 1199 | ||
1196 | /* Handler for [ENABLED -> DISABLED] transition */ | 1200 | /* Handler for [ENABLED -> DISABLED] transition */ |
1197 | static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host) | 1201 | static 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 */ | ||
1298 | static 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 | } |