Commit f2e5c096 authored by Russell King's avatar Russell King

[MMC] Use scatter-gather lists rather than walking the BIOs

Bartlomiej Zolnierkiewicz wanted to remove the "cbio" structure
from struct request.  Unfortunately, MMC was using the same
"workaround" as IDE to walk the BIO list.  With this change, we
stop using "cbio" and instead use proper scatter-gather lists.
parent 9a874d88
...@@ -75,10 +75,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) ...@@ -75,10 +75,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
1 << data->blksz_bits, data->blocks, data->flags); 1 << data->blksz_bits, data->blocks, data->flags);
host->data = data; host->data = data;
host->offset = 0;
host->size = data->blocks << data->blksz_bits; host->size = data->blocks << data->blksz_bits;
host->data_xfered = 0; host->data_xfered = 0;
mmci_init_sg(host, data);
timeout = data->timeout_clks + timeout = data->timeout_clks +
((unsigned long long)data->timeout_ns * host->cclk) / ((unsigned long long)data->timeout_ns * host->cclk) /
1000000000ULL; 1000000000ULL;
...@@ -190,160 +191,124 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, ...@@ -190,160 +191,124 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
} }
} }
static int mmci_pio_read(struct mmci_host *host, struct request *req, u32 status) static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
{ {
void *base = host->base; void *base = host->base;
int ret = 0; char *ptr = buffer;
u32 status;
do { do {
unsigned long flags; int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
unsigned int bio_remain;
char *buffer;
/* if (count > remain)
* Check for data available. count = remain;
*/
if (!(status & MCI_RXDATAAVLBL))
break;
/* if (count <= 0)
* Map the BIO buffer. break;
*/
buffer = bio_kmap_irq(req->cbio, &flags);
bio_remain = (req->current_nr_sectors << 9) - host->offset;
do { readsl(base + MMCIFIFO, ptr, count >> 2);
int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
if (count > bio_remain) ptr += count;
count = bio_remain; remain -= count;
if (count > 0) { if (remain == 0)
ret = 1; break;
readsl(base + MMCIFIFO, buffer + host->offset, count >> 2);
host->offset += count;
host->size -= count;
bio_remain -= count;
if (bio_remain == 0)
goto next_bio;
}
status = readl(base + MMCISTATUS); status = readl(base + MMCISTATUS);
} while (status & MCI_RXDATAAVLBL); } while (status & MCI_RXDATAAVLBL);
bio_kunmap_irq(buffer, &flags); return ptr - buffer;
break; }
next_bio: static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
bio_kunmap_irq(buffer, &flags); {
void *base = host->base;
char *ptr = buffer;
/* do {
* Ok, we've completed that BIO, move on to next unsigned int count, maxcnt;
* BIO in the chain. Note: this doesn't actually
* complete the BIO! maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
*/ count = min(remain, maxcnt);
if (!process_that_request_first(req, req->current_nr_sectors))
writesl(base + MMCIFIFO, ptr, count >> 2);
ptr += count;
remain -= count;
if (remain == 0)
break; break;
host->offset = 0;
status = readl(base + MMCISTATUS); status = readl(base + MMCISTATUS);
} while (1); } while (status & MCI_TXFIFOHALFEMPTY);
/*
* If we're nearing the end of the read, switch to
* "any data available" mode.
*/
if (host->size < MCI_FIFOSIZE)
writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
return ret; return ptr - buffer;
} }
static int mmci_pio_write(struct mmci_host *host, struct request *req, u32 status) /*
* PIO data transfer IRQ handler.
*/
static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs)
{ {
struct mmci_host *host = dev_id;
void *base = host->base; void *base = host->base;
int ret = 0; u32 status;
status = readl(base + MMCISTATUS);
DBG(host, "irq1 %08x\n", status);
do { do {
unsigned long flags; unsigned long flags;
unsigned int bio_remain; unsigned int remain, len;
char *buffer; char *buffer;
/* /*
* We only need to test the half-empty flag here - if * For write, we only need to test the half-empty flag
* the FIFO is completely empty, then by definition * here - if the FIFO is completely empty, then by
* it is more than half empty. * definition it is more than half empty.
*
* For read, check for data available.
*/ */
if (!(status & MCI_TXFIFOHALFEMPTY)) if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
break; break;
/* /*
* Map the BIO buffer. * Map the current scatter buffer.
*/ */
buffer = bio_kmap_irq(req->cbio, &flags); buffer = mmci_kmap_atomic(host, &flags) + host->sg_off;
bio_remain = (req->current_nr_sectors << 9) - host->offset; remain = host->sg_ptr->length - host->sg_off;
do {
unsigned int count, maxcnt;
maxcnt = status & MCI_TXFIFOEMPTY ?
MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
count = min(bio_remain, maxcnt);
writesl(base + MMCIFIFO, buffer + host->offset, count >> 2); len = 0;
host->offset += count; if (status & MCI_RXACTIVE)
host->size -= count; len = mmci_pio_read(host, buffer, remain);
bio_remain -= count; if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);
ret = 1; /*
* Unmap the buffer.
*/
mmci_kunmap_atomic(host, &flags);
if (bio_remain == 0) host->sg_off += len;
goto next_bio; host->size -= len;
remain -= len;
status = readl(base + MMCISTATUS); if (remain)
} while (status & MCI_TXFIFOHALFEMPTY);
bio_kunmap_irq(buffer, &flags);
break; break;
next_bio: if (!mmci_next_sg(host))
bio_kunmap_irq(buffer, &flags);
/*
* Ok, we've completed that BIO, move on to next
* BIO in the chain. Note: this doesn't actually
* complete the BIO!
*/
if (!process_that_request_first(req, req->current_nr_sectors))
break; break;
host->offset = 0;
status = readl(base + MMCISTATUS); status = readl(base + MMCISTATUS);
} while (1); } while (1);
return ret; /*
} * If we're nearing the end of the read, switch to
* "any data available" mode.
/*
* PIO data transfer IRQ handler.
*/ */
static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE)
{ writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
struct mmci_host *host = dev_id;
struct request *req;
void *base = host->base;
u32 status;
int ret = 0;
status = readl(base + MMCISTATUS);
DBG(host, "irq1 %08x\n", status);
req = host->data->req;
if (status & MCI_RXACTIVE)
ret = mmci_pio_read(host, req, status);
else if (status & MCI_TXACTIVE)
ret = mmci_pio_write(host, req, status);
/* /*
* If we run out of data, disable the data IRQs; this * If we run out of data, disable the data IRQs; this
...@@ -356,7 +321,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) ...@@ -356,7 +321,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs)
writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0); writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
} }
return IRQ_RETVAL(ret); return IRQ_HANDLED;
} }
/* /*
......
...@@ -115,6 +115,8 @@ ...@@ -115,6 +115,8 @@
#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) #define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
#define NR_SG 16
struct clk; struct clk;
struct mmci_host { struct mmci_host {
...@@ -137,7 +139,45 @@ struct mmci_host { ...@@ -137,7 +139,45 @@ struct mmci_host {
struct timer_list timer; struct timer_list timer;
unsigned int oldstat; unsigned int oldstat;
struct scatterlist sg[NR_SG];
unsigned int sg_len;
/* pio stuff */ /* pio stuff */
unsigned int offset; struct scatterlist *sg_ptr;
unsigned int sg_off;
unsigned int size; unsigned int size;
}; };
static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
{
struct scatterlist *sg = host->sg;
struct request *req = data->req;
/*
* Ideally, we want the higher levels to pass us a scatter list.
*/
host->sg_len = blk_rq_map_sg(req->q, req, sg);
host->sg_ptr = sg;
host->sg_off = 0;
}
static inline int mmci_next_sg(struct mmci_host *host)
{
host->sg_ptr++;
host->sg_off = 0;
return --host->sg_len;
}
static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
{
struct scatterlist *sg = host->sg_ptr;
local_irq_save(*flags);
return kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->sg_off;
}
static inline void mmci_kunmap_atomic(struct mmci_host *host, unsigned long *flags)
{
kunmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ);
local_irq_restore(*flags);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment