All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pierre Ossman <drzeus-list@drzeus.cx>
To: LKML <linux-kernel@vger.kernel.org>,
	Russell King <rmk+lkml@arm.linux.org.uk>
Subject: [PATCH] wbsd update
Date: Wed, 19 Jan 2005 11:41:03 +0100	[thread overview]
Message-ID: <41EE393F.3090705@drzeus.cx> (raw)

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

This patch includes the latest changes to the wbsd driver.

Changelog:

* Proper usage of kunmap.
* Comment about hw bugs.
* Waits for data transfers to finish properly.
* Added module version info.
* FIFO bug fix for small reads.
* Optimised FIFO loop.
* DMA demand mode.
* IRQ race condition when sending commands fixed.

/Pierre

[-- Attachment #2: wbsd-2.6.10-5.patch --]
[-- Type: text/x-patch, Size: 11881 bytes --]

Index: linux/drivers/mmc/wbsd.c
===================================================================
--- linux/drivers/mmc/wbsd.c	(revision 105)
+++ linux/drivers/mmc/wbsd.c	(working copy)
@@ -1,11 +1,24 @@
 /*
- *  linux/drivers/mmc/wbsd.c
+ *  linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
  *
- *  Copyright (C) 2004 Pierre Ossman, All Rights Reserved.
+ *  Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
+ *
+ *
+ * Warning!
+ *
+ * Changes to the FIFO system should be done with extreme care since
+ * the hardware is full of bugs related to the FIFO. Known issues are:
+ *
+ * - FIFO size field in FSR is always zero.
+ *
+ * - FIFO interrupts tend not to work as they should. Interrupts are
+ *   triggered only for full/empty events, not for threshold values.
+ *
+ * - On APIC systems the FIFO empty interrupt is sometimes lost.
  */
 
 #include <linux/config.h>
@@ -27,7 +40,7 @@
 #include "wbsd.h"
 
 #define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.0"
+#define DRIVER_VERSION "1.1"
 
 #ifdef CONFIG_MMC_DEBUG
 #define DBG(x...) \
@@ -239,13 +252,14 @@
 
 static inline char* wbsd_kmap_sg(struct wbsd_host* host)
 {
-	return kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
+	host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
 		host->cur_sg->offset;
+	return host->mapped_sg;
 }
 
 static inline void wbsd_kunmap_sg(struct wbsd_host* host)
 {
-	kunmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ);
+	kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
 }
 
 static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data)
@@ -272,7 +286,7 @@
 			memcpy(dmabuf, sgbuf, size);
 		else
 			memcpy(dmabuf, sgbuf, sg[i].length);
-		kunmap_atomic(sg[i].page, KM_BIO_SRC_IRQ);
+		kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
 		dmabuf += sg[i].length;
 		
 		if (size < sg[i].length)
@@ -318,7 +332,7 @@
 			memcpy(sgbuf, dmabuf, size);
 		else
 			memcpy(sgbuf, dmabuf, sg[i].length);
-		kunmap_atomic(sg[i].page, KM_BIO_SRC_IRQ);
+		kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
 		dmabuf += sg[i].length;
 		
 		if (size < sg[i].length)
@@ -400,16 +414,16 @@
 static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
 {
 	int i;
-	u8 status, eir, isr;
+	u8 status, isr;
 	
 	DBGF("Sending cmd (%x)\n", cmd->opcode);
 
 	/*
-	 * Disable interrupts as the interrupt routine
-	 * will destroy the contents of ISR.
+	 * Clear accumulated ISR. The interrupt routine
+	 * will fill this one with events that occur during
+	 * transfer.
 	 */
-	eir = inb(host->base + WBSD_EIR);
-	outb(0, host->base + WBSD_EIR);
+	host->isr = 0;
 	
 	/*
 	 * Send the command (CRC calculated by host).
@@ -435,7 +449,7 @@
 		/*
 		 * Read back status.
 		 */
-		isr = inb(host->base + WBSD_ISR);
+		isr = host->isr;
 		
 		/* Card removed? */
 		if (isr & WBSD_INT_CARD)
@@ -456,17 +470,6 @@
 		}
 	}
 
-	/*
-	 * Restore interrupt mask to previous value.
-	 */
-	outb(eir, host->base + WBSD_EIR);
-	
-	/*
-	 * Call the interrupt routine to jump start
-	 * interrupts.
-	 */
-	wbsd_irq(0, host, NULL);
-
 	DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
 }
 
@@ -478,6 +481,7 @@
 {
 	struct mmc_data* data = host->mrq->cmd->data;
 	char* buffer;
+	int i, fsr, fifo;
 	
 	/*
 	 * Handle excessive data.
@@ -491,60 +495,83 @@
 	 * Drain the fifo. This has a tendency to loop longer
 	 * than the FIFO length (usually one block).
 	 */
-	while (!(inb(host->base + WBSD_FSR) & WBSD_FIFO_EMPTY))
+	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY))
 	{
-		*buffer = inb(host->base + WBSD_DFR);
-		buffer++;
-		host->offset++;
-		host->remain--;
-
-		data->bytes_xfered++;
-		
 		/*
-		 * Transfer done?
-		 */
-		if (data->bytes_xfered == host->size)
-		{
-			wbsd_kunmap_sg(host);				
-			return;
-		}
+		 * The size field in the FSR is broken so we have to
+		 * do some guessing.
+		 */		
+		if (fsr & WBSD_FIFO_FULL)
+			fifo = 16;
+		else if (fsr & WBSD_FIFO_FUTHRE)
+			fifo = 8;
+		else
+			fifo = 1;
 		
-		/*
-		 * End of scatter list entry?
-		 */
-		if (host->remain == 0)
+		for (i = 0;i < fifo;i++)
 		{
-			wbsd_kunmap_sg(host);
+			*buffer = inb(host->base + WBSD_DFR);
+			buffer++;
+			host->offset++;
+			host->remain--;
+
+			data->bytes_xfered++;
 			
 			/*
-			 * Get next entry. Check if last.
+			 * Transfer done?
 			 */
-			if (!wbsd_next_sg(host))
+			if (data->bytes_xfered == host->size)
 			{
+				wbsd_kunmap_sg(host);				
+				return;
+			}
+			
+			/*
+			 * End of scatter list entry?
+			 */
+			if (host->remain == 0)
+			{
+				wbsd_kunmap_sg(host);
+				
 				/*
-				 * We should never reach this point.
-				 * It means that we're trying to
-				 * transfer more blocks than can fit
-				 * into the scatter list.
+				 * Get next entry. Check if last.
 				 */
-				BUG_ON(1);
+				if (!wbsd_next_sg(host))
+				{
+					/*
+					 * We should never reach this point.
+					 * It means that we're trying to
+					 * transfer more blocks than can fit
+					 * into the scatter list.
+					 */
+					BUG_ON(1);
+					
+					host->size = data->bytes_xfered;
+					
+					return;
+				}
 				
-				host->size = data->bytes_xfered;
-				
-				return;
+				buffer = wbsd_kmap_sg(host);
 			}
-			
-			buffer = wbsd_kmap_sg(host);
 		}
 	}
 	
 	wbsd_kunmap_sg(host);
+
+	/*
+	 * This is a very dirty hack to solve a
+	 * hardware problem. The chip doesn't trigger
+	 * FIFO threshold interrupts properly.
+	 */
+	if ((host->size - data->bytes_xfered) < 16)
+		tasklet_schedule(&host->fifo_tasklet);
 }
 
 static void wbsd_fill_fifo(struct wbsd_host* host)
 {
 	struct mmc_data* data = host->mrq->cmd->data;
 	char* buffer;
+	int i, fsr, fifo;
 	
 	/*
 	 * Check that we aren't being called after the
@@ -559,50 +586,64 @@
 	 * Fill the fifo. This has a tendency to loop longer
 	 * than the FIFO length (usually one block).
 	 */
-	while (!(inb(host->base + WBSD_FSR) & WBSD_FIFO_FULL))
+	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL))
 	{
-		outb(*buffer, host->base + WBSD_DFR);
-		buffer++;
-		host->offset++;
-		host->remain--;
-		
-		data->bytes_xfered++;
-		
 		/*
-		 * Transfer done?
-		 */
-		if (data->bytes_xfered == host->size)
-		{
-			wbsd_kunmap_sg(host);
-			return;
-		}
+		 * The size field in the FSR is broken so we have to
+		 * do some guessing.
+		 */		
+		if (fsr & WBSD_FIFO_EMPTY)
+			fifo = 0;
+		else if (fsr & WBSD_FIFO_EMTHRE)
+			fifo = 8;
+		else
+			fifo = 15;
 
-		/*
-		 * End of scatter list entry?
-		 */
-		if (host->remain == 0)
+		for (i = 16;i > fifo;i--)
 		{
-			wbsd_kunmap_sg(host);
+			outb(*buffer, host->base + WBSD_DFR);
+			buffer++;
+			host->offset++;
+			host->remain--;
 			
+			data->bytes_xfered++;
+			
 			/*
-			 * Get next entry. Check if last.
+			 * Transfer done?
 			 */
-			if (!wbsd_next_sg(host))
+			if (data->bytes_xfered == host->size)
 			{
+				wbsd_kunmap_sg(host);
+				return;
+			}
+
+			/*
+			 * End of scatter list entry?
+			 */
+			if (host->remain == 0)
+			{
+				wbsd_kunmap_sg(host);
+				
 				/*
-				 * We should never reach this point.
-				 * It means that we're trying to
-				 * transfer more blocks than can fit
-				 * into the scatter list.
+				 * Get next entry. Check if last.
 				 */
-				BUG_ON(1);
+				if (!wbsd_next_sg(host))
+				{
+					/*
+					 * We should never reach this point.
+					 * It means that we're trying to
+					 * transfer more blocks than can fit
+					 * into the scatter list.
+					 */
+					BUG_ON(1);
+					
+					host->size = data->bytes_xfered;
+					
+					return;
+				}
 				
-				host->size = data->bytes_xfered;
-				
-				return;
+				buffer = wbsd_kmap_sg(host);
 			}
-			
-			buffer = wbsd_kmap_sg(host);
 		}
 	}
 	
@@ -689,9 +730,9 @@
 		disable_dma(host->dma);
 		clear_dma_ff(host->dma);
 		if (data->flags & MMC_DATA_READ)
-			set_dma_mode(host->dma, DMA_MODE_READ);
+			set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);
 		else
-			set_dma_mode(host->dma, DMA_MODE_WRITE);
+			set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
 		set_dma_addr(host->dma, host->dma_addr);
 		set_dma_count(host->dma, host->size);
 
@@ -701,8 +742,7 @@
 		/*
 		 * Enable DMA on the host.
 		 */
-		wbsd_write_index(host, WBSD_IDX_DMA,
-			WBSD_DMA_SINGLE | WBSD_DMA_ENABLE);
+		wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
 	}
 	else
 	{
@@ -746,6 +786,7 @@
 {
 	unsigned long dmaflags;
 	int count;
+	u8 status;
 	
 	WARN_ON(host->mrq == NULL);
 
@@ -754,6 +795,15 @@
 	 */
 	if (data->stop)
 		wbsd_send_command(host, data->stop);
+
+	/*
+	 * Wait for the controller to leave data
+	 * transfer state.
+	 */
+	do
+	{
+		status = wbsd_read_index(host, WBSD_IDX_STATUS);
+	} while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
 	
 	/*
 	 * DMA transfer?
@@ -851,7 +901,13 @@
 	 * transfered.
 	 */	
 	if (cmd->data && (cmd->error == MMC_ERR_NONE))
-	{		
+	{
+		/*
+		 * Dirty fix for hardware bug.
+		 */
+		if (host->dma == -1)
+			tasklet_schedule(&host->fifo_tasklet);
+
 		spin_unlock_bh(&host->lock);
 
 		return;
@@ -1021,7 +1077,6 @@
 	
 	spin_lock(&host->lock);
 	
-	WARN_ON(!host->mrq);
 	if (!host->mrq)
 		goto end;
 	
@@ -1046,7 +1101,6 @@
 	
 	spin_lock(&host->lock);
 	
-	WARN_ON(!host->mrq);
 	if (!host->mrq)
 		goto end;
 	
@@ -1126,6 +1180,8 @@
 	 */
 	if (isr == 0xff || isr == 0x00)
 		return IRQ_NONE;
+	
+	host->isr |= isr;
 
 	/*
 	 * Schedule tasklets as needed.
@@ -1133,7 +1189,7 @@
 	if (isr & WBSD_INT_CARD)
 		tasklet_schedule(&host->card_tasklet);
 	if (isr & WBSD_INT_FIFO_THRE)
-		tasklet_hi_schedule(&host->fifo_tasklet);
+		tasklet_schedule(&host->fifo_tasklet);
 	if (isr & WBSD_INT_CRC)
 		tasklet_hi_schedule(&host->crc_tasklet);
 	if (isr & WBSD_INT_TIMEOUT)
@@ -1392,8 +1448,8 @@
 	 * Maximum number of segments. Worst case is one sector per segment
 	 * so this will be 64kB/512.
 	 */
-	mmc->max_hw_segs = NR_SG;
-	mmc->max_phys_segs = NR_SG;
+	mmc->max_hw_segs = 128;
+	mmc->max_phys_segs = 128;
 	
 	/*
 	 * Maximum number of sectors in one transfer. Also limited by 64kB
@@ -1590,6 +1646,7 @@
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
+MODULE_VERSION(DRIVER_VERSION);
 
 MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
 MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
Index: linux/drivers/mmc/wbsd.h
===================================================================
--- linux/drivers/mmc/wbsd.h	(revision 105)
+++ linux/drivers/mmc/wbsd.h	(working copy)
@@ -1,7 +1,7 @@
 /*
- *  linux/drivers/mmc/wbsd.h
+ *  linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver
  *
- *  Copyright (C) 2004 Pierre Ossman, All Rights Reserved.
+ *  Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -119,6 +119,8 @@
 #define WBSD_FIFOEN_FULL	0x10
 #define WBSD_FIFO_THREMASK	0x0F
 
+#define WBSD_BLOCK_READ		0x80
+#define WBSD_BLOCK_WRITE	0x40
 #define WBSD_BUSY		0x20
 #define WBSD_CARDTRAFFIC	0x04
 #define WBSD_SENDCMD		0x02
@@ -132,9 +134,6 @@
 #define WBSD_CRC_FAIL		0x0B /* S101E (01011) */
 
 
-/* 64kB / 512 */
-#define NR_SG			128
-
 struct wbsd_host
 {
 	struct mmc_host*	mmc;		/* MMC structure */
@@ -143,9 +142,11 @@
 
 	struct mmc_request*	mrq;		/* Current request */
 	
-	struct scatterlist	sg[NR_SG];	/* SG list */
+	u8			isr;		/* Accumulated ISR */
+	
 	struct scatterlist*	cur_sg;		/* Current SG entry */
 	unsigned int		num_sg;		/* Number of entries left */
+	void*			mapped_sg;	/* vaddr of mapped sg */
 	
 	unsigned int		offset;		/* Offset into current entry */
 	unsigned int		remain;		/* Data left in curren entry */

             reply	other threads:[~2005-01-19 10:41 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-01-19 10:41 Pierre Ossman [this message]
2005-01-21 11:13 ` [PATCH] wbsd update Russell King
  -- strict thread matches above, loose matches on Subject: below --
2005-03-22 21:29 Pierre Ossman
2005-03-24 15:50 ` Russell King
2005-04-01  7:36   ` Pierre Ossman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=41EE393F.3090705@drzeus.cx \
    --to=drzeus-list@drzeus.cx \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rmk+lkml@arm.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.