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)
1 << data->blksz_bits, data->blocks, data->flags);
host->data = data;
host->offset = 0;
host->size = data->blocks << data->blksz_bits;
host->data_xfered = 0;
mmci_init_sg(host, data);
timeout = data->timeout_clks +
((unsigned long long)data->timeout_ns * host->cclk) /
1000000000ULL;
......@@ -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;
int ret = 0;
char *ptr = buffer;
u32 status;
do {
unsigned long flags;
unsigned int bio_remain;
char *buffer;
int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
/*
* Check for data available.
*/
if (!(status & MCI_RXDATAAVLBL))
if (count > remain)
count = remain;
if (count <= 0)
break;
/*
* Map the BIO buffer.
*/
buffer = bio_kmap_irq(req->cbio, &flags);
bio_remain = (req->current_nr_sectors << 9) - host->offset;
readsl(base + MMCIFIFO, ptr, count >> 2);
do {
int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
ptr += count;
remain -= count;
if (count > bio_remain)
count = bio_remain;
if (remain == 0)
break;
if (count > 0) {
ret = 1;
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);
} while (status & MCI_RXDATAAVLBL);
status = readl(base + MMCISTATUS);
} while (status & MCI_RXDATAAVLBL);
return ptr - buffer;
}
bio_kunmap_irq(buffer, &flags);
break;
static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
{
void *base = host->base;
char *ptr = buffer;
next_bio:
bio_kunmap_irq(buffer, &flags);
do {
unsigned int count, maxcnt;
/*
* 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))
maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
count = min(remain, maxcnt);
writesl(base + MMCIFIFO, ptr, count >> 2);
ptr += count;
remain -= count;
if (remain == 0)
break;
host->offset = 0;
status = readl(base + MMCISTATUS);
} while (1);
/*
* 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);
} while (status & MCI_TXFIFOHALFEMPTY);
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;
int ret = 0;
u32 status;
status = readl(base + MMCISTATUS);
DBG(host, "irq1 %08x\n", status);
do {
unsigned long flags;
unsigned int bio_remain;
unsigned int remain, len;
char *buffer;
/*
* We only need to test the half-empty flag here - if
* the FIFO is completely empty, then by definition
* it is more than half empty.
* For write, we only need to test the half-empty flag
* here - if the FIFO is completely empty, then by
* definition it is more than half empty.
*
* For read, check for data available.
*/
if (!(status & MCI_TXFIFOHALFEMPTY))
if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
break;
/*
* Map the BIO buffer.
* Map the current scatter buffer.
*/
buffer = bio_kmap_irq(req->cbio, &flags);
bio_remain = (req->current_nr_sectors << 9) - host->offset;
do {
unsigned int count, maxcnt;
buffer = mmci_kmap_atomic(host, &flags) + host->sg_off;
remain = host->sg_ptr->length - host->sg_off;
maxcnt = status & MCI_TXFIFOEMPTY ?
MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
count = min(bio_remain, maxcnt);
len = 0;
if (status & MCI_RXACTIVE)
len = mmci_pio_read(host, buffer, remain);
if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);
writesl(base + MMCIFIFO, buffer + host->offset, count >> 2);
host->offset += count;
host->size -= count;
bio_remain -= count;
ret = 1;
if (bio_remain == 0)
goto next_bio;
/*
* Unmap the buffer.
*/
mmci_kunmap_atomic(host, &flags);
status = readl(base + MMCISTATUS);
} while (status & MCI_TXFIFOHALFEMPTY);
host->sg_off += len;
host->size -= len;
remain -= len;
bio_kunmap_irq(buffer, &flags);
break;
next_bio:
bio_kunmap_irq(buffer, &flags);
if (remain)
break;
/*
* 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))
if (!mmci_next_sg(host))
break;
host->offset = 0;
status = readl(base + MMCISTATUS);
} while (1);
return ret;
}
/*
* 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;
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're nearing the end of the read, switch to
* "any data available" mode.
*/
if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE)
writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
/*
* 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)
writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
}
return IRQ_RETVAL(ret);
return IRQ_HANDLED;
}
/*
......
......@@ -115,6 +115,8 @@
#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
#define NR_SG 16
struct clk;
struct mmci_host {
......@@ -137,7 +139,45 @@ struct mmci_host {
struct timer_list timer;
unsigned int oldstat;
struct scatterlist sg[NR_SG];
unsigned int sg_len;
/* pio stuff */
unsigned int offset;
struct scatterlist *sg_ptr;
unsigned int sg_off;
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