diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2008-04-16 13:13:13 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-15 08:14:40 -0400 |
commit | 1e72859e3ae16346d4007024b20d2d4ef387dcc3 (patch) | |
tree | 5fc8319ce14b0770546bbbf9a72c90abaf019317 /drivers/mmc/host/sdhci.c | |
parent | 4489428ab5a49a6f443d9aa17f1d891417787d7b (diff) |
sdhci: handle hot-remove
Gracefully handle when the device is suddenly removed. Do a test read
and avoid any further access if that read returns -1.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 48 |
1 files changed, 40 insertions, 8 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 95b081a9967b..0ab582e77ac2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
@@ -712,7 +712,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
712 | 712 | ||
713 | host->mrq = mrq; | 713 | host->mrq = mrq; |
714 | 714 | ||
715 | if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { | 715 | if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT) |
716 | || (host->flags & SDHCI_DEVICE_DEAD)) { | ||
716 | host->mrq->cmd->error = -ENOMEDIUM; | 717 | host->mrq->cmd->error = -ENOMEDIUM; |
717 | tasklet_schedule(&host->finish_tasklet); | 718 | tasklet_schedule(&host->finish_tasklet); |
718 | } else | 719 | } else |
@@ -732,6 +733,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
732 | 733 | ||
733 | spin_lock_irqsave(&host->lock, flags); | 734 | spin_lock_irqsave(&host->lock, flags); |
734 | 735 | ||
736 | if (host->flags & SDHCI_DEVICE_DEAD) | ||
737 | goto out; | ||
738 | |||
735 | /* | 739 | /* |
736 | * Reset the chip on each power off. | 740 | * Reset the chip on each power off. |
737 | * Should clear out any weird states. | 741 | * Should clear out any weird states. |
@@ -770,6 +774,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
770 | if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) | 774 | if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) |
771 | sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | 775 | sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); |
772 | 776 | ||
777 | out: | ||
773 | mmiowb(); | 778 | mmiowb(); |
774 | spin_unlock_irqrestore(&host->lock, flags); | 779 | spin_unlock_irqrestore(&host->lock, flags); |
775 | } | 780 | } |
@@ -784,7 +789,10 @@ static int sdhci_get_ro(struct mmc_host *mmc) | |||
784 | 789 | ||
785 | spin_lock_irqsave(&host->lock, flags); | 790 | spin_lock_irqsave(&host->lock, flags); |
786 | 791 | ||
787 | present = readl(host->ioaddr + SDHCI_PRESENT_STATE); | 792 | if (host->flags & SDHCI_DEVICE_DEAD) |
793 | present = 0; | ||
794 | else | ||
795 | present = readl(host->ioaddr + SDHCI_PRESENT_STATE); | ||
788 | 796 | ||
789 | spin_unlock_irqrestore(&host->lock, flags); | 797 | spin_unlock_irqrestore(&host->lock, flags); |
790 | 798 | ||
@@ -801,6 +809,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) | |||
801 | 809 | ||
802 | spin_lock_irqsave(&host->lock, flags); | 810 | spin_lock_irqsave(&host->lock, flags); |
803 | 811 | ||
812 | if (host->flags & SDHCI_DEVICE_DEAD) | ||
813 | goto out; | ||
814 | |||
804 | ier = readl(host->ioaddr + SDHCI_INT_ENABLE); | 815 | ier = readl(host->ioaddr + SDHCI_INT_ENABLE); |
805 | 816 | ||
806 | ier &= ~SDHCI_INT_CARD_INT; | 817 | ier &= ~SDHCI_INT_CARD_INT; |
@@ -810,6 +821,7 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) | |||
810 | writel(ier, host->ioaddr + SDHCI_INT_ENABLE); | 821 | writel(ier, host->ioaddr + SDHCI_INT_ENABLE); |
811 | writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); | 822 | writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); |
812 | 823 | ||
824 | out: | ||
813 | mmiowb(); | 825 | mmiowb(); |
814 | 826 | ||
815 | spin_unlock_irqrestore(&host->lock, flags); | 827 | spin_unlock_irqrestore(&host->lock, flags); |
@@ -875,10 +887,11 @@ static void sdhci_tasklet_finish(unsigned long param) | |||
875 | * The controller needs a reset of internal state machines | 887 | * The controller needs a reset of internal state machines |
876 | * upon error conditions. | 888 | * upon error conditions. |
877 | */ | 889 | */ |
878 | if (mrq->cmd->error || | 890 | if (!(host->flags & SDHCI_DEVICE_DEAD) && |
879 | (mrq->data && (mrq->data->error || | 891 | (mrq->cmd->error || |
880 | (mrq->data->stop && mrq->data->stop->error))) || | 892 | (mrq->data && (mrq->data->error || |
881 | (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { | 893 | (mrq->data->stop && mrq->data->stop->error))) || |
894 | (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { | ||
882 | 895 | ||
883 | /* Some controllers need this kick or reset won't work here */ | 896 | /* Some controllers need this kick or reset won't work here */ |
884 | if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { | 897 | if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { |
@@ -1378,15 +1391,34 @@ untasklet: | |||
1378 | 1391 | ||
1379 | EXPORT_SYMBOL_GPL(sdhci_add_host); | 1392 | EXPORT_SYMBOL_GPL(sdhci_add_host); |
1380 | 1393 | ||
1381 | void sdhci_remove_host(struct sdhci_host *host) | 1394 | void sdhci_remove_host(struct sdhci_host *host, int dead) |
1382 | { | 1395 | { |
1396 | unsigned long flags; | ||
1397 | |||
1398 | if (dead) { | ||
1399 | spin_lock_irqsave(&host->lock, flags); | ||
1400 | |||
1401 | host->flags |= SDHCI_DEVICE_DEAD; | ||
1402 | |||
1403 | if (host->mrq) { | ||
1404 | printk(KERN_ERR "%s: Controller removed during " | ||
1405 | " transfer!\n", mmc_hostname(host->mmc)); | ||
1406 | |||
1407 | host->mrq->cmd->error = -ENOMEDIUM; | ||
1408 | tasklet_schedule(&host->finish_tasklet); | ||
1409 | } | ||
1410 | |||
1411 | spin_unlock_irqrestore(&host->lock, flags); | ||
1412 | } | ||
1413 | |||
1383 | mmc_remove_host(host->mmc); | 1414 | mmc_remove_host(host->mmc); |
1384 | 1415 | ||
1385 | #ifdef CONFIG_LEDS_CLASS | 1416 | #ifdef CONFIG_LEDS_CLASS |
1386 | led_classdev_unregister(&host->led); | 1417 | led_classdev_unregister(&host->led); |
1387 | #endif | 1418 | #endif |
1388 | 1419 | ||
1389 | sdhci_reset(host, SDHCI_RESET_ALL); | 1420 | if (!dead) |
1421 | sdhci_reset(host, SDHCI_RESET_ALL); | ||
1390 | 1422 | ||
1391 | free_irq(host->irq, host); | 1423 | free_irq(host->irq, host); |
1392 | 1424 | ||