Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk@flint.arm.linux.org.uk>2004-09-29 19:57:02 +0100
committerRussell King <rmk@flint.arm.linux.org.uk>2004-09-29 19:57:02 +0100
commitf2e5c0967f347c51273bb3f1eec3d65862bb1966 (patch)
treea5e11d28f3eea04066d528b7ceb3d801a23cac1e
parent9a874d881b03c10225cca3f1be3bcc677275d532 (diff)
[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.
-rw-r--r--drivers/mmc/mmci.c191
-rw-r--r--drivers/mmc/mmci.h42
2 files changed, 119 insertions, 114 deletions
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index 375305c5409b..47e1636de928 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -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;
}
/*
diff --git a/drivers/mmc/mmci.h b/drivers/mmc/mmci.h
index dca6cbe48013..51e4f6440a61 100644
--- a/drivers/mmc/mmci.h
+++ b/drivers/mmc/mmci.h
@@ -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);
+}