Subject: [PATCH] ALCHEMY: Add SD support to AU1200 MMC/SD driver

From: "Jordan Crouse" <[email-masked]>
Date: Fri, 2 Dec 2005 12:01:08 -0700

Add SD support to the AU1200 MMC driver.  This can
be added post 2.6.15, I'm just sending them out today so the various
maintainers can get them queued up. 

Signed-off-by: Jordan Crouse <[email-masked]>
---

 drivers/mmc/au1xmmc.c |  124 ++++++++++++++++++++++++++++---------------------
 1 files changed, 71 insertions(+), 53 deletions(-)

diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c
index cb32a08..c8c8f29 100644
--- a/drivers/mmc/au1xmmc.c
+++ b/drivers/mmc/au1xmmc.c
@@ -99,16 +99,24 @@ static inline void IRQ_ON(struct au1xmmc
 	au_sync();
 }
 
-static inline void FLUSH_FIFO(struct au1xmmc_host *host) 
+/* Turn on the FIFO flush - the fifo will be returned to active right
+ * before data transfer
+ */
+
+static inline void FLUSH_FIFO_ON(struct au1xmmc_host *host)
 {
 	u32 val = au_readl(HOST_CONFIG2(host));
+	val |= SD_CONFIG2_FF;
+	au_writel(val, HOST_CONFIG2(host));
+	au_sync();
+}
 
-	au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host));
-	au_sync_delay(1);
-	
-	/* SEND_STOP will turn off clock control - this re-enables it */
-	val &= ~SD_CONFIG2_DF;
+static inline void FIFO_ACTIVE(struct au1xmmc_host *host)
+{
+	u32 val = au_readl(HOST_CONFIG2(host));
 
+	/* SEND_STOP will turn off clock control - this re-enables it */
+	val &= ~(SD_CONFIG2_DF | SD_CONFIG2_FF);
 	au_writel(val, HOST_CONFIG2(host));
 	au_sync();
 }
@@ -124,8 +132,8 @@ static inline void IRQ_OFF(struct au1xmm
 static inline void SEND_STOP(struct au1xmmc_host *host) 
 {
 
-	/* We know the value of CONFIG2, so avoid a read we don't need */
-	u32 mask = SD_CONFIG2_EN;
+	/* Penalty box for Jordan - NEVER ASSUME! */
+	u32 mask = au_readl(HOST_CONFIG2(host));
 
 	WARN_ON(host->status != HOST_S_DATA);
 	host->status = HOST_S_STOP;
@@ -169,7 +177,7 @@ static void au1xmmc_finish_request(struc
 	host->flags &= HOST_F_ACTIVE; 
 
 	host->dma.len = 0;
-	host->dma.dir = 0;
+	host->dma.dir = DMA_BIDIRECTIONAL;
 
 	host->pio.index  = 0;
 	host->pio.offset = 0;
@@ -179,6 +187,9 @@ static void au1xmmc_finish_request(struc
 
 	bcsr->disk_leds |= (1 << 8);
 
+	/* Flush the FIFO until our next request */
+	FLUSH_FIFO_ON(host);
+
 	mmc_request_done(host->mmc, mrq);
 }
 
@@ -196,7 +207,11 @@ static int au1xmmc_send_command(struct a
 
 	switch(cmd->flags) {
 	case MMC_RSP_R1:
-		mmccmd |= SD_CMD_RT_1;
+		if (cmd->opcode == 0x03 && host->mmc->mode == MMC_MODE_SD)
+			mmccmd |= SD_CMD_RT_6;
+		else
+			mmccmd |= SD_CMD_RT_1;
+
 		break;
 	case MMC_RSP_R1B:
 		mmccmd |= SD_CMD_RT_1B;
@@ -504,8 +519,8 @@ static void au1xmmc_cmd_complete(struct 
 		r[3] = au_readl(host->iobase + SD_RESP0);
 		
 		/* The CRC is omitted from the response, so really we only got
-		 * 120 bytes, but the engine expects 128 bits, so we have to shift
-		 * things up 
+		 * 120 bytes, but the engine expects 128 bits, so we have to 
+		 * shift things up 
 		 */
 		
 		for(i = 0; i < 4; i++) {
@@ -576,9 +591,8 @@ au1xmmc_prepare_data(struct au1xmmc_host
 {
 
 	int datalen = data->blocks * (1 << data->blksz_bits);
-
-	if (dma != 0) 
-		host->flags |= HOST_F_DMA;
+	int i = 0;
+	u32 channel;
 
 	if (data->flags & MMC_DATA_READ)
 		host->flags |= HOST_F_RECV;
@@ -588,8 +602,6 @@ au1xmmc_prepare_data(struct au1xmmc_host
 	if (host->mrq->stop) 
 		host->flags |= HOST_F_STOP;
 		
-	host->dma.dir = DMA_BIDIRECTIONAL;
-
 	host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg,
 				   data->sg_len, host->dma.dir);
 
@@ -598,9 +610,21 @@ au1xmmc_prepare_data(struct au1xmmc_host
 
 	au_writel((1 << data->blksz_bits) - 1, HOST_BLKSIZE(host));	
 
-	if (host->flags & HOST_F_DMA) {
-		int i;
-		u32 channel = DMA_CHANNEL(host);
+	if (dma == 0) {
+		host->pio.index = 0;
+		host->pio.offset = 0;
+		host->pio.len = datalen;
+		
+		if (host->flags & HOST_F_XMIT)
+			IRQ_ON(host, SD_CONFIG_TH);
+		else 
+			IRQ_ON(host, SD_CONFIG_NE);
+
+		return MMC_ERR_NONE;
+	}
+
+	host->flags |= HOST_F_DMA;
+	channel = DMA_CHANNEL(host);
 
 		au1xxx_dbdma_stop(channel);
 
@@ -611,7 +635,7 @@ au1xmmc_prepare_data(struct au1xmmc_host
 			
 			int len = (datalen > sg_len) ? sg_len : datalen;
 
-			if (i == host->dma.len - 1)
+		if (i == (host->dma.len - 1))
 				flags = DDMA_FLAGS_IE;
 
     			if (host->flags & HOST_F_XMIT){
@@ -627,23 +651,11 @@ au1xmmc_prepare_data(struct au1xmmc_host
 					len, flags);
 			}
 
-    			if (!ret) 
+    		if (ret == 0) 
 				goto dataerr;
 
 			datalen -= len;
 		}
-	}
-	else {
-		host->pio.index = 0;
-		host->pio.offset = 0;
-		host->pio.len = datalen;
-		
-		if (host->flags & HOST_F_XMIT)
-			IRQ_ON(host, SD_CONFIG_TH);
-		else 
-			IRQ_ON(host, SD_CONFIG_NE);
-			//IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);
-	}
 
 	return MMC_ERR_NONE;
 
@@ -671,7 +683,7 @@ static void au1xmmc_request(struct mmc_h
 	bcsr->disk_leds &= ~(1 << 8);
 
 	if (mrq->data) {
-		FLUSH_FIFO(host);
+		FIFO_ACTIVE(host);
 		ret = au1xmmc_prepare_data(host, mrq->data);
 	}
 
@@ -734,6 +746,20 @@ static void au1xmmc_set_ios(struct mmc_h
 		au1xmmc_set_clock(host, ios->clock);
 		host->clock = ios->clock;
 	}
+
+	/* Set the bus width for SD */
+
+	if (ios->bus_width != host->bus_width) {
+		u32 val;
+		val = au_readl(HOST_CONFIG2(host));
+		val &= ~(SD_CONFIG2_WB);
+		val |= (ios->bus_width == MMC_BUS_WIDTH_4) ? SD_CONFIG2_WB : 0;
+
+		au_writel(val, HOST_CONFIG2(host));
+		au_sync();
+
+		host->bus_width = ios->bus_width;
+	}
 }
 
 static void au1xmmc_dma_callback(int irq, void *dev_id, struct pt_regs *regs) 
@@ -778,24 +804,8 @@ static irqreturn_t au1xmmc_irq(int irq, 
 		
 			/* In PIO mode, interrupts might still be enabled */
 			IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
-
-			//IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF);
 			tasklet_schedule(&host->finish_task);
 		}
-#if 0
-		else if (status & SD_STATUS_DD) {
-
-			/* Sometimes we get a DD before a NE in PIO mode */
-
-			if (!(host->flags & HOST_F_DMA) && 
-					(status & SD_STATUS_NE))
-				au1xmmc_receive_pio(host);
-			else {
-				au1xmmc_data_complete(host, status);
-				//tasklet_schedule(&host->data_task);
-			}
-		}
-#endif
 		else if (status & (SD_STATUS_CR)) {
 			if (host->status == HOST_S_CMD)
 				au1xmmc_cmd_complete(host,status);
@@ -875,9 +885,15 @@ static void au1xmmc_init_dma(struct au1x
 	host->rx_chan = rxchan;
 }
 
+static int au1xmmc_get_ro(struct mmc_host *mmc) {
+	struct au1xmmc_host *host = mmc_priv(mmc);
+	return au1xmmc_card_readonly(host);
+}
+
 struct mmc_host_ops au1xmmc_ops = {
 	.request	= au1xmmc_request,
 	.set_ios	= au1xmmc_set_ios,
+	.get_ro		= au1xmmc_get_ro, 
 };
 
 static int au1xmmc_probe(struct device *dev) 
@@ -914,6 +930,7 @@ static int au1xmmc_probe(struct device *
 		mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
 		mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; 
 
+		mmc->caps = MMC_CAP_4_BIT_DATA;
 		mmc->ocr_avail = AU1XMMC_OCR;
 
 		host = mmc_priv(mmc);
@@ -923,7 +940,9 @@ static int au1xmmc_probe(struct device *
 		host->iobase = au1xmmc_card_table[host->id].iobase;
 		host->clock = 0;
 		host->power_mode = MMC_POWER_OFF;
-		
+
+		host->bus_width = MMC_BUS_WIDTH_1;
+
 		host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0;
 		host->status = HOST_S_IDLE;
 
@@ -1017,4 +1036,3 @@ MODULE_AUTHOR("Advanced Micro Devices, I
 MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX");
 MODULE_LICENSE("GPL");
 #endif
-

---

From: Pierre Ossman <[email-masked]>
Date: Fri, 02 Dec 2005 20:42:02 +0100

>  1 files changed, 71 insertions(+), 53 deletions(-)

Russell is still the maintainer of the MMC layer. :)

I still have some comments though.

>  	host->status = HOST_S_STOP;

This comment will be terribly confusing to anyone reading your code.

> +		if (cmd->opcode == 0x03 && host->mmc->mode == MMC_MODE_SD)

No, no, no! Even if this wasn't already fixed in the current kernel you
never hack around bugs in other parts of the kernel, you fix them!

> +		 * 120 bytes, but the engine expects 128 bits, so we have to 

s/bytes/bits/

>      			if (host->flags & HOST_F_XMIT){

broken indentation.

> -			//IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);

Here aswell. And it seems the block above will get the wrong indentation.

Rgds
Pierre

---

From: Russell King <[email-masked]>
Date: Fri, 2 Dec 2005 19:45:35 +0000

> Russell is still the maintainer of the MMC layer. :)

Indeed.  Jordan - please send me a copy of the patch.  Thanks.

Note that I agree with Pierre's comments.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

---

From: "Jordan Crouse" <[email-masked]>
Date: Fri, 2 Dec 2005 14:17:09 -0700

> never hack around bugs in other parts of the kernel, you fix them!

As of a git pull about 30 minutes ago, both MMC_RSP_R1 and MMC_RSP_R6 resolve
to (MMC_RSP_SHORT|MMC_RSP_CRC).  Now, I really wouldn't call that a 
bug in the subsystem, because it is technically correct, but the Au1200
needs us to specifically specify if the required response is an R1 or
an R6, thus the specified logic.  

Jordan
--
Jordan Crouse
Senior Linux Engineer
AMD - Personal Connectivity Solutions Group
<www.amd.com/embeddedprocessors>

---

From: Pierre Ossman <[email-masked]>
Date: Fri, 02 Dec 2005 23:05:46 +0100

> an R6, thus the specified logic.  

Point, but then you should figure out the distinction and why the
controller requires it (I assume you have tried giving the controller
"incorrect" settings). At that point the MMC layer can be extended to
handle this in a general manner instead of hacks in every other driver.
Judging from your email address you seem to be at a good position to
find out what the problem is.

Rgds
Pierre

---

From: Rodolfo Giometti <[email-masked]>
Date: Wed, 14 Dec 2005 14:41:39 +0100

> maintainers can get them queued up. 

According to AMD Application Note titled "MultiMediaCard Support Using
the AMD Alchemy Au1200 and Au1100 Processors" I'd like to test your
driver on my Au1100 based board.

Can you please told me the Linux kernel version where the patch apply
to?

Do you think there should be some issue to keep in consideration
regarding the Au1100?

Thanks in advance,

Rodolfo

-- 

GNU/Linux Solutions                  e-mail:    [email-masked]
Linux Device Driver                             [email-masked]
Embedded Systems                     		[email-masked]
UNIX programming                     phone:     +39 349 2432127

---

From: "Jordan Crouse" <[email-masked]>
Date: Wed, 14 Dec 2005 08:53:25 -0700

> Can you please told me the Linux kernel version where the patch apply

The patch should apply to the most recent linux-mips git (at least as
of Wednesday morning).  

> Do you think there should be some issue to keep in consideration

Well, hopefully everything will Just Work (TM), but you'll want to make
sure that all the various definitions are enabled for the AU1100.  I'll
have to give you my standard disclaimer that I haven't compiled this
for anything but a DB1200 and PB1200, so I can't promise that it will work,
but there is nothing in the code that says it won't.

Regards,
Jordan