public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
@ 2007-07-02 12:16 Nicolas Ferre
  2007-07-02 12:45 ` Marc Pignat
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Nicolas Ferre @ 2007-07-02 12:16 UTC (permalink / raw)
  To: Pierre Ossman, ARM Linux Mailing List
  Cc: Linux Kernel list, Andrew Victor, Andreas Beier, Hamish Guthrie,
	Marc Pignat, wux, Patrice Vilchez

Fixes hanging using multi block operations (seen during CMD25).
Follows closely the datasheet flowcharts.

This piece of code handles better big file writing. I had to take 
care of the notbusy signal during write (at91_mci_handle_cmdrdy 
function) and to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF 
flag usage.

Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
---
This patch is applied on top of the "typo" one :
http://kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.22-rc6/2.6.22-rc6-mm1/broken-out/mmc-at91_mci-typo.patch

It is cooked with the help of Wu Xuan and Marc Pignat. Big thanks to 
them.

It has been tested on several systems and reported to work ok :
at91rm9200 ok w/ 1 wire
at91sam9261 ok w/ 1 wire
at91sam926[03] ok w/ 4 wires
The code takes care of those specificities.

 drivers/mmc/host/at91_mci.c |  213 ++++++++++++++++++----------------
 1 file changed, 118 insertions(+), 95 deletions(-)

Index: b/drivers/mmc/host/at91_mci.c
===================================================================
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -80,8 +78,6 @@
 
 #define DRIVER_NAME "at91_mci"
 
-#undef	SUPPORT_4WIRE
-
 #define FL_SENT_COMMAND	(1 << 0)
 #define FL_SENT_STOP	(1 << 1)
 
@@ -250,28 +246,27 @@ static void at91_mci_pre_dma_read(struct
 /*
  * Handle after a dma read
  */
-static void at91_mci_post_dma_read(struct at91mci_host *host)
+static int at91_mci_post_dma_read(struct at91mci_host *host)
 {
 	struct mmc_command *cmd;
 	struct mmc_data *data;
+	int completed = 0;
 
 	pr_debug("post dma read\n");
 
 	cmd = host->cmd;
 	if (!cmd) {
 		pr_debug("no command\n");
-		return;
+		return 1;
 	}
 
 	data = cmd->data;
 	if (!data) {
 		pr_debug("no data\n");
-		return;
+		return 1;
 	}
 
 	while (host->in_use_index < host->transfer_index) {
-		unsigned int *buffer;
-
 		struct scatterlist *sg;
 
 		pr_debug("finishing index %d\n", host->in_use_index);
@@ -282,20 +277,22 @@ static void at91_mci_post_dma_read(struc
 
 		dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
 
-		/* Swap the contents of the buffer */
-		buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
-		pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
-
 		data->bytes_xfered += sg->length;
 
 		if (cpu_is_at91rm9200()) {	/* AT91RM9200 errata */
+			unsigned int *buffer;
 			int index;
 
+			/* Swap the contents of the buffer */
+			buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+			pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
+
 			for (index = 0; index < (sg->length / 4); index++)
 				buffer[index] = swab32(buffer[index]);
+
+			kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
 		}
 
-		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
 		flush_dcache_page(sg->page);
 	}
 
@@ -303,11 +300,12 @@ static void at91_mci_post_dma_read(struc
 	if (host->transfer_index < data->sg_len)
 		at91_mci_pre_dma_read(host);
 	else {
+		at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
 		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
-		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
 	}
 
 	pr_debug("post dma read done\n");
+	return completed;
 }
 
 /*
@@ -325,7 +323,6 @@ static void at91_mci_handle_transmitted(
 
 	/* Now wait for cmd ready */
 	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
-	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
 
 	cmd = host->cmd;
 	if (!cmd) return;
@@ -333,18 +330,53 @@ static void at91_mci_handle_transmitted(
 	data = cmd->data;
 	if (!data) return;
 
+	if (cmd->data->flags & MMC_DATA_MULTI) {
+		pr_debug("multiple write : wait for BLKE...\n");
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
+	} else
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+
 	data->bytes_xfered = host->total_length;
 }
 
+/*Handle after command sent ready*/
+static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
+{
+	if (!host->cmd)
+		return 1;
+	else if (!host->cmd->data) {
+		if (host->flags & FL_SENT_STOP) {
+			/*After multi block write, we must wait for NOTBUSY*/
+			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+		} else return 1;
+	} else if (host->cmd->data->flags & MMC_DATA_WRITE) {
+		/*After sendding multi-block-write command, start DMA transfer*/
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE);
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
+		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+	}
+
+	/* command not completed, have to wait */
+	return 0;
+}
+
+
 /*
  * Enable the controller
  */
 static void at91_mci_enable(struct at91mci_host *host)
 {
+	unsigned int mr;
+
 	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
 	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
 	at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
-	at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a);
+	mr = AT91_MCI_PDCMODE | 0x34a;
+
+	if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+		mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
+
+	at91_mci_write(host, AT91_MCI_MR, mr);
 
 	/* use Slot A or B (only one at same time) */
 	at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
@@ -360,9 +392,8 @@ static void at91_mci_disable(struct at91
 
 /*
  * Send a command
- * return the interrupts to enable
  */
-static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
+static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
 {
 	unsigned int cmdr, mr;
 	unsigned int block_length;
@@ -373,8 +404,7 @@ static unsigned int at91_mci_send_comman
 
 	host->cmd = cmd;
 
-	/* Not sure if this is needed */
-#if 0
+	/* Needed for leaving busy state before CMD1 */
 	if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
 		pr_debug("Clearing timeout\n");
 		at91_mci_write(host, AT91_MCI_ARGR, 0);
@@ -384,7 +414,7 @@ static unsigned int at91_mci_send_comman
 			pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
 		}
 	}
-#endif
+
 	cmdr = cmd->opcode;
 
 	if (mmc_resp_type(cmd) == MMC_RSP_NONE)
@@ -441,50 +471,48 @@ static unsigned int at91_mci_send_comman
 		at91_mci_write(host, ATMEL_PDC_TCR, 0);
 		at91_mci_write(host, ATMEL_PDC_TNPR, 0);
 		at91_mci_write(host, ATMEL_PDC_TNCR, 0);
+		ier = AT91_MCI_CMDRDY;
+	} else {
+		/* zero block length and PDC mode */
+		mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
+		at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+
+		/*
+		 * Disable the PDC controller
+		 */
+		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
 
-		at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
-		at91_mci_write(host, AT91_MCI_CMDR, cmdr);
-		return AT91_MCI_CMDRDY;
-	}
-
-	mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;	/* zero block length and PDC mode */
-	at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
-
-	/*
-	 * Disable the PDC controller
-	 */
-	at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
-
-	if (cmdr & AT91_MCI_TRCMD_START) {
-		data->bytes_xfered = 0;
-		host->transfer_index = 0;
-		host->in_use_index = 0;
-		if (cmdr & AT91_MCI_TRDIR) {
-			/*
-			 * Handle a read
-			 */
-			host->buffer = NULL;
-			host->total_length = 0;
+		if (cmdr & AT91_MCI_TRCMD_START) {
+			data->bytes_xfered = 0;
+			host->transfer_index = 0;
+			host->in_use_index = 0;
+			if (cmdr & AT91_MCI_TRDIR) {
+				/*
+				 * Handle a read
+				 */
+				host->buffer = NULL;
+				host->total_length = 0;
 
-			at91_mci_pre_dma_read(host);
-			ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
-		}
-		else {
-			/*
-			 * Handle a write
-			 */
-			host->total_length = block_length * blocks;
-			host->buffer = dma_alloc_coherent(NULL,
-						  host->total_length,
-						  &host->physical_address, GFP_KERNEL);
-
-			at91_mci_sg_to_dma(host, data);
-
-			pr_debug("Transmitting %d bytes\n", host->total_length);
-
-			at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
-			at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
-			ier = AT91_MCI_TXBUFE;
+				at91_mci_pre_dma_read(host);
+				ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
+			}
+			else {
+				/*
+				 * Handle a write
+				 */
+				host->total_length = block_length * blocks;
+				host->buffer = dma_alloc_coherent(NULL,
+						host->total_length,
+						&host->physical_address, GFP_KERNEL);
+
+				at91_mci_sg_to_dma(host, data);
+
+				pr_debug("Transmitting %d bytes\n", host->total_length);
+
+				at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
+				at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
+				ier = AT91_MCI_CMDRDY;
+			}
 		}
 	}
 
@@ -499,24 +527,9 @@ static unsigned int at91_mci_send_comman
 	if (cmdr & AT91_MCI_TRCMD_START) {
 		if (cmdr & AT91_MCI_TRDIR)
 			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
-		else
-			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
 	}
-	return ier;
-}
 
-/*
- * Wait for a command to complete
- */
-static void at91_mci_process_command(struct at91mci_host *host, struct mmc_command *cmd)
-{
-	unsigned int ier;
-
-	ier = at91_mci_send_command(host, cmd);
-
-	pr_debug("setting ier to %08X\n", ier);
-
-	/* Stop on errors or the required value */
+	/* Enable selected interrupts */
 	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
 }
 
@@ -527,11 +540,11 @@ static void at91_mci_process_next(struct
 {
 	if (!(host->flags & FL_SENT_COMMAND)) {
 		host->flags |= FL_SENT_COMMAND;
-		at91_mci_process_command(host, host->request->cmd);
+		at91_mci_send_command(host, host->request->cmd);
 	}
 	else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
 		host->flags |= FL_SENT_STOP;
-		at91_mci_process_command(host, host->request->stop);
+		at91_mci_send_command(host, host->request->stop);
 	}
 	else
 		mmc_request_done(host->mmc, host->request);
@@ -698,29 +711,33 @@ static irqreturn_t at91_mci_irq(int irq,
 			at91_mci_handle_transmitted(host);
 		}
 
+		if (int_status & AT91_MCI_ENDRX) {
+			pr_debug("ENDRX\n");
+			at91_mci_post_dma_read(host);
+		}
+
 		if (int_status & AT91_MCI_RXBUFF) {
 			pr_debug("RX buffer full\n");
-			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+			at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX);
+			completed = 1;
 		}
 
 		if (int_status & AT91_MCI_ENDTX)
 			pr_debug("Transmit has ended\n");
 
-		if (int_status & AT91_MCI_ENDRX) {
-			pr_debug("Receive has ended\n");
-			at91_mci_post_dma_read(host);
-		}
-
 		if (int_status & AT91_MCI_NOTBUSY) {
 			pr_debug("Card is ready\n");
-			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+			completed = 1;
 		}
 
 		if (int_status & AT91_MCI_DTIP)
 			pr_debug("Data transfer in progress\n");
 
-		if (int_status & AT91_MCI_BLKE)
+		if (int_status & AT91_MCI_BLKE) {
 			pr_debug("Block transfer has ended\n");
+			completed = 1;
+		}
 
 		if (int_status & AT91_MCI_TXRDY)
 			pr_debug("Ready to transmit\n");
@@ -730,7 +747,7 @@ static irqreturn_t at91_mci_irq(int irq,
 
 		if (int_status & AT91_MCI_CMDRDY) {
 			pr_debug("Command ready\n");
-			completed = 1;
+			completed = at91_mci_handle_cmdrdy(host);
 		}
 	}
 
@@ -817,7 +834,11 @@ static int __init at91_mci_probe(struct 
 
 	mmc->ops = &at91_mci_ops;
 	mmc->f_min = 375000;
-	mmc->f_max = 25000000;
+	if (cpu_is_at91sam9263())
+		mmc->f_max = 50000000;
+	else
+		mmc->f_max = 25000000;
+
 	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 	mmc->caps = MMC_CAP_BYTEBLOCK | MMC_CAP_MULTIWRITE;
 
@@ -830,11 +851,11 @@ static int __init at91_mci_probe(struct 
 	host->bus_mode = 0;
 	host->board = pdev->dev.platform_data;
 	if (host->board->wire4) {
-#ifdef SUPPORT_4WIRE
-		mmc->caps |= MMC_CAP_4_BIT_DATA;
-#else
-		printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
-#endif
+		if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+		else
+			printk("AT91 MMC: 4 wire bus mode not supported"
+				" - using 1 wire\n");
 	}
 
 	/*





^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-02 12:16 [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts Nicolas Ferre
@ 2007-07-02 12:45 ` Marc Pignat
  2007-07-02 13:32 ` Pierre Ossman
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Marc Pignat @ 2007-07-02 12:45 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: Pierre Ossman, ARM Linux Mailing List, Linux Kernel list,
	Andrew Victor, Andreas Beier, Hamish Guthrie, wux,
	Patrice Vilchez

On Monday 02 July 2007 14:16, Nicolas Ferre wrote:
> Fixes hanging using multi block operations (seen during CMD25).
> Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
Verified working on at91rm9200 using 1 wire mode.

A small remark, only for future improvements:
> @@ -817,7 +834,11 @@ static int __init at91_mci_probe(struct 
>  
>  	mmc->ops = &at91_mci_ops;
>  	mmc->f_min = 375000;
> -	mmc->f_max = 25000000;
> +	if (cpu_is_at91sam9263())
> +		mmc->f_max = 50000000;
> +	else
> +		mmc->f_max = 25000000;
for at91rm9200 f_max is MCK/2 why don't use it?

Nice work, thanks

Regards

Marc

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-02 12:16 [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts Nicolas Ferre
  2007-07-02 12:45 ` Marc Pignat
@ 2007-07-02 13:32 ` Pierre Ossman
  2007-07-09 10:51   ` Pierre Ossman
  2007-07-09 12:48   ` Nicolas Ferre
  2007-07-02 19:13 ` Mariusz Kozlowski
  2007-07-09 12:58 ` Nicolas Ferre
  3 siblings, 2 replies; 8+ messages in thread
From: Pierre Ossman @ 2007-07-02 13:32 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: ARM Linux Mailing List, Linux Kernel list, Andrew Victor,
	Andreas Beier, Hamish Guthrie, Marc Pignat, wux, Patrice Vilchez

Nicolas Ferre wrote:
> Fixes hanging using multi block operations (seen during CMD25).
> Follows closely the datasheet flowcharts.
> 
> This piece of code handles better big file writing. I had to take care
> of the notbusy signal during write (at91_mci_handle_cmdrdy function) and
> to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF flag usage.
> 
> Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
> ---

Most of the patch looks ok. Do you want to wait for some more tests or should I
chuck this into the imminent merge window?

> @@ -817,7 +834,11 @@ static int __init at91_mci_probe(struct
>     mmc->ops = &at91_mci_ops;
>     mmc->f_min = 375000;
> -    mmc->f_max = 25000000;
> +    if (cpu_is_at91sam9263())
> +        mmc->f_max = 50000000;
> +    else
> +        mmc->f_max = 25000000;
> +
>     mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
>     mmc->caps = MMC_CAP_BYTEBLOCK | MMC_CAP_MULTIWRITE;
> 

This seems unrelated to the rest of the patch. Also, high-speed won't be enabled
unless you set the appropriate caps (which should be checked against timing
specifications, not just assigned and hope for the best).

> @@ -830,11 +851,11 @@ static int __init at91_mci_probe(struct
>     host->bus_mode = 0;
>     host->board = pdev->dev.platform_data;
>     if (host->board->wire4) {
> -#ifdef SUPPORT_4WIRE
> -        mmc->caps |= MMC_CAP_4_BIT_DATA;
> -#else
> -        printk("AT91 MMC: 4 wire bus mode not supported by this driver
> - using 1 wire\n");
> -#endif
> +        if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
> +            mmc->caps |= MMC_CAP_4_BIT_DATA;
> +        else
> +            printk("AT91 MMC: 4 wire bus mode not supported"
> +                " - using 1 wire\n");
>     }
> 
>     /*
> 

This also looks unrelated.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-02 12:16 [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts Nicolas Ferre
  2007-07-02 12:45 ` Marc Pignat
  2007-07-02 13:32 ` Pierre Ossman
@ 2007-07-02 19:13 ` Mariusz Kozlowski
  2007-07-09 12:58 ` Nicolas Ferre
  3 siblings, 0 replies; 8+ messages in thread
From: Mariusz Kozlowski @ 2007-07-02 19:13 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: Pierre Ossman, ARM Linux Mailing List, Linux Kernel list,
	Andrew Victor, Andreas Beier, Hamish Guthrie, Marc Pignat, wux,
	Patrice Vilchez

Hello,

> @@ -250,28 +246,27 @@ static void at91_mci_pre_dma_read(struct
>  /*
>   * Handle after a dma read
>   */
> -static void at91_mci_post_dma_read(struct at91mci_host *host)
> +static int at91_mci_post_dma_read(struct at91mci_host *host)
>  {
>         struct mmc_command *cmd;
>         struct mmc_data *data;
> +       int completed = 0;

You added 'completed' but you use it only once - as a return value.

>         pr_debug("post dma read done\n");
> +       return completed;
> }

What's the point?

	Mariusz

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-02 13:32 ` Pierre Ossman
@ 2007-07-09 10:51   ` Pierre Ossman
  2007-07-09 12:48   ` Nicolas Ferre
  1 sibling, 0 replies; 8+ messages in thread
From: Pierre Ossman @ 2007-07-09 10:51 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: wux, Linux Kernel list, Hamish Guthrie, Andrew Victor,
	ARM Linux Mailing List

On Mon, 02 Jul 2007 15:32:00 +0200
Pierre Ossman <drzeus-list@drzeus.cx> wrote:

> Nicolas Ferre wrote:
> > Fixes hanging using multi block operations (seen during CMD25).
> > Follows closely the datasheet flowcharts.
> > 
> > This piece of code handles better big file writing. I had to take
> > care of the notbusy signal during write (at91_mci_handle_cmdrdy
> > function) and to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF
> > flag usage.
> > 
> > Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
> > ---
> 
> Most of the patch looks ok. Do you want to wait for some more tests
> or should I chuck this into the imminent merge window?
> 

*ping*

-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-02 13:32 ` Pierre Ossman
  2007-07-09 10:51   ` Pierre Ossman
@ 2007-07-09 12:48   ` Nicolas Ferre
  1 sibling, 0 replies; 8+ messages in thread
From: Nicolas Ferre @ 2007-07-09 12:48 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: ARM Linux Mailing List, Linux Kernel list, Andrew Victor,
	Andreas Beier, Hamish Guthrie, Marc Pignat, wux, Patrice Vilchez

Pierre Ossman :
> Nicolas Ferre wrote:
>> Fixes hanging using multi block operations (seen during CMD25).
>> Follows closely the datasheet flowcharts.
>>
>> This piece of code handles better big file writing. I had to take care
>> of the notbusy signal during write (at91_mci_handle_cmdrdy function) and
>> to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF flag usage.
>>
>> Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
>> ---
> 
> Most of the patch looks ok. Do you want to wait for some more tests or should I
> chuck this into the imminent merge window?

*pong*

I think it is ok to put it into the merge window.

>> @@ -817,7 +834,11 @@ static int __init at91_mci_probe(struct
>>     mmc->ops = &at91_mci_ops;
>>     mmc->f_min = 375000;
>> -    mmc->f_max = 25000000;
>> +    if (cpu_is_at91sam9263())
>> +        mmc->f_max = 50000000;
>> +    else
>> +        mmc->f_max = 25000000;
>> +
>>     mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
>>     mmc->caps = MMC_CAP_BYTEBLOCK | MMC_CAP_MULTIWRITE;
>>
> 
> This seems unrelated to the rest of the patch. Also, high-speed won't be enabled
> unless you set the appropriate caps (which should be checked against timing
> specifications, not just assigned and hope for the best).

True. I remove this chunk off the patch.


>> @@ -830,11 +851,11 @@ static int __init at91_mci_probe(struct
>>     host->bus_mode = 0;
>>     host->board = pdev->dev.platform_data;
>>     if (host->board->wire4) {
>> -#ifdef SUPPORT_4WIRE
>> -        mmc->caps |= MMC_CAP_4_BIT_DATA;
>> -#else
>> -        printk("AT91 MMC: 4 wire bus mode not supported by this driver
>> - using 1 wire\n");
>> -#endif
>> +        if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
>> +            mmc->caps |= MMC_CAP_4_BIT_DATA;
>> +        else
>> +            printk("AT91 MMC: 4 wire bus mode not supported"
>> +                " - using 1 wire\n");
>>     }
>>
>>     /*
>>
> 
> This also looks unrelated.

Well, It is related to capacities of newer IPs that allow readproof and 
writeproof features that I enable here :

+	if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+		mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;

This allows to have a smooth data transmission internally (prevent 
underruns) and so have full 4 wires capacity. So I think this is good 
for having a full featured driver on all chips.

I resend a corrected patch now.

Regards,
-- 
Nicolas Ferre



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-02 12:16 [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts Nicolas Ferre
                   ` (2 preceding siblings ...)
  2007-07-02 19:13 ` Mariusz Kozlowski
@ 2007-07-09 12:58 ` Nicolas Ferre
  2007-07-09 16:27   ` Pierre Ossman
  3 siblings, 1 reply; 8+ messages in thread
From: Nicolas Ferre @ 2007-07-09 12:58 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Nicolas Ferre, ARM Linux Mailing List, wux, Linux Kernel list,
	Hamish Guthrie, Andrew Victor, Marc Pignat, m.kozlowski

Fixes hanging using multi block operations (seen during CMD25).
Follows closely the datasheet flowcharts.

This piece of code handles better big file writing. I had to take care 
of the notbusy signal during write (at91_mci_handle_cmdrdy function) and 
to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF flag usage.

Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
---
This second patch is rebuild following Mariusz and Pierre comments.

This patch is applied on top of the "typo" one :
http://kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.22-rc6/2.6.22-rc6-mm1/broken-out/mmc-at91_mci-typo.patch 

It is cooked with the help of Wu Xuan and Marc Pignat. Big thanks to them.

It has been tested on several systems and reported to work ok :
at91rm9200 ok w/ 1 wire
at91sam9261 ok w/ 1 wire
at91sam926[03] ok w/ 4 wires
The code takes care of those specificities.

 drivers/mmc/host/at91_mci.c |  199 ++++++++++++++++++----------------
 1 file changed, 108 insertions(+), 91 deletions(-)

Index: b/drivers/mmc/host/at91_mci.c
===================================================================
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -80,8 +78,6 @@
 
 #define DRIVER_NAME "at91_mci"
 
-#undef	SUPPORT_4WIRE
-
 #define FL_SENT_COMMAND	(1 << 0)
 #define FL_SENT_STOP	(1 << 1)
 
@@ -270,8 +266,6 @@ static void at91_mci_post_dma_read(struc
 	}
 
 	while (host->in_use_index < host->transfer_index) {
-		unsigned int *buffer;
-
 		struct scatterlist *sg;
 
 		pr_debug("finishing index %d\n", host->in_use_index);
@@ -282,20 +276,22 @@ static void at91_mci_post_dma_read(struc
 
 		dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
 
-		/* Swap the contents of the buffer */
-		buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
-		pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
-
 		data->bytes_xfered += sg->length;
 
 		if (cpu_is_at91rm9200()) {	/* AT91RM9200 errata */
+			unsigned int *buffer;
 			int index;
 
+			/* Swap the contents of the buffer */
+			buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+			pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
+
 			for (index = 0; index < (sg->length / 4); index++)
 				buffer[index] = swab32(buffer[index]);
+
+			kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
 		}
 
-		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
 		flush_dcache_page(sg->page);
 	}
 
@@ -303,8 +299,8 @@ static void at91_mci_post_dma_read(struc
 	if (host->transfer_index < data->sg_len)
 		at91_mci_pre_dma_read(host);
 	else {
+		at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
 		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
-		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
 	}
 
 	pr_debug("post dma read done\n");
@@ -325,7 +321,6 @@ static void at91_mci_handle_transmitted(
 
 	/* Now wait for cmd ready */
 	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
-	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
 
 	cmd = host->cmd;
 	if (!cmd) return;
@@ -333,18 +328,53 @@ static void at91_mci_handle_transmitted(
 	data = cmd->data;
 	if (!data) return;
 
+	if (cmd->data->flags & MMC_DATA_MULTI) {
+		pr_debug("multiple write : wait for BLKE...\n");
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
+	} else
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+
 	data->bytes_xfered = host->total_length;
 }
 
+/*Handle after command sent ready*/
+static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
+{
+	if (!host->cmd)
+		return 1;
+	else if (!host->cmd->data) {
+		if (host->flags & FL_SENT_STOP) {
+			/*After multi block write, we must wait for NOTBUSY*/
+			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+		} else return 1;
+	} else if (host->cmd->data->flags & MMC_DATA_WRITE) {
+		/*After sendding multi-block-write command, start DMA transfer*/
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE);
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
+		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+	}
+
+	/* command not completed, have to wait */
+	return 0;
+}
+
+
 /*
  * Enable the controller
  */
 static void at91_mci_enable(struct at91mci_host *host)
 {
+	unsigned int mr;
+
 	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
 	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
 	at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
-	at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a);
+	mr = AT91_MCI_PDCMODE | 0x34a;
+
+	if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+		mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
+
+	at91_mci_write(host, AT91_MCI_MR, mr);
 
 	/* use Slot A or B (only one at same time) */
 	at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
@@ -360,9 +390,8 @@ static void at91_mci_disable(struct at91
 
 /*
  * Send a command
- * return the interrupts to enable
  */
-static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
+static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
 {
 	unsigned int cmdr, mr;
 	unsigned int block_length;
@@ -373,8 +402,7 @@ static unsigned int at91_mci_send_comman
 
 	host->cmd = cmd;
 
-	/* Not sure if this is needed */
-#if 0
+	/* Needed for leaving busy state before CMD1 */
 	if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
 		pr_debug("Clearing timeout\n");
 		at91_mci_write(host, AT91_MCI_ARGR, 0);
@@ -384,7 +412,7 @@ static unsigned int at91_mci_send_comman
 			pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
 		}
 	}
-#endif
+
 	cmdr = cmd->opcode;
 
 	if (mmc_resp_type(cmd) == MMC_RSP_NONE)
@@ -441,50 +469,48 @@ static unsigned int at91_mci_send_comman
 		at91_mci_write(host, ATMEL_PDC_TCR, 0);
 		at91_mci_write(host, ATMEL_PDC_TNPR, 0);
 		at91_mci_write(host, ATMEL_PDC_TNCR, 0);
+		ier = AT91_MCI_CMDRDY;
+	} else {
+		/* zero block length and PDC mode */
+		mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
+		at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+
+		/*
+		 * Disable the PDC controller
+		 */
+		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
 
-		at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
-		at91_mci_write(host, AT91_MCI_CMDR, cmdr);
-		return AT91_MCI_CMDRDY;
-	}
-
-	mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;	/* zero block length and PDC mode */
-	at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+		if (cmdr & AT91_MCI_TRCMD_START) {
+			data->bytes_xfered = 0;
+			host->transfer_index = 0;
+			host->in_use_index = 0;
+			if (cmdr & AT91_MCI_TRDIR) {
+				/*
+				 * Handle a read
+				 */
+				host->buffer = NULL;
+				host->total_length = 0;
 
-	/*
-	 * Disable the PDC controller
-	 */
-	at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
-
-	if (cmdr & AT91_MCI_TRCMD_START) {
-		data->bytes_xfered = 0;
-		host->transfer_index = 0;
-		host->in_use_index = 0;
-		if (cmdr & AT91_MCI_TRDIR) {
-			/*
-			 * Handle a read
-			 */
-			host->buffer = NULL;
-			host->total_length = 0;
-
-			at91_mci_pre_dma_read(host);
-			ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
-		}
-		else {
-			/*
-			 * Handle a write
-			 */
-			host->total_length = block_length * blocks;
-			host->buffer = dma_alloc_coherent(NULL,
-						  host->total_length,
-						  &host->physical_address, GFP_KERNEL);
-
-			at91_mci_sg_to_dma(host, data);
-
-			pr_debug("Transmitting %d bytes\n", host->total_length);
-
-			at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
-			at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
-			ier = AT91_MCI_TXBUFE;
+				at91_mci_pre_dma_read(host);
+				ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
+			}
+			else {
+				/*
+				 * Handle a write
+				 */
+				host->total_length = block_length * blocks;
+				host->buffer = dma_alloc_coherent(NULL,
+						host->total_length,
+						&host->physical_address, GFP_KERNEL);
+
+				at91_mci_sg_to_dma(host, data);
+
+				pr_debug("Transmitting %d bytes\n", host->total_length);
+
+				at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
+				at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
+				ier = AT91_MCI_CMDRDY;
+			}
 		}
 	}
 
@@ -499,24 +525,9 @@ static unsigned int at91_mci_send_comman
 	if (cmdr & AT91_MCI_TRCMD_START) {
 		if (cmdr & AT91_MCI_TRDIR)
 			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
-		else
-			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
 	}
-	return ier;
-}
 
-/*
- * Wait for a command to complete
- */
-static void at91_mci_process_command(struct at91mci_host *host, struct mmc_command *cmd)
-{
-	unsigned int ier;
-
-	ier = at91_mci_send_command(host, cmd);
-
-	pr_debug("setting ier to %08X\n", ier);
-
-	/* Stop on errors or the required value */
+	/* Enable selected interrupts */
 	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
 }
 
@@ -527,11 +538,11 @@ static void at91_mci_process_next(struct
 {
 	if (!(host->flags & FL_SENT_COMMAND)) {
 		host->flags |= FL_SENT_COMMAND;
-		at91_mci_process_command(host, host->request->cmd);
+		at91_mci_send_command(host, host->request->cmd);
 	}
 	else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
 		host->flags |= FL_SENT_STOP;
-		at91_mci_process_command(host, host->request->stop);
+		at91_mci_send_command(host, host->request->stop);
 	}
 	else
 		mmc_request_done(host->mmc, host->request);
@@ -698,29 +709,33 @@ static irqreturn_t at91_mci_irq(int irq,
 			at91_mci_handle_transmitted(host);
 		}
 
+		if (int_status & AT91_MCI_ENDRX) {
+			pr_debug("ENDRX\n");
+			at91_mci_post_dma_read(host);
+		}
+
 		if (int_status & AT91_MCI_RXBUFF) {
 			pr_debug("RX buffer full\n");
-			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+			at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX);
+			completed = 1;
 		}
 
 		if (int_status & AT91_MCI_ENDTX)
 			pr_debug("Transmit has ended\n");
 
-		if (int_status & AT91_MCI_ENDRX) {
-			pr_debug("Receive has ended\n");
-			at91_mci_post_dma_read(host);
-		}
-
 		if (int_status & AT91_MCI_NOTBUSY) {
 			pr_debug("Card is ready\n");
-			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+			completed = 1;
 		}
 
 		if (int_status & AT91_MCI_DTIP)
 			pr_debug("Data transfer in progress\n");
 
-		if (int_status & AT91_MCI_BLKE)
+		if (int_status & AT91_MCI_BLKE) {
 			pr_debug("Block transfer has ended\n");
+			completed = 1;
+		}
 
 		if (int_status & AT91_MCI_TXRDY)
 			pr_debug("Ready to transmit\n");
@@ -730,7 +745,7 @@ static irqreturn_t at91_mci_irq(int irq,
 
 		if (int_status & AT91_MCI_CMDRDY) {
 			pr_debug("Command ready\n");
-			completed = 1;
+			completed = at91_mci_handle_cmdrdy(host);
 		}
 	}
 
@@ -830,11 +845,11 @@ static int __init at91_mci_probe(struct 
 	host->bus_mode = 0;
 	host->board = pdev->dev.platform_data;
 	if (host->board->wire4) {
-#ifdef SUPPORT_4WIRE
-		mmc->caps |= MMC_CAP_4_BIT_DATA;
-#else
-		printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
-#endif
+		if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+		else
+			printk("AT91 MMC: 4 wire bus mode not supported"
+				" - using 1 wire\n");
 	}
 
 	/*



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts
  2007-07-09 12:58 ` Nicolas Ferre
@ 2007-07-09 16:27   ` Pierre Ossman
  0 siblings, 0 replies; 8+ messages in thread
From: Pierre Ossman @ 2007-07-09 16:27 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: Nicolas Ferre, ARM Linux Mailing List, wux, Linux Kernel list,
	Hamish Guthrie, Andrew Victor, Marc Pignat, m.kozlowski

[-- Attachment #1: Type: text/plain, Size: 535 bytes --]

On Mon, 09 Jul 2007 14:58:16 +0200
Nicolas Ferre <nicolas.ferre@rfo.atmel.com> wrote:

> Fixes hanging using multi block operations (seen during CMD25).
> Follows closely the datasheet flowcharts.
> 
> This piece of code handles better big file writing. I had to take
> care of the notbusy signal during write (at91_mci_handle_cmdrdy
> function) and to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF
> flag usage.
> 
> Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
> ---

Thanks, applied.

Rgds
Pierre

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2007-07-09 16:43 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-02 12:16 [PATCH] mmc: at91_mci: fix hanging and rework to match flowcharts Nicolas Ferre
2007-07-02 12:45 ` Marc Pignat
2007-07-02 13:32 ` Pierre Ossman
2007-07-09 10:51   ` Pierre Ossman
2007-07-09 12:48   ` Nicolas Ferre
2007-07-02 19:13 ` Mariusz Kozlowski
2007-07-09 12:58 ` Nicolas Ferre
2007-07-09 16:27   ` Pierre Ossman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox