diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index eeac479..7913cd8 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -39,7 +39,6 @@ enum { EVENT_STOP_COMPLETE, EVENT_DMA_COMPLETE, EVENT_DMA_ERROR, - EVENT_CARD_DETECT, }; struct atmel_mci_dma { @@ -70,6 +69,9 @@ struct atmel_mci { int detect_pin; int wp_pin; + /* For detect pin debouncing */ + struct timer_list detect_timer; + unsigned long bus_hz; unsigned long mapbase; struct clk *mck; @@ -108,8 +110,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); test_bit(EVENT_DMA_COMPLETE, &host->completed_events) #define mci_dma_error_is_complete(host) \ test_bit(EVENT_DMA_ERROR, &host->completed_events) -#define mci_card_detect_is_complete(host) \ - test_bit(EVENT_CARD_DETECT, &host->completed_events) /* Test and clear bit macros for pending events */ #define mci_clear_cmd_is_pending(host) \ @@ -124,8 +124,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); test_and_clear_bit(EVENT_STOP_COMPLETE, &host->pending_events) #define mci_clear_dma_error_is_pending(host) \ test_and_clear_bit(EVENT_DMA_ERROR, &host->pending_events) -#define mci_clear_card_detect_is_pending(host) \ - test_and_clear_bit(EVENT_CARD_DETECT, &host->pending_events) /* Test and set bit macros for completed events */ #define mci_set_cmd_is_completed(host) \ @@ -140,8 +138,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); test_and_set_bit(EVENT_STOP_COMPLETE, &host->completed_events) #define mci_set_dma_error_is_completed(host) \ test_and_set_bit(EVENT_DMA_ERROR, &host->completed_events) -#define mci_set_card_detect_is_completed(host) \ - test_and_set_bit(EVENT_CARD_DETECT, &host->completed_events) /* Set bit macros for completed events */ #define mci_set_cmd_complete(host) \ @@ -158,8 +154,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); set_bit(EVENT_DMA_COMPLETE, &host->completed_events) #define mci_set_dma_error_complete(host) \ set_bit(EVENT_DMA_ERROR, &host->completed_events) -#define mci_set_card_detect_complete(host) \ - set_bit(EVENT_CARD_DETECT, &host->completed_events) /* Set bit macros for pending events */ #define mci_set_cmd_pending(host) \ @@ -174,8 +168,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); set_bit(EVENT_STOP_COMPLETE, &host->pending_events) #define mci_set_dma_error_pending(host) \ set_bit(EVENT_DMA_ERROR, &host->pending_events) -#define mci_set_card_detect_pending(host) \ - set_bit(EVENT_CARD_DETECT, &host->pending_events) /* Clear bit macros for pending events */ #define mci_clear_cmd_pending(host) \ @@ -190,8 +182,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); clear_bit(EVENT_STOP_COMPLETE, &host->pending_events) #define mci_clear_dma_error_pending(host) \ clear_bit(EVENT_DMA_ERROR, &host->pending_events) -#define mci_clear_card_detect_pending(host) \ - clear_bit(EVENT_CARD_DETECT, &host->pending_events) #ifdef CONFIG_DEBUG_FS @@ -560,6 +550,21 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) mci_readl(host, IMR)); WARN_ON(host->mrq != NULL); + + /* + * We may "know" the card is gone even though there's still an + * electrical connection. If so, we really need to communicate + * this to the MMC core since there won't be any more + * interrupts as the card is completely removed. Otherwise, + * the MMC core might believe the card is still there even + * though the card was just removed very slowly. + */ + if (!host->present) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + } + host->mrq = mrq; host->pending_events = 0; host->completed_events = 0; @@ -729,6 +734,61 @@ static void atmci_command_complete(struct atmel_mci *host, } } +static void atmci_detect_change(unsigned long data) +{ + struct atmel_mci *host = (struct atmel_mci *)data; + struct mmc_request *mrq = host->mrq; + int present; + + /* + * atmci_remove() sets detect_pin to -1 before freeing the + * interrupt. We must not re-enable the interrupt if it has + * been freed. + */ + smp_rmb(); + if (host->detect_pin < 0) + return; + + enable_irq(gpio_to_irq(host->detect_pin)); + present = !gpio_get_value(host->detect_pin); + + dev_vdbg(&host->pdev->dev, "detect change: %d (was %d)\n", + present, host->present); + + if (present != host->present) { + dev_dbg(&host->mmc->class_dev, "card %s\n", + present ? "inserted" : "removed"); + host->present = present; + + /* Reset controller if card is gone */ + if (!present) { + mci_writel(host, CR, MCI_BIT(SWRST)); + mci_writel(host, IDR, ~0UL); + mci_writel(host, CR, MCI_BIT(MCIEN)); + } + + /* Clean up queue if present */ + if (mrq) { + if (!mci_cmd_is_complete(host)) + mrq->cmd->error = -ENOMEDIUM; + if (mrq->data && !mci_data_is_complete(host) + && !mci_data_error_is_complete(host)) { + dma_stop_request(host->dma.req.req.dmac, + host->dma.req.req.channel); + host->data->error = -ENOMEDIUM; + atmci_data_complete(host, host->data); + } + if (mrq->stop && !mci_stop_is_complete(host)) + mrq->stop->error = -ENOMEDIUM; + + host->cmd = NULL; + atmci_request_end(host->mmc, mrq); + } + + mmc_detect_change(host->mmc, 0); + } +} + static void atmci_tasklet_func(unsigned long priv) { struct mmc_host *mmc = (struct mmc_host *)priv; @@ -806,33 +866,6 @@ static void atmci_tasklet_func(unsigned long priv) data->bytes_xfered = data->blocks * data->blksz; atmci_data_complete(host, data); } - if (mci_clear_card_detect_is_pending(host)) { - /* Reset controller if card is gone */ - if (!host->present) { - mci_writel(host, CR, MCI_BIT(SWRST)); - mci_writel(host, IDR, ~0UL); - mci_writel(host, CR, MCI_BIT(MCIEN)); - } - - /* Clean up queue if present */ - if (mrq) { - if (!mci_cmd_is_complete(host)) - mrq->cmd->error = -ETIMEDOUT; - if (mrq->data && !mci_data_is_complete(host) - && !mci_data_error_is_complete(host)) { - dma_stop_request(host->dma.req.req.dmac, - host->dma.req.req.channel); - host->data->error = -ETIMEDOUT; - atmci_data_complete(host, data); - } - if (mrq->stop && !mci_stop_is_complete(host)) - mrq->stop->error = -ETIMEDOUT; - - host->cmd = NULL; - atmci_request_end(mmc, mrq); - } - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); - } } static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status) @@ -957,20 +990,19 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t atmci_detect_change(int irq, void *dev_id) +static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) { struct mmc_host *mmc = dev_id; struct atmel_mci *host = mmc_priv(mmc); - int present = !gpio_get_value(irq_to_gpio(irq)); + /* + * Disable interrupts until the pin has stabilized and check + * the state then. Use mod_timer() since we may be in the + * middle of the timer routine when this interrupt triggers. + */ + disable_irq_nosync(irq); + mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(20)); - if (present != host->present) { - dev_dbg(&mmc->class_dev, "card %s\n", - present ? "inserted" : "removed"); - host->present = present; - mci_set_card_detect_pending(host); - tasklet_schedule(&host->tasklet); - } return IRQ_HANDLED; } @@ -1079,8 +1111,11 @@ static int __devinit atmci_probe(struct platform_device *pdev) mmc_add_host(mmc); if (host->detect_pin >= 0) { + setup_timer(&host->detect_timer, atmci_detect_change, + (unsigned long)host); + ret = request_irq(gpio_to_irq(host->detect_pin), - atmci_detect_change, + atmci_detect_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, DRIVER_NAME, mmc); if (ret) { @@ -1125,9 +1160,16 @@ static int __devexit atmci_remove(struct platform_device *pdev) atmci_cleanup_debugfs(host); if (host->detect_pin >= 0) { - free_irq(gpio_to_irq(host->detect_pin), host->mmc); + int pin = host->detect_pin; + + /* Make sure our timer doesn't enable the interrupt */ + host->detect_pin = -1; + smp_wmb(); + + free_irq(gpio_to_irq(pin), host->mmc); + del_timer_sync(&host->detect_timer); cancel_delayed_work(&host->mmc->detect); - gpio_free(host->detect_pin); + gpio_free(pin); } mmc_remove_host(host->mmc);