wbsd update
PATCH 1/1 wbsd update
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
drivers/mmc/wbsd.c +155 -98
@@ -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)" );
drivers/mmc/wbsd.h +7 -6
@@ -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 */
Quoted 1 line · Pierre Ossman
This patch includes the latest changes to the wbsd driver.
Thanks, I've applied this patch.
Signature · 4 lines
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of: 2.6 PCMCIA - http://pcmcia.arm.linux.org.uk/
2.6 Serial core