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)
yield();
mdelay(ms);
} else {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(ms * HZ / 1000);
msleep_interruptible (ms);
}
}
......@@ -450,12 +449,27 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
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.
*/
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;
......
......@@ -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");
......@@ -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->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