aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2008-04-16 13:13:13 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-07-15 08:14:40 -0400
commit1e72859e3ae16346d4007024b20d2d4ef387dcc3 (patch)
tree5fc8319ce14b0770546bbbf9a72c90abaf019317 /drivers/mmc
parent4489428ab5a49a6f443d9aa17f1d891417787d7b (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')
-rw-r--r--drivers/mmc/host/sdhci-pci.c21
-rw-r--r--drivers/mmc/host/sdhci.c48
-rw-r--r--drivers/mmc/host/sdhci.h3
3 files changed, 58 insertions, 14 deletions
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 5dcb4958e47b..8554466e0f4e 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -47,7 +47,7 @@ struct sdhci_pci_fixes {
47 int (*probe)(struct sdhci_pci_chip*); 47 int (*probe)(struct sdhci_pci_chip*);
48 48
49 int (*probe_slot)(struct sdhci_pci_slot*); 49 int (*probe_slot)(struct sdhci_pci_slot*);
50 void (*remove_slot)(struct sdhci_pci_slot*); 50 void (*remove_slot)(struct sdhci_pci_slot*, int);
51 51
52 int (*suspend)(struct sdhci_pci_chip*, 52 int (*suspend)(struct sdhci_pci_chip*,
53 pm_message_t); 53 pm_message_t);
@@ -209,8 +209,11 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
209 return 0; 209 return 0;
210} 210}
211 211
212static void jmicron_remove_slot(struct sdhci_pci_slot *slot) 212static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead)
213{ 213{
214 if (dead)
215 return;
216
214 if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) 217 if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
215 jmicron_enable_mmc(slot->host, 0); 218 jmicron_enable_mmc(slot->host, 0);
216} 219}
@@ -540,7 +543,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
540 543
541remove: 544remove:
542 if (chip->fixes && chip->fixes->remove_slot) 545 if (chip->fixes && chip->fixes->remove_slot)
543 chip->fixes->remove_slot(slot); 546 chip->fixes->remove_slot(slot, 0);
544 547
545unmap: 548unmap:
546 iounmap(host->ioaddr); 549 iounmap(host->ioaddr);
@@ -554,10 +557,18 @@ release:
554 557
555static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) 558static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
556{ 559{
557 sdhci_remove_host(slot->host); 560 int dead;
561 u32 scratch;
562
563 dead = 0;
564 scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
565 if (scratch == (u32)-1)
566 dead = 1;
567
568 sdhci_remove_host(slot->host, dead);
558 569
559 if (slot->chip->fixes && slot->chip->fixes->remove_slot) 570 if (slot->chip->fixes && slot->chip->fixes->remove_slot)
560 slot->chip->fixes->remove_slot(slot); 571 slot->chip->fixes->remove_slot(slot, dead);
561 572
562 pci_release_region(slot->chip->pdev, slot->pci_bar); 573 pci_release_region(slot->chip->pdev, slot->pci_bar);
563 574
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
777out:
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
824out:
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
1379EXPORT_SYMBOL_GPL(sdhci_add_host); 1392EXPORT_SYMBOL_GPL(sdhci_add_host);
1380 1393
1381void sdhci_remove_host(struct sdhci_host *host) 1394void 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
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 7ce12f3e7452..7c302515a6a5 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -198,6 +198,7 @@ struct sdhci_host {
198 int flags; /* Host attributes */ 198 int flags; /* Host attributes */
199#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */ 199#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */
200#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */ 200#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */
201#define SDHCI_DEVICE_DEAD (1<<2) /* Device unresponsive */
201 202
202 unsigned int max_clk; /* Max possible freq (MHz) */ 203 unsigned int max_clk; /* Max possible freq (MHz) */
203 unsigned int timeout_clk; /* Timeout freq (KHz) */ 204 unsigned int timeout_clk; /* Timeout freq (KHz) */
@@ -239,7 +240,7 @@ static inline void *sdhci_priv(struct sdhci_host *host)
239} 240}
240 241
241extern int sdhci_add_host(struct sdhci_host *host); 242extern int sdhci_add_host(struct sdhci_host *host);
242extern void sdhci_remove_host(struct sdhci_host *host); 243extern void sdhci_remove_host(struct sdhci_host *host, int dead);
243 244
244#ifdef CONFIG_PM 245#ifdef CONFIG_PM
245extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state); 246extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);