I haven't managed to produce a hang without this patch see also comments below 2015-06-16 12:37 GMT+02:00 Vignesh R <vigneshr@xxxxxx>: > Usually when there is an error in transfer, DTO/CTO or other error > interrupts are raised. But if the card is unplugged in the middle of a > data transfer, it is observed that, neither completion(success) or > timeout(error) interrupts are raised. Hence, the mmc-core is waiting > for-ever for the transfer to complete. This results failure to recognise > sd card on the next insertion. > The only way to solve this is to introduce code to detect this condition > and recover on card insertion (in hsmmc specific cd_irq). Hence, > introduce cd_irq and add code to clear mmc_request that is pending from > the failed transaction. > > Signed-off-by: Vignesh R <vigneshr@xxxxxx> > --- > drivers/mmc/host/omap_hsmmc.c | 73 ++++++++++++++++++++++++++++++++++- > 1 file changed, 72 insertions(+), 1 deletion(-) > > diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c > index fb4bfefd9250..ec1fff3c0c9c 100644 > --- a/drivers/mmc/host/omap_hsmmc.c > +++ b/drivers/mmc/host/omap_hsmmc.c > @@ -221,6 +221,12 @@ struct omap_hsmmc_host { > #define HSMMC_WAKE_IRQ_ENABLED (1 << 2) > struct omap_hsmmc_next next_data; > struct omap_hsmmc_platform_data *pdata; > + /* > + * flag to determine whether card was removed during data > + * transfer > + */ > + bool transfer_incomplete; > + > > /* return MMC cover switch state, can be NULL if not supported. > * > @@ -867,6 +873,26 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req > } > > /* > + * Cleanup incomplete card removal sequence. This will make sure the > + * next card enumeration is clean. > + */ > +static void omap_hsmmc_request_clear(struct omap_hsmmc_host *host, > + struct mmc_request *mrq) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&host->irq_lock, flags); > + host->req_in_progress = 0; > + host->dma_ch = -1; > + spin_unlock_irqrestore(&host->irq_lock, flags); > + > + mmc_request_done(host->mmc, mrq); > + if (host->mmc->card) > + mmc_card_set_removed(host->mmc->card); > + host->mrq = NULL; > +} > + > +/* > * Notify the transfer complete to MMC core > */ > static void > @@ -1248,6 +1274,47 @@ static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id) > return IRQ_HANDLED; > } > > +/* > + * irq handler to notify the core about card insertion/removal > + */ > +static irqreturn_t omap_hsmmc_cd_irq(int irq, void *dev_id) > +{ Move this code to 'omap_hsmmc_get_cd' function. Or rather clear any pending transfer upon '.init_card/omap_hsmmc_init_card' I guess other hosts also have some housekeeping upon unexpected card removals. So I guess there is some generic code to this problem. Did you check? > + struct omap_hsmmc_host *host = mmc_priv(dev_id); > + int carddetect = mmc_gpio_get_cd(host->mmc); > + struct mmc_request *mrq = host->mrq; > + > + /* > + * If the card was removed in the middle of data transfer last > + * time, the TC/CC/timeout interrupt is not raised due to which > + * mmc_request is not cleared. Hence, this card insertion will > + * still see pending mmc_request. Clear the request to make sure > + * that this card enumeration is successful. > + */ > + if (!carddetect && mrq && host->transfer_incomplete) { > + omap_hsmmc_disable_irq(host); > + dev_info(host->dev, > + "card removed during transfer last time\n"); > + hsmmc_command_incomplete(host, -ENOMEDIUM, 1); > + omap_hsmmc_request_clear(host, host->mrq); > + dev_info(host->dev, "recovery done\n"); > + } > + host->transfer_incomplete = false; > + > + mmc_detect_change(host->mmc, (HZ * 200) / 1000); > + > + /* > + * The current mmc_request is usually null before card removal > + * sequence is complete. It may not be null if TC/CC interrupt > + * never happens due to removal of card during a data > + * transfer. Set a flag to indicate mmc_request was not null > + * in order to do cleanup on next card insertion. > + */ > + if (carddetect && mrq) > + host->transfer_incomplete = true; > + > + return IRQ_HANDLED; > +} > + > static void omap_hsmmc_dma_callback(void *param) > { > struct omap_hsmmc_host *host = param; > @@ -1918,7 +1985,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) > struct mmc_host *mmc; > struct omap_hsmmc_host *host = NULL; > struct resource *res; > - int ret, irq; > + int ret, irq, len; > const struct of_device_id *match; > dma_cap_mask_t mask; > unsigned tx_req, rx_req; > @@ -1980,6 +2047,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) > if (ret) > goto err_gpio; > > + /* register cd_irq, if cd-gpios property is specified in dt */ > + if (of_find_property(host->dev->of_node, "cd-gpios", &len)) > + mmc_gpio_set_cd_isr(mmc, omap_hsmmc_cd_irq); > + nack, this is done by mmc_of_parse > platform_set_drvdata(pdev, host); > > if (pdev->dev.of_node) > -- > 2.4.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in