diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-09-29 04:03:09 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-09-29 04:03:09 -0700 |
| commit | 019c21a84e5a3e2bbe797dbb3e2886c0ce69e866 (patch) | |
| tree | bd3ce741fd9d5e4b5fbda358a29ca91eca9d03b2 | |
| parent | 87bd1b54ce195f6b3a7be3a63c8c7b1ccf78ae43 (diff) | |
| parent | e2885a5112f5a6c819ab4eac75fc5e94f097589d (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.c | 38 | ||||
| -rw-r--r-- | drivers/mmc/mmc_block.c | 17 | ||||
| -rw-r--r-- | drivers/mmc/mmci.c | 191 | ||||
| -rw-r--r-- | drivers/mmc/mmci.h | 42 |
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); +} |
