From: Ludovic Desroches <[email protected]>

Fix mci IP bugs and endianness issue.

Signed-off-by: Ludovic Desroches <[email protected]>
---
 drivers/mmc/host/atmel-mci.c |   62 +++++++++++++++++++++++++++++++++++++++---
 1 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index cad830f..92c4145 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -78,6 +78,9 @@ struct atmel_mci_caps {
        bool    has_highspeed;
        bool    has_rwproof;
        bool    has_odd_clk_div;
+       bool    has_bad_data_ordering;
+       bool    need_reset_after_xfer;
+       bool    need_blksz_mul_4;
 };
 
 struct atmel_mci_dma {
@@ -121,6 +124,7 @@ struct atmel_mci_dma {
  * @queue: List of slots waiting for access to the controller.
  * @need_clock_update: Update the clock rate before the next request.
  * @need_reset: Reset controller before next request.
+ * @timer: Timer to balance the data timeout error flag which cannot rise.
  * @mode_reg: Value of the MR register.
  * @cfg_reg: Value of the CFG register.
  * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
@@ -197,6 +201,7 @@ struct atmel_mci {
 
        bool                    need_clock_update;
        bool                    need_reset;
+       struct timer_list       timer;
        u32                     mode_reg;
        u32                     cfg_reg;
        unsigned long           bus_hz;
@@ -493,6 +498,27 @@ static inline unsigned int atmci_get_version(struct 
atmel_mci *host)
        return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
 }
 
+static void atmci_timeout_timer(unsigned long data)
+{
+       struct atmel_mci *host;
+
+       host = (struct atmel_mci *)data;
+
+       dev_dbg(&host->pdev->dev, "software timeout\n");
+
+       if (host->mrq->cmd->data) {
+               host->mrq->cmd->data->error = -ETIMEDOUT;
+               host->data = NULL;
+       } else {
+               host->mrq->cmd->error = -ETIMEDOUT;
+               host->cmd = NULL;
+       }
+       host->need_reset = 1;
+       host->state = STATE_END_REQUEST;
+       smp_wmb();
+       tasklet_schedule(&host->tasklet);
+}
+
 static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
                                        unsigned int ns)
 {
@@ -692,13 +718,18 @@ static void atmci_pdc_cleanup(struct atmel_mci *host)
 static void atmci_pdc_complete(struct atmel_mci *host)
 {
        int transfer_size = host->data->blocks * host->data->blksz;
+       int i;
 
        atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
 
        if ((!host->caps.has_rwproof)
-           && (host->data->flags & MMC_DATA_READ))
+           && (host->data->flags & MMC_DATA_READ)) {
+               if (host->caps.has_bad_data_ordering)
+                       for (i = 0; i < transfer_size; i++)
+                               host->buffer[i] = swab32(host->buffer[i]);
                sg_copy_from_buffer(host->data->sg, host->data->sg_len,
                                    host->buffer, transfer_size);
+       }
 
        atmci_pdc_cleanup(host);
 
@@ -818,6 +849,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct 
mmc_data *data)
        u32 iflags, tmp;
        unsigned int sg_len;
        enum dma_data_direction dir;
+       int i;
 
        data->error = -EINPROGRESS;
 
@@ -847,9 +879,13 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct 
mmc_data *data)
        sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir);
 
        if ((!host->caps.has_rwproof)
-           && (host->data->flags & MMC_DATA_WRITE))
+           && (host->data->flags & MMC_DATA_WRITE)) {
                sg_copy_to_buffer(host->data->sg, host->data->sg_len,
                                  host->buffer, host->data_size);
+               if (host->caps.has_bad_data_ordering)
+                       for (i = 0; i < host->data_size; i++)
+                               host->buffer[i] = swab32(host->buffer[i]);
+       }
 
        if (host->data_size)
                atmci_pdc_set_both_buf(host,
@@ -1014,7 +1050,7 @@ static void atmci_start_request(struct atmel_mci *host,
        host->cmd_status = 0;
        host->data_status = 0;
 
-       if (host->need_reset) {
+       if (host->need_reset || host->caps.need_reset_after_xfer) {
                iflags = atmci_readl(host, ATMCI_IMR);
                iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB);
                atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
@@ -1078,6 +1114,8 @@ static void atmci_start_request(struct atmel_mci *host,
         * prepared yet.)
         */
        atmci_writel(host, ATMCI_IER, iflags);
+
+       mod_timer(&host->timer, jiffies +  msecs_to_jiffies(2000));
 }
 
 static void atmci_queue_request(struct atmel_mci *host,
@@ -1343,6 +1381,8 @@ static void atmci_request_end(struct atmel_mci *host, 
struct mmc_request *mrq)
                host->state = STATE_IDLE;
        }
 
+       del_timer(&host->timer);
+
        spin_unlock(&host->lock);
        mmc_request_done(prev_mmc, mrq);
        spin_lock(&host->lock);
@@ -1365,7 +1405,12 @@ static void atmci_command_complete(struct atmel_mci 
*host,
                cmd->error = -EILSEQ;
        else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
                cmd->error = -EIO;
-       else
+       else if (host->mrq->data && (host->mrq->data->blksz & 3)) {
+               if (host->caps.need_blksz_mul_4) {
+                       cmd->error = -EINVAL;
+                       host->need_reset = 1;
+               }
+       } else
                cmd->error = 0;
 }
 
@@ -2122,6 +2167,9 @@ static void __init atmci_get_cap(struct atmel_mci *host)
        host->caps.has_highspeed = 0;
        host->caps.has_rwproof = 0;
        host->caps.has_odd_clk_div = 0;
+       host->caps.has_bad_data_ordering = 1;
+       host->caps.need_reset_after_xfer = 1;
+       host->caps.need_blksz_mul_4 = 1;
 
        /* keep only major version number */
        switch (version & 0xf00) {
@@ -2141,7 +2189,11 @@ static void __init atmci_get_cap(struct atmel_mci *host)
                host->caps.has_highspeed = 1;
        case 0x200:
                host->caps.has_rwproof = 1;
+               host->caps.need_blksz_mul_4 = 0;
        case 0x100:
+               host->caps.has_bad_data_ordering = 0;
+               host->caps.need_reset_after_xfer = 0;
+       case 0x0:
                break;
        default:
                host->caps.has_pdc = 0;
@@ -2260,6 +2312,8 @@ static int __init atmci_probe(struct platform_device 
*pdev)
                }
        }
 
+       setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
+
        dev_info(&pdev->dev,
                        "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
                        host->mapbase, irq, nr_slots);
-- 
1.7.5.4

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to