|
@@ -1,7 +1,7 @@
|
|
|
/*
|
|
|
* linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
|
|
|
*
|
|
|
- * Copyright (C) 2004-2006 Pierre Ossman, All Rights Reserved.
|
|
|
+ * Copyright (C) 2004-2007 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 as published by
|
|
@@ -278,90 +278,36 @@ static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
|
|
|
|
|
|
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
|
|
|
{
|
|
|
- unsigned int len, i, size;
|
|
|
+ unsigned int len, i;
|
|
|
struct scatterlist *sg;
|
|
|
char *dmabuf = host->dma_buffer;
|
|
|
char *sgbuf;
|
|
|
|
|
|
- size = host->size;
|
|
|
-
|
|
|
sg = data->sg;
|
|
|
len = data->sg_len;
|
|
|
|
|
|
- /*
|
|
|
- * Just loop through all entries. Size might not
|
|
|
- * be the entire list though so make sure that
|
|
|
- * we do not transfer too much.
|
|
|
- */
|
|
|
for (i = 0; i < len; i++) {
|
|
|
sgbuf = page_address(sg[i].page) + sg[i].offset;
|
|
|
- if (size < sg[i].length)
|
|
|
- memcpy(dmabuf, sgbuf, size);
|
|
|
- else
|
|
|
- memcpy(dmabuf, sgbuf, sg[i].length);
|
|
|
+ memcpy(dmabuf, sgbuf, sg[i].length);
|
|
|
dmabuf += sg[i].length;
|
|
|
-
|
|
|
- if (size < sg[i].length)
|
|
|
- size = 0;
|
|
|
- else
|
|
|
- size -= sg[i].length;
|
|
|
-
|
|
|
- if (size == 0)
|
|
|
- break;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- * Check that we didn't get a request to transfer
|
|
|
- * more data than can fit into the SG list.
|
|
|
- */
|
|
|
-
|
|
|
- BUG_ON(size != 0);
|
|
|
-
|
|
|
- host->size -= size;
|
|
|
}
|
|
|
|
|
|
static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
|
|
|
{
|
|
|
- unsigned int len, i, size;
|
|
|
+ unsigned int len, i;
|
|
|
struct scatterlist *sg;
|
|
|
char *dmabuf = host->dma_buffer;
|
|
|
char *sgbuf;
|
|
|
|
|
|
- size = host->size;
|
|
|
-
|
|
|
sg = data->sg;
|
|
|
len = data->sg_len;
|
|
|
|
|
|
- /*
|
|
|
- * Just loop through all entries. Size might not
|
|
|
- * be the entire list though so make sure that
|
|
|
- * we do not transfer too much.
|
|
|
- */
|
|
|
for (i = 0; i < len; i++) {
|
|
|
sgbuf = page_address(sg[i].page) + sg[i].offset;
|
|
|
- if (size < sg[i].length)
|
|
|
- memcpy(sgbuf, dmabuf, size);
|
|
|
- else
|
|
|
- memcpy(sgbuf, dmabuf, sg[i].length);
|
|
|
+ memcpy(sgbuf, dmabuf, sg[i].length);
|
|
|
dmabuf += sg[i].length;
|
|
|
-
|
|
|
- if (size < sg[i].length)
|
|
|
- size = 0;
|
|
|
- else
|
|
|
- size -= sg[i].length;
|
|
|
-
|
|
|
- if (size == 0)
|
|
|
- break;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- * Check that we didn't get a request to transfer
|
|
|
- * more data than can fit into the SG list.
|
|
|
- */
|
|
|
-
|
|
|
- BUG_ON(size != 0);
|
|
|
-
|
|
|
- host->size -= size;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -484,7 +430,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
|
|
/*
|
|
|
* Handle excessive data.
|
|
|
*/
|
|
|
- if (data->bytes_xfered == host->size)
|
|
|
+ if (host->num_sg == 0)
|
|
|
return;
|
|
|
|
|
|
buffer = wbsd_sg_to_buffer(host) + host->offset;
|
|
@@ -513,12 +459,6 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
|
|
|
|
|
data->bytes_xfered++;
|
|
|
|
|
|
- /*
|
|
|
- * Transfer done?
|
|
|
- */
|
|
|
- if (data->bytes_xfered == host->size)
|
|
|
- return;
|
|
|
-
|
|
|
/*
|
|
|
* End of scatter list entry?
|
|
|
*/
|
|
@@ -526,19 +466,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
|
|
/*
|
|
|
* Get next entry. Check if last.
|
|
|
*/
|
|
|
- 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;
|
|
|
-
|
|
|
+ if (!wbsd_next_sg(host))
|
|
|
return;
|
|
|
- }
|
|
|
|
|
|
buffer = wbsd_sg_to_buffer(host);
|
|
|
}
|
|
@@ -550,7 +479,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
|
|
|
* hardware problem. The chip doesn't trigger
|
|
|
* FIFO threshold interrupts properly.
|
|
|
*/
|
|
|
- if ((host->size - data->bytes_xfered) < 16)
|
|
|
+ if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
|
|
|
tasklet_schedule(&host->fifo_tasklet);
|
|
|
}
|
|
|
|
|
@@ -564,7 +493,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
|
|
* Check that we aren't being called after the
|
|
|
* entire buffer has been transfered.
|
|
|
*/
|
|
|
- if (data->bytes_xfered == host->size)
|
|
|
+ if (host->num_sg == 0)
|
|
|
return;
|
|
|
|
|
|
buffer = wbsd_sg_to_buffer(host) + host->offset;
|
|
@@ -593,12 +522,6 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
|
|
|
|
|
data->bytes_xfered++;
|
|
|
|
|
|
- /*
|
|
|
- * Transfer done?
|
|
|
- */
|
|
|
- if (data->bytes_xfered == host->size)
|
|
|
- return;
|
|
|
-
|
|
|
/*
|
|
|
* End of scatter list entry?
|
|
|
*/
|
|
@@ -606,19 +529,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
|
|
|
/*
|
|
|
* Get next entry. Check if last.
|
|
|
*/
|
|
|
- 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;
|
|
|
-
|
|
|
+ if (!wbsd_next_sg(host))
|
|
|
return;
|
|
|
- }
|
|
|
|
|
|
buffer = wbsd_sg_to_buffer(host);
|
|
|
}
|
|
@@ -638,6 +550,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
u16 blksize;
|
|
|
u8 setup;
|
|
|
unsigned long dmaflags;
|
|
|
+ unsigned int size;
|
|
|
|
|
|
DBGF("blksz %04x blks %04x flags %08x\n",
|
|
|
data->blksz, data->blocks, data->flags);
|
|
@@ -647,7 +560,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
/*
|
|
|
* Calculate size.
|
|
|
*/
|
|
|
- host->size = data->blocks * data->blksz;
|
|
|
+ size = data->blocks * data->blksz;
|
|
|
|
|
|
/*
|
|
|
* Check timeout values for overflow.
|
|
@@ -705,8 +618,8 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
/*
|
|
|
* The buffer for DMA is only 64 kB.
|
|
|
*/
|
|
|
- BUG_ON(host->size > 0x10000);
|
|
|
- if (host->size > 0x10000) {
|
|
|
+ BUG_ON(size > 0x10000);
|
|
|
+ if (size > 0x10000) {
|
|
|
data->error = MMC_ERR_INVALID;
|
|
|
return;
|
|
|
}
|
|
@@ -729,7 +642,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
else
|
|
|
set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
|
|
|
set_dma_addr(host->dma, host->dma_addr);
|
|
|
- set_dma_count(host->dma, host->size);
|
|
|
+ set_dma_count(host->dma, size);
|
|
|
|
|
|
enable_dma(host->dma);
|
|
|
release_dma_lock(dmaflags);
|
|
@@ -812,6 +725,10 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
count = get_dma_residue(host->dma);
|
|
|
release_dma_lock(dmaflags);
|
|
|
|
|
|
+ data->bytes_xfered = host->mrq->data->blocks *
|
|
|
+ host->mrq->data->blksz - count;
|
|
|
+ data->bytes_xfered -= data->bytes_xfered % data->blksz;
|
|
|
+
|
|
|
/*
|
|
|
* Any leftover data?
|
|
|
*/
|
|
@@ -820,7 +737,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
"%d bytes left.\n",
|
|
|
mmc_hostname(host->mmc), count);
|
|
|
|
|
|
- data->error = MMC_ERR_FAILED;
|
|
|
+ if (data->error == MMC_ERR_NONE)
|
|
|
+ data->error = MMC_ERR_FAILED;
|
|
|
} else {
|
|
|
/*
|
|
|
* Transfer data from DMA buffer to
|
|
@@ -828,8 +746,11 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
|
|
|
*/
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
|
wbsd_dma_to_sg(host, data);
|
|
|
+ }
|
|
|
|
|
|
- data->bytes_xfered = host->size;
|
|
|
+ if (data->error != MMC_ERR_NONE) {
|
|
|
+ if (data->bytes_xfered)
|
|
|
+ data->bytes_xfered -= data->blksz;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1167,7 +1088,7 @@ static void wbsd_tasklet_fifo(unsigned long param)
|
|
|
/*
|
|
|
* Done?
|
|
|
*/
|
|
|
- if (host->size == data->bytes_xfered) {
|
|
|
+ if (host->num_sg == 0) {
|
|
|
wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
|
|
|
tasklet_schedule(&host->finish_tasklet);
|
|
|
}
|