[PATCH 3/4] mmc: atmel-mci: add support for version lower than v2xx

ludovic.desroches at atmel.com ludovic.desroches at atmel.com
Wed May 16 09:26:00 EDT 2012


From: Ludovic Desroches <ludovic.desroches at atmel.com>

Fix mci IP bugs and endianness issue.

Signed-off-by: Ludovic Desroches <ludovic.desroches at atmel.com>
---
 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




More information about the linux-arm-kernel mailing list