aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorNicolas Pitre <nico@fluxnic.net>2009-09-22 19:45:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:38 -0400
commit95cdfb72b9bc568803f395c266152c71b034b461 (patch)
treeba4d10ee9eb595398d14cf44b433b82ea7802996 /drivers/mmc
parent17d33e14f7ffc05f8c81e4a3bdb9a8003a05dcce (diff)
mmc: propagate error codes back from bus drivers' suspend/resume methods
Especially for SDIO drivers which may have special conditions/errors to report, it is a good thing to relay the returned error code back to upper layers. This also allows for the rationalization of the resume path where code to "remove" a no-longer-existing or replaced card was duplicated into the MMC, SD and SDIO bus drivers. In the SDIO case, if a function suspend method returns an error, then all previously suspended functions are resumed and the error returned. An exception is made for -ENOSYS which the core interprets as "we don't support suspend so just kick the card out for suspend and return success". When resuming SDIO cards, the core code only validates the manufacturer and product IDs to make sure the same kind of card is still present before invoking functions resume methods. It's the function driver's responsibility to perform further tests to confirm that the actual same card is present (same MAC address, etc.) and return an error otherwise. Signed-off-by: Nicolas Pitre <nico@marvell.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')
-rw-r--r--drivers/mmc/core/core.c35
-rw-r--r--drivers/mmc/core/core.h4
-rw-r--r--drivers/mmc/core/mmc.c15
-rw-r--r--drivers/mmc/core/sd.c15
-rw-r--r--drivers/mmc/core/sdio.c54
5 files changed, 69 insertions, 54 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 0842f6829250..7dab2e5f4bc9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1236,6 +1236,8 @@ EXPORT_SYMBOL(mmc_card_can_sleep);
1236 */ 1236 */
1237int mmc_suspend_host(struct mmc_host *host, pm_message_t state) 1237int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
1238{ 1238{
1239 int err = 0;
1240
1239 if (host->caps & MMC_CAP_DISABLE) 1241 if (host->caps & MMC_CAP_DISABLE)
1240 cancel_delayed_work(&host->disable); 1242 cancel_delayed_work(&host->disable);
1241 cancel_delayed_work(&host->detect); 1243 cancel_delayed_work(&host->detect);
@@ -1244,21 +1246,26 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
1244 mmc_bus_get(host); 1246 mmc_bus_get(host);
1245 if (host->bus_ops && !host->bus_dead) { 1247 if (host->bus_ops && !host->bus_dead) {
1246 if (host->bus_ops->suspend) 1248 if (host->bus_ops->suspend)
1247 host->bus_ops->suspend(host); 1249 err = host->bus_ops->suspend(host);
1248 if (!host->bus_ops->resume) { 1250 if (err == -ENOSYS || !host->bus_ops->resume) {
1251 /*
1252 * We simply "remove" the card in this case.
1253 * It will be redetected on resume.
1254 */
1249 if (host->bus_ops->remove) 1255 if (host->bus_ops->remove)
1250 host->bus_ops->remove(host); 1256 host->bus_ops->remove(host);
1251
1252 mmc_claim_host(host); 1257 mmc_claim_host(host);
1253 mmc_detach_bus(host); 1258 mmc_detach_bus(host);
1254 mmc_release_host(host); 1259 mmc_release_host(host);
1260 err = 0;
1255 } 1261 }
1256 } 1262 }
1257 mmc_bus_put(host); 1263 mmc_bus_put(host);
1258 1264
1259 mmc_power_off(host); 1265 if (!err)
1266 mmc_power_off(host);
1260 1267
1261 return 0; 1268 return err;
1262} 1269}
1263 1270
1264EXPORT_SYMBOL(mmc_suspend_host); 1271EXPORT_SYMBOL(mmc_suspend_host);
@@ -1269,12 +1276,26 @@ EXPORT_SYMBOL(mmc_suspend_host);
1269 */ 1276 */
1270int mmc_resume_host(struct mmc_host *host) 1277int mmc_resume_host(struct mmc_host *host)
1271{ 1278{
1279 int err = 0;
1280
1272 mmc_bus_get(host); 1281 mmc_bus_get(host);
1273 if (host->bus_ops && !host->bus_dead) { 1282 if (host->bus_ops && !host->bus_dead) {
1274 mmc_power_up(host); 1283 mmc_power_up(host);
1275 mmc_select_voltage(host, host->ocr); 1284 mmc_select_voltage(host, host->ocr);
1276 BUG_ON(!host->bus_ops->resume); 1285 BUG_ON(!host->bus_ops->resume);
1277 host->bus_ops->resume(host); 1286 err = host->bus_ops->resume(host);
1287 if (err) {
1288 printk(KERN_WARNING "%s: error %d during resume "
1289 "(card was removed?)\n",
1290 mmc_hostname(host), err);
1291 if (host->bus_ops->remove)
1292 host->bus_ops->remove(host);
1293 mmc_claim_host(host);
1294 mmc_detach_bus(host);
1295 mmc_release_host(host);
1296 /* no need to bother upper layers */
1297 err = 0;
1298 }
1278 } 1299 }
1279 mmc_bus_put(host); 1300 mmc_bus_put(host);
1280 1301
@@ -1284,7 +1305,7 @@ int mmc_resume_host(struct mmc_host *host)
1284 */ 1305 */
1285 mmc_detect_change(host, 1); 1306 mmc_detect_change(host, 1);
1286 1307
1287 return 0; 1308 return err;
1288} 1309}
1289 1310
1290EXPORT_SYMBOL(mmc_resume_host); 1311EXPORT_SYMBOL(mmc_resume_host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c386348f5f73..67ae6abc4230 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -20,8 +20,8 @@ struct mmc_bus_ops {
20 int (*sleep)(struct mmc_host *); 20 int (*sleep)(struct mmc_host *);
21 void (*remove)(struct mmc_host *); 21 void (*remove)(struct mmc_host *);
22 void (*detect)(struct mmc_host *); 22 void (*detect)(struct mmc_host *);
23 void (*suspend)(struct mmc_host *); 23 int (*suspend)(struct mmc_host *);
24 void (*resume)(struct mmc_host *); 24 int (*resume)(struct mmc_host *);
25 void (*power_save)(struct mmc_host *); 25 void (*power_save)(struct mmc_host *);
26 void (*power_restore)(struct mmc_host *); 26 void (*power_restore)(struct mmc_host *);
27}; 27};
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ddddad46b402..bfefce365ae7 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -530,7 +530,7 @@ static void mmc_detect(struct mmc_host *host)
530/* 530/*
531 * Suspend callback from host. 531 * Suspend callback from host.
532 */ 532 */
533static void mmc_suspend(struct mmc_host *host) 533static int mmc_suspend(struct mmc_host *host)
534{ 534{
535 BUG_ON(!host); 535 BUG_ON(!host);
536 BUG_ON(!host->card); 536 BUG_ON(!host->card);
@@ -540,6 +540,8 @@ static void mmc_suspend(struct mmc_host *host)
540 mmc_deselect_cards(host); 540 mmc_deselect_cards(host);
541 host->card->state &= ~MMC_STATE_HIGHSPEED; 541 host->card->state &= ~MMC_STATE_HIGHSPEED;
542 mmc_release_host(host); 542 mmc_release_host(host);
543
544 return 0;
543} 545}
544 546
545/* 547/*
@@ -548,7 +550,7 @@ static void mmc_suspend(struct mmc_host *host)
548 * This function tries to determine if the same card is still present 550 * This function tries to determine if the same card is still present
549 * and, if so, restore all state to it. 551 * and, if so, restore all state to it.
550 */ 552 */
551static void mmc_resume(struct mmc_host *host) 553static int mmc_resume(struct mmc_host *host)
552{ 554{
553 int err; 555 int err;
554 556
@@ -559,14 +561,7 @@ static void mmc_resume(struct mmc_host *host)
559 err = mmc_init_card(host, host->ocr, host->card); 561 err = mmc_init_card(host, host->ocr, host->card);
560 mmc_release_host(host); 562 mmc_release_host(host);
561 563
562 if (err) { 564 return err;
563 mmc_remove(host);
564
565 mmc_claim_host(host);
566 mmc_detach_bus(host);
567 mmc_release_host(host);
568 }
569
570} 565}
571 566
572static void mmc_power_restore(struct mmc_host *host) 567static void mmc_power_restore(struct mmc_host *host)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 13b3a6b8f5b5..10b2a4d20f5a 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -564,7 +564,7 @@ static void mmc_sd_detect(struct mmc_host *host)
564/* 564/*
565 * Suspend callback from host. 565 * Suspend callback from host.
566 */ 566 */
567static void mmc_sd_suspend(struct mmc_host *host) 567static int mmc_sd_suspend(struct mmc_host *host)
568{ 568{
569 BUG_ON(!host); 569 BUG_ON(!host);
570 BUG_ON(!host->card); 570 BUG_ON(!host->card);
@@ -574,6 +574,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
574 mmc_deselect_cards(host); 574 mmc_deselect_cards(host);
575 host->card->state &= ~MMC_STATE_HIGHSPEED; 575 host->card->state &= ~MMC_STATE_HIGHSPEED;
576 mmc_release_host(host); 576 mmc_release_host(host);
577
578 return 0;
577} 579}
578 580
579/* 581/*
@@ -582,7 +584,7 @@ static void mmc_sd_suspend(struct mmc_host *host)
582 * This function tries to determine if the same card is still present 584 * This function tries to determine if the same card is still present
583 * and, if so, restore all state to it. 585 * and, if so, restore all state to it.
584 */ 586 */
585static void mmc_sd_resume(struct mmc_host *host) 587static int mmc_sd_resume(struct mmc_host *host)
586{ 588{
587 int err; 589 int err;
588 590
@@ -593,14 +595,7 @@ static void mmc_sd_resume(struct mmc_host *host)
593 err = mmc_sd_init_card(host, host->ocr, host->card); 595 err = mmc_sd_init_card(host, host->ocr, host->card);
594 mmc_release_host(host); 596 mmc_release_host(host);
595 597
596 if (err) { 598 return err;
597 mmc_sd_remove(host);
598
599 mmc_claim_host(host);
600 mmc_detach_bus(host);
601 mmc_release_host(host);
602 }
603
604} 599}
605 600
606static void mmc_sd_power_restore(struct mmc_host *host) 601static void mmc_sd_power_restore(struct mmc_host *host)
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index f4c8637fd072..cdb845b68ab5 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -302,6 +302,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
302 goto err; 302 goto err;
303 } 303 }
304 card = oldcard; 304 card = oldcard;
305 return 0;
305 } 306 }
306 307
307 /* 308 /*
@@ -399,62 +400,65 @@ static void mmc_sdio_detect(struct mmc_host *host)
399 * Therefore all registered functions must have drivers with suspend 400 * Therefore all registered functions must have drivers with suspend
400 * and resume methods. Failing that we simply remove the whole card. 401 * and resume methods. Failing that we simply remove the whole card.
401 */ 402 */
402static void mmc_sdio_suspend(struct mmc_host *host) 403static int mmc_sdio_suspend(struct mmc_host *host)
403{ 404{
404 int i; 405 int i, err = 0;
405 406
406 /* make sure all registered functions can suspend/resume */
407 for (i = 0; i < host->card->sdio_funcs; i++) { 407 for (i = 0; i < host->card->sdio_funcs; i++) {
408 struct sdio_func *func = host->card->sdio_func[i]; 408 struct sdio_func *func = host->card->sdio_func[i];
409 if (func && sdio_func_present(func) && func->dev.driver) { 409 if (func && sdio_func_present(func) && func->dev.driver) {
410 const struct dev_pm_ops *pmops = func->dev.driver->pm; 410 const struct dev_pm_ops *pmops = func->dev.driver->pm;
411 if (!pmops || !pmops->suspend || !pmops->resume) { 411 if (!pmops || !pmops->suspend || !pmops->resume) {
412 /* just remove the entire card in that case */ 412 /* force removal of entire card in that case */
413 mmc_sdio_remove(host); 413 err = -ENOSYS;
414 mmc_claim_host(host); 414 } else
415 mmc_detach_bus(host); 415 err = pmops->suspend(&func->dev);
416 mmc_release_host(host); 416 if (err)
417 return; 417 break;
418 }
419 } 418 }
420 } 419 }
421 420 while (err && --i >= 0) {
422 /* now suspend them */
423 for (i = 0; i < host->card->sdio_funcs; i++) {
424 struct sdio_func *func = host->card->sdio_func[i]; 421 struct sdio_func *func = host->card->sdio_func[i];
425 if (func && sdio_func_present(func) && func->dev.driver) { 422 if (func && sdio_func_present(func) && func->dev.driver) {
426 const struct dev_pm_ops *pmops = func->dev.driver->pm; 423 const struct dev_pm_ops *pmops = func->dev.driver->pm;
427 pmops->suspend(&func->dev); 424 pmops->resume(&func->dev);
428 } 425 }
429 } 426 }
427
428 return err;
430} 429}
431 430
432static void mmc_sdio_resume(struct mmc_host *host) 431static int mmc_sdio_resume(struct mmc_host *host)
433{ 432{
434 int i, err; 433 int i, err;
435 434
436 BUG_ON(!host); 435 BUG_ON(!host);
437 BUG_ON(!host->card); 436 BUG_ON(!host->card);
438 437
438 /* Basic card reinitialization. */
439 mmc_claim_host(host); 439 mmc_claim_host(host);
440 err = mmc_sdio_init_card(host, host->ocr, host->card); 440 err = mmc_sdio_init_card(host, host->ocr, host->card);
441 mmc_release_host(host); 441 mmc_release_host(host);
442 if (err) {
443 mmc_sdio_remove(host);
444 mmc_claim_host(host);
445 mmc_detach_bus(host);
446 mmc_release_host(host);
447 return;
448 }
449 442
450 /* resume all functions */ 443 /*
451 for (i = 0; i < host->card->sdio_funcs; i++) { 444 * If the card looked to be the same as before suspending, then
445 * we proceed to resume all card functions. If one of them returns
446 * an error then we simply return that error to the core and the
447 * card will be redetected as new. It is the responsibility of
448 * the function driver to perform further tests with the extra
449 * knowledge it has of the card to confirm the card is indeed the
450 * same as before suspending (same MAC address for network cards,
451 * etc.) and return an error otherwise.
452 */
453 for (i = 0; !err && i < host->card->sdio_funcs; i++) {
452 struct sdio_func *func = host->card->sdio_func[i]; 454 struct sdio_func *func = host->card->sdio_func[i];
453 if (func && sdio_func_present(func) && func->dev.driver) { 455 if (func && sdio_func_present(func) && func->dev.driver) {
454 const struct dev_pm_ops *pmops = func->dev.driver->pm; 456 const struct dev_pm_ops *pmops = func->dev.driver->pm;
455 pmops->resume(&func->dev); 457 err = pmops->resume(&func->dev);
456 } 458 }
457 } 459 }
460
461 return err;
458} 462}
459 463
460static const struct mmc_bus_ops mmc_sdio_ops = { 464static const struct mmc_bus_ops mmc_sdio_ops = {