Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Nicolas Pitre and committed by
Linus Torvalds
95cdfb72 17d33e14

+85 -70
+28 -7
drivers/mmc/core/core.c
··· 1236 1236 */ 1237 1237 int mmc_suspend_host(struct mmc_host *host, pm_message_t state) 1238 1238 { 1239 + int err = 0; 1240 + 1239 1241 if (host->caps & MMC_CAP_DISABLE) 1240 1242 cancel_delayed_work(&host->disable); 1241 1243 cancel_delayed_work(&host->detect); ··· 1246 1244 mmc_bus_get(host); 1247 1245 if (host->bus_ops && !host->bus_dead) { 1248 1246 if (host->bus_ops->suspend) 1249 - host->bus_ops->suspend(host); 1250 - if (!host->bus_ops->resume) { 1247 + err = host->bus_ops->suspend(host); 1248 + if (err == -ENOSYS || !host->bus_ops->resume) { 1249 + /* 1250 + * We simply "remove" the card in this case. 1251 + * It will be redetected on resume. 1252 + */ 1251 1253 if (host->bus_ops->remove) 1252 1254 host->bus_ops->remove(host); 1253 - 1254 1255 mmc_claim_host(host); 1255 1256 mmc_detach_bus(host); 1256 1257 mmc_release_host(host); 1258 + err = 0; 1257 1259 } 1258 1260 } 1259 1261 mmc_bus_put(host); 1260 1262 1261 - mmc_power_off(host); 1263 + if (!err) 1264 + mmc_power_off(host); 1262 1265 1263 - return 0; 1266 + return err; 1264 1267 } 1265 1268 1266 1269 EXPORT_SYMBOL(mmc_suspend_host); ··· 1276 1269 */ 1277 1270 int mmc_resume_host(struct mmc_host *host) 1278 1271 { 1272 + int err = 0; 1273 + 1279 1274 mmc_bus_get(host); 1280 1275 if (host->bus_ops && !host->bus_dead) { 1281 1276 mmc_power_up(host); 1282 1277 mmc_select_voltage(host, host->ocr); 1283 1278 BUG_ON(!host->bus_ops->resume); 1284 - host->bus_ops->resume(host); 1279 + err = host->bus_ops->resume(host); 1280 + if (err) { 1281 + printk(KERN_WARNING "%s: error %d during resume " 1282 + "(card was removed?)\n", 1283 + mmc_hostname(host), err); 1284 + if (host->bus_ops->remove) 1285 + host->bus_ops->remove(host); 1286 + mmc_claim_host(host); 1287 + mmc_detach_bus(host); 1288 + mmc_release_host(host); 1289 + /* no need to bother upper layers */ 1290 + err = 0; 1291 + } 1285 1292 } 1286 1293 mmc_bus_put(host); 1287 1294 ··· 1305 1284 */ 1306 1285 mmc_detect_change(host, 1); 1307 1286 1308 - return 0; 1287 + return err; 1309 1288 } 1310 1289 1311 1290 EXPORT_SYMBOL(mmc_resume_host);
+2 -2
drivers/mmc/core/core.h
··· 20 20 int (*sleep)(struct mmc_host *); 21 21 void (*remove)(struct mmc_host *); 22 22 void (*detect)(struct mmc_host *); 23 - void (*suspend)(struct mmc_host *); 24 - void (*resume)(struct mmc_host *); 23 + int (*suspend)(struct mmc_host *); 24 + int (*resume)(struct mmc_host *); 25 25 void (*power_save)(struct mmc_host *); 26 26 void (*power_restore)(struct mmc_host *); 27 27 };
+5 -10
drivers/mmc/core/mmc.c
··· 530 530 /* 531 531 * Suspend callback from host. 532 532 */ 533 - static void mmc_suspend(struct mmc_host *host) 533 + static int mmc_suspend(struct mmc_host *host) 534 534 { 535 535 BUG_ON(!host); 536 536 BUG_ON(!host->card); ··· 540 540 mmc_deselect_cards(host); 541 541 host->card->state &= ~MMC_STATE_HIGHSPEED; 542 542 mmc_release_host(host); 543 + 544 + return 0; 543 545 } 544 546 545 547 /* ··· 550 548 * This function tries to determine if the same card is still present 551 549 * and, if so, restore all state to it. 552 550 */ 553 - static void mmc_resume(struct mmc_host *host) 551 + static int mmc_resume(struct mmc_host *host) 554 552 { 555 553 int err; 556 554 ··· 561 559 err = mmc_init_card(host, host->ocr, host->card); 562 560 mmc_release_host(host); 563 561 564 - if (err) { 565 - mmc_remove(host); 566 - 567 - mmc_claim_host(host); 568 - mmc_detach_bus(host); 569 - mmc_release_host(host); 570 - } 571 - 562 + return err; 572 563 } 573 564 574 565 static void mmc_power_restore(struct mmc_host *host)
+5 -10
drivers/mmc/core/sd.c
··· 564 564 /* 565 565 * Suspend callback from host. 566 566 */ 567 - static void mmc_sd_suspend(struct mmc_host *host) 567 + static int mmc_sd_suspend(struct mmc_host *host) 568 568 { 569 569 BUG_ON(!host); 570 570 BUG_ON(!host->card); ··· 574 574 mmc_deselect_cards(host); 575 575 host->card->state &= ~MMC_STATE_HIGHSPEED; 576 576 mmc_release_host(host); 577 + 578 + return 0; 577 579 } 578 580 579 581 /* ··· 584 582 * This function tries to determine if the same card is still present 585 583 * and, if so, restore all state to it. 586 584 */ 587 - static void mmc_sd_resume(struct mmc_host *host) 585 + static int mmc_sd_resume(struct mmc_host *host) 588 586 { 589 587 int err; 590 588 ··· 595 593 err = mmc_sd_init_card(host, host->ocr, host->card); 596 594 mmc_release_host(host); 597 595 598 - if (err) { 599 - mmc_sd_remove(host); 600 - 601 - mmc_claim_host(host); 602 - mmc_detach_bus(host); 603 - mmc_release_host(host); 604 - } 605 - 596 + return err; 606 597 } 607 598 608 599 static void mmc_sd_power_restore(struct mmc_host *host)
+45 -41
drivers/mmc/core/sdio.c
··· 302 302 goto err; 303 303 } 304 304 card = oldcard; 305 + return 0; 305 306 } 306 307 307 308 /* ··· 400 399 * Therefore all registered functions must have drivers with suspend 401 400 * and resume methods. Failing that we simply remove the whole card. 402 401 */ 403 - static void mmc_sdio_suspend(struct mmc_host *host) 402 + static int mmc_sdio_suspend(struct mmc_host *host) 404 403 { 405 - int i; 404 + int i, err = 0; 406 405 407 - /* make sure all registered functions can suspend/resume */ 408 406 for (i = 0; i < host->card->sdio_funcs; i++) { 409 407 struct sdio_func *func = host->card->sdio_func[i]; 410 408 if (func && sdio_func_present(func) && func->dev.driver) { 411 409 const struct dev_pm_ops *pmops = func->dev.driver->pm; 412 410 if (!pmops || !pmops->suspend || !pmops->resume) { 413 - /* just remove the entire card in that case */ 414 - mmc_sdio_remove(host); 415 - mmc_claim_host(host); 416 - mmc_detach_bus(host); 417 - mmc_release_host(host); 418 - return; 419 - } 411 + /* force removal of entire card in that case */ 412 + err = -ENOSYS; 413 + } else 414 + err = pmops->suspend(&func->dev); 415 + if (err) 416 + break; 420 417 } 421 418 } 422 - 423 - /* now suspend them */ 424 - for (i = 0; i < host->card->sdio_funcs; i++) { 425 - struct sdio_func *func = host->card->sdio_func[i]; 426 - if (func && sdio_func_present(func) && func->dev.driver) { 427 - const struct dev_pm_ops *pmops = func->dev.driver->pm; 428 - pmops->suspend(&func->dev); 429 - } 430 - } 431 - } 432 - 433 - static void mmc_sdio_resume(struct mmc_host *host) 434 - { 435 - int i, err; 436 - 437 - BUG_ON(!host); 438 - BUG_ON(!host->card); 439 - 440 - mmc_claim_host(host); 441 - err = mmc_sdio_init_card(host, host->ocr, host->card); 442 - mmc_release_host(host); 443 - if (err) { 444 - mmc_sdio_remove(host); 445 - mmc_claim_host(host); 446 - mmc_detach_bus(host); 447 - mmc_release_host(host); 448 - return; 449 - } 450 - 451 - /* resume all functions */ 452 - for (i = 0; i < host->card->sdio_funcs; i++) { 419 + while (err && --i >= 0) { 453 420 struct sdio_func *func = host->card->sdio_func[i]; 454 421 if (func && sdio_func_present(func) && func->dev.driver) { 455 422 const struct dev_pm_ops *pmops = func->dev.driver->pm; 456 423 pmops->resume(&func->dev); 457 424 } 458 425 } 426 + 427 + return err; 428 + } 429 + 430 + static int mmc_sdio_resume(struct mmc_host *host) 431 + { 432 + int i, err; 433 + 434 + BUG_ON(!host); 435 + BUG_ON(!host->card); 436 + 437 + /* Basic card reinitialization. */ 438 + mmc_claim_host(host); 439 + err = mmc_sdio_init_card(host, host->ocr, host->card); 440 + mmc_release_host(host); 441 + 442 + /* 443 + * If the card looked to be the same as before suspending, then 444 + * we proceed to resume all card functions. If one of them returns 445 + * an error then we simply return that error to the core and the 446 + * card will be redetected as new. It is the responsibility of 447 + * the function driver to perform further tests with the extra 448 + * knowledge it has of the card to confirm the card is indeed the 449 + * same as before suspending (same MAC address for network cards, 450 + * etc.) and return an error otherwise. 451 + */ 452 + for (i = 0; !err && i < host->card->sdio_funcs; i++) { 453 + struct sdio_func *func = host->card->sdio_func[i]; 454 + if (func && sdio_func_present(func) && func->dev.driver) { 455 + const struct dev_pm_ops *pmops = func->dev.driver->pm; 456 + err = pmops->resume(&func->dev); 457 + } 458 + } 459 + 460 + return err; 459 461 } 460 462 461 463 static const struct mmc_bus_ops mmc_sdio_ops = {