[PATCH] mmci: handle out-of-order IRQ events
Linus Walleij
linus.walleij at stericsson.com
Thu Sep 16 06:05:34 EDT 2010
On the U300 the MCI_DATAEND and MCI_DATABLOCKEND IRQs can arrive
out-of-order. Replace an ugly #ifdef hack with a proper runtime
solution which models what is really happening.
Also host->data_xfered should be incremented and not assigned
a fixed value as is done in earlier patches I believe.
Signed-off-by: Linus Walleij <linus.walleij at stericsson.com>
---
This patch is broken out of the DMA patch series I'm preparing
for MMCI, and applied on top of the stuff that is already pending
in the patch tracker.
I have regression tested this (on top of Rabins patches) on U300,
U8500 and RealView PB1176.
On a side note I can actually increase the f_max parameter quite
a bit on the PB1176 nowadays, but I don't know if it is only on
this board.
---
drivers/mmc/host/mmci.c | 70 +++++++++++++++++++++++++++++++++++------------
drivers/mmc/host/mmci.h | 3 ++
2 files changed, 55 insertions(+), 18 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 082abad..ae164e1 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -254,20 +254,7 @@ static void
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
unsigned int status)
{
- if (status & MCI_DATABLOCKEND) {
- host->data_xfered += data->blksz;
-#ifdef CONFIG_ARCH_U300
- /*
- * On the U300 some signal or other is
- * badly routed so that a data write does
- * not properly terminate with a MCI_DATAEND
- * status flag. This quirk will make writes
- * work again.
- */
- if (data->flags & MMC_DATA_WRITE)
- status |= MCI_DATAEND;
-#endif
- }
+ /* First check for errors */
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status);
if (status & MCI_DATACRCFAIL)
@@ -276,7 +263,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
data->error = -ETIMEDOUT;
else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
data->error = -EIO;
- status |= MCI_DATAEND;
+
+ /* Force-complete the transaction */
+ host->blockend = true;
+ host->dataend = true;
/*
* We hit an error condition. Ensure that any data
@@ -294,12 +284,56 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
local_irq_restore(flags);
}
}
- if (status & MCI_DATAEND) {
+
+ /*
+ * On ARM variants in PIO mode, MCI_DATABLOCKEND
+ * is always sent first, and we increase the
+ * transfered number of bytes for that IRQ. Then
+ * MCI_DATEND follows and we conclude the transaction.
+ *
+ * The Ux500 single-IRQ variant only fires the
+ * MCI_DATAEND IRQ at the end of the entire transfer.
+ *
+ * In the U300, the IRQs can arrive out-of-order,
+ * e.g. MCI_DATABLOCKEND sometimes arrives after MCI_DATAEND,
+ * so in this case we use the flags "blockend" and
+ * "dataend" to make sure both IRQs have arrived before
+ * concluding the transaction. (This does not apply
+ * to the Ux500 which doesn't fire MCI_DATABLOCKEND
+ * at all.)
+ */
+ if (status & MCI_DATABLOCKEND) {
+ /*
+ * Just being a little over-cautious, we do not
+ * use this progressive update in DMA or single-IRQ
+ * mode.
+ */
+ if (!host->variant->singleirq)
+ host->data_xfered += data->blksz;
+ host->blockend = true;
+ }
+
+ if (status & MCI_DATAEND)
+ host->dataend = true;
+
+ /*
+ * On Ux500 we shall only wait for dataend, on others we must sync
+ * with the blockend signal since they can appear out-of-order.
+ */
+ if (host->dataend && (host->blockend || host->variant->singleirq)) {
mmci_stop_data(host);
- /* MCI_DATABLOCKEND not used with single irq */
+ /* Reset these flags */
+ host->blockend = false;
+ host->dataend = false;
+
+ /*
+ * Single IRQ variants do not transmit MCI_DATABLOCKEND
+ * and in DMA mode this signal need to be synchronized
+ * with MCI_DATEND
+ */
if (host->variant->singleirq && !data->error)
- host->data_xfered = data->blksz * data->blocks;
+ host->data_xfered += data->blksz * data->blocks;
if (!data->stop) {
mmci_request_end(host, data->mrq);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index ba203b8..acac081 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -170,6 +170,9 @@ struct mmci_host {
struct timer_list timer;
unsigned int oldstat;
+ bool blockend;
+ bool dataend;
+
/* pio stuff */
struct sg_mapping_iter sg_miter;
unsigned int size;
--
1.7.2.2
More information about the linux-arm-kernel
mailing list