Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2004-09-29 04:03:09 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-09-29 04:03:09 -0700
commit019c21a84e5a3e2bbe797dbb3e2886c0ce69e866 (patch)
treebd3ce741fd9d5e4b5fbda358a29ca91eca9d03b2
parent87bd1b54ce195f6b3a7be3a63c8c7b1ccf78ae43 (diff)
parente2885a5112f5a6c819ab4eac75fc5e94f097589d (diff)
Merge bk://bk.arm.linux.org.uk/linux-2.6-mmc
into ppc970.osdl.org:/home/torvalds/v2.6/linux
-rw-r--r--drivers/mmc/mmc.c38
-rw-r--r--drivers/mmc/mmc_block.c17
-rw-r--r--drivers/mmc/mmci.c191
-rw-r--r--drivers/mmc/mmci.h42
4 files changed, 158 insertions, 130 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 3d057384dd17..5c3d848943ed 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -270,8 +270,7 @@ static inline void mmc_delay(unsigned int ms)
yield();
mdelay(ms);
} else {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(ms * HZ / 1000);
+ msleep_interruptible (ms);
}
}
@@ -451,11 +450,26 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
}
/*
+ * Tell attached cards to go to IDLE state
+ */
+static void mmc_idle_cards(struct mmc_host *host)
+{
+ struct mmc_command cmd;
+
+ cmd.opcode = MMC_GO_IDLE_STATE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_NONE;
+
+ mmc_wait_for_cmd(host, &cmd, 0);
+
+ mmc_delay(1);
+}
+
+/*
* Apply power to the MMC stack.
*/
static void mmc_power_up(struct mmc_host *host)
{
- struct mmc_command cmd;
int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
@@ -470,12 +484,6 @@ static void mmc_power_up(struct mmc_host *host)
host->ops->set_ios(host, &host->ios);
mmc_delay(2);
-
- cmd.opcode = MMC_GO_IDLE_STATE;
- cmd.arg = 0;
- cmd.flags = MMC_RSP_NONE;
-
- mmc_wait_for_cmd(host, &cmd, 0);
}
static void mmc_power_off(struct mmc_host *host)
@@ -505,6 +513,8 @@ static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
break;
err = MMC_ERR_TIMEOUT;
+
+ mmc_delay(10);
}
if (rocr)
@@ -647,12 +657,22 @@ static void mmc_setup(struct mmc_host *host)
u32 ocr;
mmc_power_up(host);
+ mmc_idle_cards(host);
err = mmc_send_op_cond(host, 0, &ocr);
if (err != MMC_ERR_NONE)
return;
host->ocr = mmc_select_voltage(host, ocr);
+
+ /*
+ * Since we're changing the OCR value, we seem to
+ * need to tell some cards to go back to the idle
+ * state. We wait 1ms to give cards time to
+ * respond.
+ */
+ if (host->ocr)
+ mmc_idle_cards(host);
} else {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.clock = host->f_min;
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index dc095d7fd749..f76673a47542 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -42,7 +42,7 @@
*/
#define MMC_SHIFT 3
-static int mmc_major;
+static int major;
/*
* There is one mmc_blk_data per slot.
@@ -323,7 +323,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
md->queue.issue_fn = mmc_blk_issue_rq;
md->queue.data = md;
- md->disk->major = mmc_major;
+ md->disk->major = major;
md->disk->first_minor = devidx << MMC_SHIFT;
md->disk->fops = &mmc_bdops;
md->disk->private_data = md;
@@ -462,14 +462,14 @@ static int __init mmc_blk_init(void)
{
int res = -ENOMEM;
- res = register_blkdev(mmc_major, "mmc");
+ res = register_blkdev(major, "mmc");
if (res < 0) {
printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",
- mmc_major, res);
+ major, res);
goto out;
}
- if (mmc_major == 0)
- mmc_major = res;
+ if (major == 0)
+ major = res;
devfs_mk_dir("mmc");
return mmc_register_driver(&mmc_driver);
@@ -482,7 +482,7 @@ static void __exit mmc_blk_exit(void)
{
mmc_unregister_driver(&mmc_driver);
devfs_remove("mmc");
- unregister_blkdev(mmc_major, "mmc");
+ unregister_blkdev(major, "mmc");
}
module_init(mmc_blk_init);
@@ -490,3 +490,6 @@ module_exit(mmc_blk_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
+
+module_param(major, int, 0444);
+MODULE_PARM_DESC(major, "specify the major device number for MMC block driver");
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..ce3a026d1591 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->offset;
+}
+
+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);
+}