Commit 019c21a8 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://bk.arm.linux.org.uk/linux-2.6-mmc

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 87bd1b54 e2885a51
...@@ -270,8 +270,7 @@ static inline void mmc_delay(unsigned int ms) ...@@ -270,8 +270,7 @@ static inline void mmc_delay(unsigned int ms)
yield(); yield();
mdelay(ms); mdelay(ms);
} else { } else {
set_current_state(TASK_INTERRUPTIBLE); msleep_interruptible (ms);
schedule_timeout(ms * HZ / 1000);
} }
} }
...@@ -450,12 +449,27 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca) ...@@ -450,12 +449,27 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
return card; return card;
} }
/*
* 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. * Apply power to the MMC stack.
*/ */
static void mmc_power_up(struct mmc_host *host) static void mmc_power_up(struct mmc_host *host)
{ {
struct mmc_command cmd;
int bit = fls(host->ocr_avail) - 1; int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit; host->ios.vdd = bit;
...@@ -470,12 +484,6 @@ static void mmc_power_up(struct mmc_host *host) ...@@ -470,12 +484,6 @@ static void mmc_power_up(struct mmc_host *host)
host->ops->set_ios(host, &host->ios); host->ops->set_ios(host, &host->ios);
mmc_delay(2); 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) 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) ...@@ -505,6 +513,8 @@ static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
break; break;
err = MMC_ERR_TIMEOUT; err = MMC_ERR_TIMEOUT;
mmc_delay(10);
} }
if (rocr) if (rocr)
...@@ -647,12 +657,22 @@ static void mmc_setup(struct mmc_host *host) ...@@ -647,12 +657,22 @@ static void mmc_setup(struct mmc_host *host)
u32 ocr; u32 ocr;
mmc_power_up(host); mmc_power_up(host);
mmc_idle_cards(host);
err = mmc_send_op_cond(host, 0, &ocr); err = mmc_send_op_cond(host, 0, &ocr);
if (err != MMC_ERR_NONE) if (err != MMC_ERR_NONE)
return; return;
host->ocr = mmc_select_voltage(host, ocr); 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 { } else {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.clock = host->f_min; host->ios.clock = host->f_min;
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
*/ */
#define MMC_SHIFT 3 #define MMC_SHIFT 3
static int mmc_major; static int major;
/* /*
* There is one mmc_blk_data per slot. * There is one mmc_blk_data per slot.
...@@ -323,7 +323,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) ...@@ -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.issue_fn = mmc_blk_issue_rq;
md->queue.data = md; md->queue.data = md;
md->disk->major = mmc_major; md->disk->major = major;
md->disk->first_minor = devidx << MMC_SHIFT; md->disk->first_minor = devidx << MMC_SHIFT;
md->disk->fops = &mmc_bdops; md->disk->fops = &mmc_bdops;
md->disk->private_data = md; md->disk->private_data = md;
...@@ -462,14 +462,14 @@ static int __init mmc_blk_init(void) ...@@ -462,14 +462,14 @@ static int __init mmc_blk_init(void)
{ {
int res = -ENOMEM; int res = -ENOMEM;
res = register_blkdev(mmc_major, "mmc"); res = register_blkdev(major, "mmc");
if (res < 0) { if (res < 0) {
printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",
mmc_major, res); major, res);
goto out; goto out;
} }
if (mmc_major == 0) if (major == 0)
mmc_major = res; major = res;
devfs_mk_dir("mmc"); devfs_mk_dir("mmc");
return mmc_register_driver(&mmc_driver); return mmc_register_driver(&mmc_driver);
...@@ -482,7 +482,7 @@ static void __exit mmc_blk_exit(void) ...@@ -482,7 +482,7 @@ static void __exit mmc_blk_exit(void)
{ {
mmc_unregister_driver(&mmc_driver); mmc_unregister_driver(&mmc_driver);
devfs_remove("mmc"); devfs_remove("mmc");
unregister_blkdev(mmc_major, "mmc"); unregister_blkdev(major, "mmc");
} }
module_init(mmc_blk_init); module_init(mmc_blk_init);
...@@ -490,3 +490,6 @@ module_exit(mmc_blk_exit); ...@@ -490,3 +490,6 @@ module_exit(mmc_blk_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); 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");
...@@ -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->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);
}
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