aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@stericsson.com>2011-04-29 10:30:44 +0200
committerUlf HANSSON <ulf.hansson@stericsson.com>2011-04-29 10:36:07 +0200
commit69f7772b447f4cddc37a096de2c98a7dbf2b045d (patch)
treed1c92f7b31418db5d91194764a6109d92eb865c4
parent947b39ca1cc39caa60049e8083fc95929f78a609 (diff)
Revert "mmci: Implement tasklet."u8500-android-2.3_v0.68
This reverts commit 6d8c735241f9f369a11c9f2b15656951bad438a6. Change-Id: Ibb1b3f86bf2eacd254d4f1127baf09900de3812a Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21928 Reviewed-by: Ulf HANSSON <ulf.hansson@stericsson.com> Tested-by: Ulf HANSSON <ulf.hansson@stericsson.com>
-rw-r--r--drivers/mmc/host/mmci.c1347
-rw-r--r--drivers/mmc/host/mmci.h71
2 files changed, 593 insertions, 825 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 0209828435c..5a08b0fcd2a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -59,6 +59,8 @@ static unsigned int dataread_delay_clks = 7500000;
* @txsize_threshold: Sets DMA burst size to minimal if transfer size is
* less or equal to this threshold. This shall be specified in
* number of bytes. Set 0 for no burst compensation
+ * @broken_blockend: the MCI_DATABLOCKEND is broken on the hardware
+ * and will not work at all.
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @pwrreg_powerup: power up value for MMCIPOWER register
@@ -75,6 +77,7 @@ struct variant_data {
unsigned int fifosize;
unsigned int fifohalfsize;
unsigned int txsize_threshold;
+ bool broken_blockend;
bool sdio;
bool st_clkdiv;
unsigned int pwrreg_powerup;
@@ -107,13 +110,13 @@ static struct variant_data variant_ux500 = {
.clkreg_enable = 1 << 14, /* HWFCEN */
.dmareg_enable = 1 << 12, /* DMAREQCTRL */
.datalength_bits = 24,
+ .broken_blockend = true,
.sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
.non_power_of_2_blksize = true,
};
-
/*
* Debugfs
*/
@@ -192,127 +195,6 @@ static const struct file_operations mmci_fops_regs = {
.release = single_release,
};
-static int mmci_stat_show(struct seq_file *seq, void *v)
-{
- struct mmci_host *host = seq->private;
-
- seq_printf(seq, "\033[1;34mMMCI Statistic\033[0m\n");
-
- seq_printf(seq, "%-20s:%ld\n",
- "nb core requetes", host->stat.nb_core_req);
- seq_printf(seq, "%-20s:%ld\n",
- "nb cmdcrcfail", host->stat.nb_cmdcrcfail);
- seq_printf(seq, "%-20s:%ld\n",
- "nb rx overrun", host->stat.nb_rxoverrun);
- seq_printf(seq, "%-20s:%ld\n",
- "nb tx underrun", host->stat.nb_txunderrun);
- seq_printf(seq, "%-20s:%ld\n",
- "nb datacrcfail", host->stat.nb_datacrcfail);
- seq_printf(seq, "%-20s:%ld\n",
- "nb datatimeout", host->stat.nb_datatimeout);
- seq_printf(seq, "%-20s:%ld\n",
- "nb startbiterr", host->stat.nb_startbiterr);
-
- seq_printf(seq, "\n\033[1;34mMMCI Status\033[0m\n");
- seq_printf(seq, "%-20s:", "completion pending");
- switch (host->complete_what) {
- case COMPLETION_NONE:
- seq_printf(seq, "COMPLETION_NONE");
- break;
- case COMPLETION_FINALIZE:
- seq_printf(seq, "COMPLETION_FINALIZE");
- break;
- case COMPLETION_REQ:
- seq_printf(seq, "COMPLETION_REQ");
- break;
- case COMPLETION_CMDSENT:
- seq_printf(seq, "COMPLETION_CMDSENT");
- break;
- case COMPLETION_RSPFIN:
- seq_printf(seq, "COMPLETION_RSPFIN");
- break;
- case COMPLETION_XFERFINISH:
- seq_printf(seq, "COMPLETION_XFERFINISH");
- break;
- case COMPLETION_XFERFINISH_RSPFIN:
- seq_printf(seq, "COMPLETION_XFERFINISH_RSPFIN");
- break;
- default:
- seq_printf(seq, "warning not define");
- break;
- }
- seq_printf(seq, "\n");
-
- seq_printf(seq, "%-20s:", "completion dma");
- switch (host->dma_complete) {
- case COMPLETION_DMA_NONE:
- seq_printf(seq, "COMPLETION_DMA_NONE");
- break;
- case COMPLETION_DMA_START:
- seq_printf(seq, "COMPLETION_DMA_START");
- break;
- case COMPLETION_DMA_XFERFINISH:
- seq_printf(seq, "COMPLETION_DMA_XFERFINISH");
- break;
- default:
- seq_printf(seq, "warning not define");
- break;
- }
- seq_printf(seq, "\n");
-
- seq_printf(seq, "%-20s:", "pio active");
- switch (host->pio_active) {
- case XFER_NONE:
- seq_printf(seq, "XFER_NONE");
- break;
- case XFER_READ:
- seq_printf(seq, "XFER_READ");
- break;
- case XFER_WRITE:
- seq_printf(seq, "XFER_WRITE");
- break;
- default:
- seq_printf(seq, "warning not define");
- break;
- }
- seq_printf(seq, "\n");
-
- seq_printf(seq, "%-20s:", "imask0");
- seq_printf(seq, "0x%x\n", host->mmci_mask0);
- seq_printf(seq, "%-20s:", "imask1");
- seq_printf(seq, "0x%x\n", host->mmci_mask1);
-
- seq_printf(seq, "\n\033[1;34mMMCI clock\033[0m\n");
- seq_printf(seq, "%-20s:%d\n", "mclk", host->mclk);
- seq_printf(seq, "%-20s:%d\n", "cclk", host->cclk);
-
- return 0;
-}
-
-static ssize_t mmci_stat_reset(struct file *filp,
- const char __user *ubuf, size_t count, loff_t *ppos)
-{
- struct mmci_host *host =
- ((struct seq_file *)filp->private_data)->private;
-
- memset(&(host->stat), 0, sizeof(host->stat));
- return count;
-}
-
-static int mmci_stat_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mmci_stat_show, inode->i_private);
-}
-
-static const struct file_operations mmci_fops_stat = {
- .owner = THIS_MODULE,
- .open = mmci_stat_open,
- .read = seq_read,
- .write = mmci_stat_reset,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static void mmci_debugfs_create(struct mmci_host *host)
{
host->debug_regs = debugfs_create_file("regs", S_IRUGO,
@@ -322,20 +204,11 @@ static void mmci_debugfs_create(struct mmci_host *host)
if (IS_ERR(host->debug_regs))
dev_err(mmc_dev(host->mmc),
"failed to create debug regs file\n");
-
- host->debug_stat = debugfs_create_file("stat", S_IRUGO | S_IWUSR,
- host->mmc->debugfs_root, host,
- &mmci_fops_stat);
-
- if (IS_ERR(host->debug_stat))
- dev_err(mmc_dev(host->mmc),
- "failed to create debug stat file\n");
}
static void mmci_debugfs_remove(struct mmci_host *host)
{
debugfs_remove(host->debug_regs);
- debugfs_remove(host->debug_stat);
}
#else
@@ -398,43 +271,51 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
clk |= MCI_ST_8BIT_BUS;
- host->mmci_clockctrl = clk;
writel(clk, host->base + MMCICLOCK);
}
-static void mmci_set_mask1(struct mmci_host *host, u32 mask)
+static void
+mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
{
- if (host->singleirq) {
- host->mmci_mask0 &= ~MCI_IRQ1MASK;
- host->mmci_mask0 |= (mask & MCI_IRQ1MASK);
- }
- host->mmci_mask1 = mask;
-}
+ writel(0, host->base + MMCICOMMAND);
-static inline void enable_imasks(struct mmci_host *host)
-{
- writel(host->mmci_mask0, host->base + MMCIMASK0);
- writel(host->mmci_mask1, host->base + MMCIMASK1);
-}
+ BUG_ON(host->data);
-static inline void disable_imasks(struct mmci_host *host)
-{
- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
+ host->mrq = NULL;
+ host->cmd = NULL;
+
+ if (mrq->data)
+ mrq->data->bytes_xfered = host->data_xfered;
+
+ /*
+ * Need to drop the host lock here; mmc_request_done may call
+ * back into the driver...
+ */
+ spin_unlock(&host->lock);
+ mmc_request_done(host->mmc, mrq);
+ spin_lock(&host->lock);
}
-static inline void clear_imasks(struct mmci_host *host)
+static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
{
- /* preserve the SDIO IRQ mask state */
- host->mmci_mask0 &= MCI_SDIOITMASK;
- host->mmci_mask1 = 0;
- writel(host->mmci_mask0, host->base + MMCIMASK0);
- writel(host->mmci_mask1, host->base + MMCIMASK1);
+ void __iomem *base = host->base;
+
+ if (host->singleirq) {
+ unsigned int mask0 = readl(base + MMCIMASK0);
+
+ mask0 &= ~MCI_IRQ1MASK;
+ mask0 |= mask;
+
+ writel(mask0, base + MMCIMASK0);
+ }
+
+ writel(mask, base + MMCIMASK1);
}
static void mmci_stop_data(struct mmci_host *host)
{
- host->mmci_datactrl = 0;
+ u32 clk;
+ unsigned int datactrl = 0;
/*
* The ST Micro variants has a special bit
@@ -445,40 +326,99 @@ static void mmci_stop_data(struct mmci_host *host)
if (host->variant->sdio &&
host->mmc->card &&
mmc_card_sdio(host->mmc->card))
- host->mmci_datactrl |= MCI_ST_DPSM_SDIOEN;
+ datactrl |= MCI_ST_DPSM_SDIOEN;
- writel(host->mmci_datactrl, host->base + MMCIDATACTRL);
+ writel(datactrl, host->base + MMCIDATACTRL);
+ mmci_set_mask1(host, 0);
/* Needed for DDR */
if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
- host->mmci_clockctrl &= ~(MCI_NEG_EDGE);
- writel(host->mmci_clockctrl, (host->base + MMCICLOCK));
+ clk = readl(host->base + MMCICLOCK);
+ clk &= ~(MCI_NEG_EDGE);
+
+ writel(clk, (host->base + MMCICLOCK));
}
+
+ host->data = NULL;
}
static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
{
unsigned int flags = SG_MITER_ATOMIC;
- flags |= data->flags & MMC_DATA_READ ?
- SG_MITER_TO_SG : SG_MITER_FROM_SG;
+ if (data->flags & MMC_DATA_READ)
+ flags |= SG_MITER_TO_SG;
+ else
+ flags |= SG_MITER_FROM_SG;
+
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
}
+static void
+mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
+{
+ void __iomem *base = host->base;
+
+ dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
+ cmd->opcode, cmd->arg, cmd->flags);
+
+ if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
+ writel(0, base + MMCICOMMAND);
+ udelay(1);
+ }
+
+ c |= cmd->opcode | MCI_CPSM_ENABLE;
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136)
+ c |= MCI_CPSM_LONGRSP;
+ c |= MCI_CPSM_RESPONSE;
+ }
+ if (/*interrupt*/0)
+ c |= MCI_CPSM_INTERRUPT;
+
+ host->cmd = cmd;
+
+ writel(cmd->arg, base + MMCIARGUMENT);
+ writel(c, base + MMCICOMMAND);
+}
+
+static void
+mmci_complete_data_xfer(struct mmci_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ if ((host->size == 0) || data->error) {
+
+ /*
+ * Variants with broken blockend flags and as well dma
+ * transfers handles the end of the entire transfer here.
+ */
+ if (host->last_blockend && !data->error)
+ host->data_xfered = data->blksz * data->blocks;
+
+ mmci_stop_data(host);
+
+ if (!data->stop)
+ mmci_request_end(host, data->mrq);
+ else
+ mmci_start_command(host, data->stop, 0);
+ }
+}
+
/*
* All the DMA operation mode stuff goes inside this ifdef.
* This assumes that you have a generic DMA device interface,
* no custom DMA interfaces are supported.
*/
#ifdef CONFIG_DMA_ENGINE
-static int __devinit mmci_setup_dma(struct mmci_host *host)
+static void __devinit mmci_setup_dma(struct mmci_host *host)
{
struct mmci_platform_data *plat = host->plat;
dma_cap_mask_t mask;
if (!plat || !plat->dma_filter) {
dev_err(mmc_dev(host->mmc), "no DMA platform data!\n");
- return -EINVAL;
+ return;
}
/* Try to acquire a generic DMA engine slave channel */
@@ -495,7 +435,7 @@ static int __devinit mmci_setup_dma(struct mmci_host *host)
/* E.g if no DMA hardware is present */
if (!host->dma_rx_channel) {
dev_err(mmc_dev(host->mmc), "no RX DMA channel!\n");
- return -EINVAL;
+ return;
}
if (plat->dma_tx_param) {
host->dma_tx_channel = dma_request_channel(mask,
@@ -504,7 +444,7 @@ static int __devinit mmci_setup_dma(struct mmci_host *host)
if (!host->dma_tx_channel) {
dma_release_channel(host->dma_rx_channel);
host->dma_rx_channel = NULL;
- return -EINVAL;
+ return;
}
} else {
host->dma_tx_channel = host->dma_rx_channel;
@@ -513,20 +453,46 @@ static int __devinit mmci_setup_dma(struct mmci_host *host)
dev_info(mmc_dev(host->mmc), "use DMA channels DMA RX %s, DMA TX %s\n",
dma_chan_name(host->dma_rx_channel),
dma_chan_name(host->dma_tx_channel));
+}
- return 0;
+/*
+ * This is used in __devinit or __devexit so inline it
+ * so it can be discarded.
+ */
+static inline void mmci_disable_dma(struct mmci_host *host)
+{
+ if (host->dma_rx_channel)
+ dma_release_channel(host->dma_rx_channel);
+ if (host->dma_tx_channel)
+ dma_release_channel(host->dma_tx_channel);
+ host->dma_enable = false;
}
-static void mmci_dma_abort(struct mmci_host *host)
+static void mmci_dma_data_end(struct mmci_host *host)
{
- struct dma_chan *chan;
+ struct mmc_data *data = host->data;
- dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ host->dma_on_current_xfer = false;
+}
- chan = (host->mrq->data->flags & MMC_DATA_READ) ?
- host->dma_rx_channel : host->dma_tx_channel;
+static void mmci_dma_terminate(struct mmci_host *host)
+{
+ struct mmc_data *data = host->data;
+ struct dma_chan *chan;
+ dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+ if (data->flags & MMC_DATA_READ)
+ chan = host->dma_rx_channel;
+ else
+ chan = host->dma_tx_channel;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ host->dma_on_current_xfer = false;
}
static void mmci_dma_callback(void *arg)
@@ -534,47 +500,56 @@ static void mmci_dma_callback(void *arg)
unsigned long flags;
struct mmci_host *host = arg;
- dev_dbg(mmc_dev(host->mmc), "DMA transfer done!\n");
+ dev_vdbg(mmc_dev(host->mmc), "DMA transfer done!\n");
spin_lock_irqsave(&host->lock, flags);
- host->dma_complete = COMPLETION_DMA_XFERFINISH;
+ mmci_dma_data_end(host);
+
+ /* Mark that the entire data is transferred for this dma transfer. */
+ host->size = 0;
+
+ /*
+ * Make sure MMCI has received MCI_DATAEND before
+ * completing the data transfer.
+ */
+ if (host->dataend)
+ mmci_complete_data_xfer(host);
- tasklet_schedule(&host->mmci_tasklet);
spin_unlock_irqrestore(&host->lock, flags);
}
-static inline int mmci_prepare_dma(struct mmci_host *host,
- struct mmc_data *data)
+static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
{
struct variant_data *variant = host->variant;
- int burst_sz = variant->fifohalfsize >> 2; /* # of words */
struct dma_slave_config rx_conf = {
.src_addr = host->phybase + MMCIFIFO,
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.direction = DMA_FROM_DEVICE,
- .src_maxburst = burst_sz,
+ .src_maxburst = variant->fifohalfsize >> 2, /* # of words */
};
struct dma_slave_config tx_conf = {
.dst_addr = host->phybase + MMCIFIFO,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.direction = DMA_TO_DEVICE,
- .dst_maxburst = burst_sz,
+ .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
};
+ struct mmc_data *data = host->data;
enum dma_data_direction direction;
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
struct scatterlist *sg;
dma_cookie_t cookie;
int i;
+ unsigned int irqmask0;
int sg_len;
/* If less than or equal to the fifo size, don't bother with DMA */
if (host->size <= variant->fifosize)
return -EINVAL;
- host->mmci_datactrl |= MCI_DPSM_DMAENABLE;
- host->mmci_datactrl |= variant->dmareg_enable;
+ datactrl |= MCI_DPSM_DMAENABLE;
+ datactrl |= variant->dmareg_enable;
if (data->flags & MMC_DATA_READ) {
if (host->size <= variant->txsize_threshold)
@@ -597,8 +572,8 @@ static inline int mmci_prepare_dma(struct mmci_host *host,
/* Check for weird stuff in the sg list */
for_each_sg(data->sg, sg, data->sg_len, i) {
dev_vdbg(mmc_dev(host->mmc),
- "[%s] MMCI SGlist %d dir %d: length: %08x\n",
- __func__, i, direction, sg->length);
+ "MMCI SGlist %d dir %d: length: %08x\n",
+ i, direction, sg->length);
if (sg->offset & 3 || sg->length & 3)
return -EINVAL;
}
@@ -617,18 +592,30 @@ static inline int mmci_prepare_dma(struct mmci_host *host,
desc->callback = mmci_dma_callback;
desc->callback_param = host;
host->dma_desc = desc;
+ dev_vdbg(mmc_dev(host->mmc), "Submit MMCI DMA job, sglen %d "
+ "blksz %04x blks %04x flags %08x\n",
+ data->sg_len, data->blksz, data->blocks, data->flags);
cookie = desc->tx_submit(desc);
/* Here overloaded DMA controllers may fail */
if (dma_submit_error(cookie))
goto unmap_exit;
+ host->dma_on_current_xfer = true;
chan->device->device_issue_pending(chan);
- host->mmci_mask0 |= MCI_DATAENDMASK;
+ /*
+ * MMCI monitors both MCI_DATAEND and the DMA callback.
+ * Both events must occur before the transfer is considered
+ * to be completed. MCI_DATABLOCKEND is not used in DMA mode.
+ */
+ host->last_blockend = true;
+ irqmask0 = readl(host->base + MMCIMASK0);
+ irqmask0 &= ~MCI_DATABLOCKENDMASK;
+ writel(irqmask0, host->base + MMCIMASK0);
- dev_dbg(mmc_dev(host->mmc), "[%s] config dma transfert: len:%d\n",
- __func__, host->mmci_datalenght);
+ /* Trigger the DMA transfer */
+ writel(datactrl, host->base + MMCIDATACTRL);
return 0;
unmap_exit:
@@ -639,28 +626,285 @@ map_err:
}
#else
/* Blank functions if the DMA engine is not available */
-static inline int mmci_setup_dma(struct mmci_host *host)
+static inline void mmci_setup_dma(struct mmci_host *host)
+{
+}
+
+static inline void mmci_disable_dma(struct mmci_host *host)
+{
+}
+
+static inline void mmci_dma_data_end(struct mmci_host *host)
{
- return -1;
}
-static inline int mmci_prepare_dma(struct mmci_host *host,
- struct mmc_data *data)
+static inline void mmci_dma_terminate(struct mmci_host *host)
{
- return -1;
}
-static inline void mmci_dma_abort(struct mmci_host *host)
+
+static inline int mmci_dma_start_data(struct mmci_host *host,
+ unsigned int datactrl)
{
+ return -ENOSYS;
}
#endif
-static inline void mmci_disable_dma(struct mmci_host *host)
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
{
- if (host->dma_rx_channel)
- dma_release_channel(host->dma_rx_channel);
- if (host->dma_tx_channel)
- dma_release_channel(host->dma_tx_channel);
- host->dma_enable = false;
+ struct variant_data *variant = host->variant;
+ unsigned int datactrl, timeout, irqmask0, irqmask1;
+ unsigned int clkcycle_ns;
+ void __iomem *base;
+ int blksz_bits;
+ u32 clk;
+
+ dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
+ data->blksz, data->blocks, data->flags);
+
+ host->data = data;
+ host->size = data->blksz * data->blocks;
+ host->data_xfered = 0;
+ host->last_blockend = false;
+ host->dataend = false;
+ host->cache_len = 0;
+ host->cache = 0;
+
+ clkcycle_ns = 1000000000 / host->cclk;
+ timeout = data->timeout_ns / clkcycle_ns;
+ timeout += data->timeout_clks;
+
+ if (data->flags & MMC_DATA_READ) {
+ /*
+ * Since the read command is sent after we have setup
+ * the data transfer we must increase the data timeout.
+ * Unfortunately this is not enough since some cards
+ * does not seem to stick to what is stated in their
+ * CSD for TAAC and NSAC.
+ */
+ timeout += dataread_delay_clks;
+ }
+
+ base = host->base;
+ writel(timeout, base + MMCIDATATIMER);
+ writel(host->size, base + MMCIDATALENGTH);
+
+ blksz_bits = ffs(data->blksz) - 1;
+
+#ifdef CONFIG_ARCH_U8500
+ /* Temporary solution for db8500v2. */
+ if (cpu_is_u8500v20_or_later())
+ datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
+ else
+#endif
+ datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
+
+ if (data->flags & MMC_DATA_READ)
+ datactrl |= MCI_DPSM_DIRECTION;
+
+ if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
+ datactrl |= MCI_ST_DPSM_DDRMODE;
+
+ /* Needed for DDR */
+ clk = readl(base + MMCICLOCK);
+ clk |= MCI_NEG_EDGE;
+
+ writel(clk, (base + MMCICLOCK));
+ }
+
+ if (variant->sdio &&
+ host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO mode. This bit is set the first time
+ * a SDIO data transfer is done and must remain set
+ * after the data transfer is completed. The reason is
+ * because of otherwise no SDIO interrupts can be
+ * received.
+ */
+ datactrl |= MCI_ST_DPSM_SDIOEN;
+
+ /*
+ * The ST Micro variant for SDIO transfer sizes
+ * less than or equal to 8 bytes needs to have clock
+ * H/W flow control disabled. Since flow control is
+ * not really needed for anything that fits in the
+ * FIFO, we can disable it for any write smaller
+ * than the FIFO size.
+ */
+ if ((host->size <= variant->fifosize) &&
+ (data->flags & MMC_DATA_WRITE))
+ writel(readl(host->base + MMCICLOCK) &
+ ~variant->clkreg_enable,
+ host->base + MMCICLOCK);
+ else
+ writel(readl(host->base + MMCICLOCK) |
+ variant->clkreg_enable,
+ host->base + MMCICLOCK);
+ }
+
+ if (host->dma_enable) {
+ int ret;
+
+ /*
+ * Attempt to use DMA operation mode, if this
+ * should fail, fall back to PIO mode
+ */
+ ret = mmci_dma_start_data(host, datactrl);
+ if (!ret)
+ return;
+ }
+
+ /* IRQ mode, map the SG list for CPU reading/writing */
+ mmci_init_sg(host, data);
+
+ if (data->flags & MMC_DATA_READ) {
+ irqmask1 = MCI_RXFIFOHALFFULLMASK;
+
+ /*
+ * If we have less than a FIFOSIZE of bytes to
+ * transfer, trigger a PIO interrupt as soon as any
+ * data is available.
+ */
+ if (host->size < variant->fifosize)
+ irqmask1 |= MCI_RXDATAAVLBLMASK;
+ } else {
+ /*
+ * We don't actually need to include "FIFO empty" here
+ * since its implicit in "FIFO half empty".
+ */
+ irqmask1 = MCI_TXFIFOHALFEMPTYMASK;
+ }
+
+ /* Setup IRQ */
+ irqmask0 = readl(base + MMCIMASK0);
+ if (variant->broken_blockend) {
+ host->last_blockend = true;
+ irqmask0 &= ~MCI_DATABLOCKENDMASK;
+ } else {
+ irqmask0 |= MCI_DATABLOCKENDMASK;
+ }
+ writel(irqmask0, base + MMCIMASK0);
+ mmci_set_mask1(host, irqmask1);
+
+ /* Start the data transfer */
+ writel(datactrl, base + MMCIDATACTRL);
+}
+
+static void
+mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
+ unsigned int status)
+{
+ struct variant_data *variant = host->variant;
+
+ /* First check for errors */
+ if (status & MCI_DATA_ERR) {
+ dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n",
+ status);
+ if (status & MCI_DATACRCFAIL)
+ data->error = -EILSEQ;
+ else if (status & MCI_DATATIMEOUT)
+ data->error = -ETIMEDOUT;
+ else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+ data->error = -EIO;
+
+ /*
+ * We hit an error condition. Ensure that any data
+ * partially written to a page is properly coherent,
+ * unless we're using DMA.
+ */
+ if (host->dma_on_current_xfer)
+ mmci_dma_terminate(host);
+ else if (data->flags & MMC_DATA_READ) {
+ struct sg_mapping_iter *sg_miter = &host->sg_miter;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (sg_miter_next(sg_miter)) {
+ flush_dcache_page(sg_miter->page);
+ sg_miter_stop(sg_miter);
+ }
+ local_irq_restore(flags);
+ }
+ }
+
+ /*
+ * On ARM variants in PIO mode, MCI_DATABLOCKEND
+ * is always sent first, and we increase the
+ * transfered number of bytes for that IRQ. Then
+ * MCI_DATAEND follows and we conclude the transaction.
+ *
+ * On the Ux500 single-IRQ variant MCI_DATABLOCKEND
+ * doesn't seem to immediately clear from the status,
+ * so we can't use it keep count when only one irq is
+ * used because the irq will hit for other reasons, and
+ * then the flag is still up. So we use the MCI_DATAEND
+ * IRQ at the end of the entire transfer because
+ * MCI_DATABLOCKEND is broken.
+ *
+ * In the U300, the IRQs can arrive out-of-order,
+ * e.g. MCI_DATABLOCKEND sometimes arrives after MCI_DATAEND,
+ * so for this case we use the flags "last_blockend" and
+ * "dataend" to make sure both IRQs have arrived before
+ * concluding the transaction. (This does not apply
+ * to the Ux500 which doesn't fire MCI_DATABLOCKEND
+ * at all.) In DMA mode it suffers from the same problem
+ * as the Ux500.
+ */
+ if (status & MCI_DATABLOCKEND) {
+ /*
+ * Just being a little over-cautious, we do not
+ * use this progressive update if the hardware blockend
+ * flag is unreliable: since it can stay high between
+ * IRQs it will corrupt the transfer counter.
+ */
+ if (!variant->broken_blockend && !host->dma_on_current_xfer) {
+ host->data_xfered += data->blksz;
+
+ if (host->data_xfered == data->blksz * data->blocks)
+ host->last_blockend = true;
+ }
+ }
+
+ if (status & MCI_DATAEND)
+ host->dataend = true;
+
+ /*
+ * On variants with broken blockend we shall only wait for dataend,
+ * on others we must sync with the blockend signal since they can
+ * appear out-of-order.
+ */
+ if ((host->dataend && host->last_blockend) || data->error)
+ mmci_complete_data_xfer(host);
+}
+
+static void
+mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
+ unsigned int status)
+{
+ void __iomem *base = host->base;
+
+ host->cmd = NULL;
+
+ cmd->resp[0] = readl(base + MMCIRESPONSE0);
+ cmd->resp[1] = readl(base + MMCIRESPONSE1);
+ cmd->resp[2] = readl(base + MMCIRESPONSE2);
+ cmd->resp[3] = readl(base + MMCIRESPONSE3);
+
+ if (status & MCI_CMDTIMEOUT)
+ cmd->error = -ETIMEDOUT;
+ else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC)
+ cmd->error = -EILSEQ;
+
+ if (!cmd->data || cmd->error) {
+ if (host->data) {
+ if (host->dma_on_current_xfer)
+ mmci_dma_terminate(host);
+ mmci_stop_data(host);
+ }
+ mmci_request_end(host, cmd->mrq);
+ } else if (!(cmd->data->flags & MMC_DATA_READ))
+ mmci_start_data(host, cmd->data);
}
static int mmci_pio_read(struct mmci_host *host, char *buffer,
@@ -812,429 +1056,153 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer,
return ptr - buffer;
}
-static void mmci_start_data(struct mmci_host *host)
-{
- writel(host->mmci_clockctrl, host->base + MMCICLOCK);
- writel(host->mmci_datatimer, host->base + MMCIDATATIMER);
- writel(host->mmci_datalenght, host->base + MMCIDATALENGTH);
- writel(host->mmci_datactrl, host->base + MMCIDATACTRL);
-}
-
/*
* PIO data transfer IRQ handler.
*/
static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
- u32 mmci_status;
-
- mmci_status = readl(host->base + MMCISTATUS);
-
- if ((host->pio_active == XFER_WRITE) &&
- (mmci_status & MCI_TXFIFOHALFEMPTYMASK)) {
- dev_dbg(mmc_dev(host->mmc), "pio tx\n");
- host->mmci_mask1 &= ~MCI_TXFIFOHALFEMPTYMASK;
- } else if ((host->pio_active == XFER_READ) &&
- (mmci_status & MCI_RXDATAAVLBL)) {
- dev_dbg(mmc_dev(host->mmc), "pio rx\n");
- host->mmci_mask1 &= ~(MCI_RXDATAAVLBL
- | MCI_RXFIFOHALFFULLMASK);
- } else
- goto irq_out;
-
- mmci_set_mask1(host, host->mmci_mask1);
- enable_imasks(host);
- tasklet_schedule(&host->mmci_tasklet);
-
-irq_out:
- return IRQ_HANDLED;
-}
-
-/*
- * Handle completion of command and data transfers.
- */
-static irqreturn_t mmci_irq(int irq, void *dev_id)
-{
- struct mmci_host *host = dev_id;
- struct mmc_command *cmd;
- u32 mmci_status, mmci_mask, mmci_iclear = 0;
- unsigned long iflags;
- int sdio_irq = 0;
-
- spin_lock_irqsave(&host->lock, iflags);
-
- mmci_status = readl(host->base + MMCISTATUS);
- mmci_mask = readl(host->base + MMCIMASK0);
-
- if (mmci_status & mmci_mask & MCI_SDIOIT) {
- /* clear status, defer handling until after other interrupts */
- writel(MCI_SDIOITC, host->base + MMCICLEAR);
- sdio_irq = 1;
- }
-
- if ((host->complete_what == COMPLETION_NONE) ||
- (host->complete_what == COMPLETION_FINALIZE)) {
- dev_dbg(mmc_dev(host->mmc), "nothing to complete\n");
- clear_imasks(host);
- goto irq_out;
- }
+ struct sg_mapping_iter *sg_miter = &host->sg_miter;
+ struct variant_data *variant = host->variant;
+ void __iomem *base = host->base;
+ unsigned long flags;
+ u32 status;
- if (!host->mrq) {
- dev_dbg(mmc_dev(host->mmc), "no active mrq\n");
- clear_imasks(host);
- goto irq_out;
- }
+ status = readl(base + MMCISTATUS);
- cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
- if (!cmd) {
- dev_dbg(mmc_dev(host->mmc), "no active cmd\n");
- clear_imasks(host);
- goto irq_out;
- }
+ dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
- if (host->singleirq)
- if (mmci_status & (MCI_TXFIFOHALFEMPTYMASK | MCI_RXDATAAVLBL))
- mmci_pio_irq(irq, dev_id);
+ local_irq_save(flags);
- if (mmci_status & MCI_CMDTIMEOUT) {
- dev_dbg(mmc_dev(host->mmc), "error: CMDTIMEOUT\n");
- cmd->error = -ETIMEDOUT;
- goto fail_transfer;
- }
+ do {
+ unsigned int remain, len;
+ char *buffer;
- if (mmci_status & MCI_CMDCRCFAIL) {
- mmci_iclear |= MCI_CMDCRCFAILCLR;
- if (cmd->flags & MMC_RSP_CRC) {
- dev_dbg(mmc_dev(host->mmc), "error: CMDCRCFAIL\n");
- host->stat.nb_cmdcrcfail++;
- cmd->error = -EILSEQ;
- goto fail_transfer;
- }
- /* When you send a cmd that not check the crc
- * ( without MMC_RSP_CRC flags example cmd41)
+ /*
+ * 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.
*/
- goto close_transfer;
- }
-
- if (mmci_status & MCI_CMDSENT) {
- mmci_iclear |= MCI_CMDSENTCLR;
- if (host->complete_what == COMPLETION_CMDSENT) {
- dev_dbg(mmc_dev(host->mmc), "ok: command sent\n");
- goto close_transfer;
- }
- }
-
- if (mmci_status & MCI_CMDRESPEND) {
- mmci_iclear |= MCI_CMDRESPENDCLR;
- if (host->complete_what == COMPLETION_RSPFIN) {
- dev_dbg(mmc_dev(host->mmc),
- "ok: command response received\n");
- goto close_transfer;
- }
- if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
- dev_dbg(mmc_dev(host->mmc),
- "command response received, "
- "wait data end\n");
- host->complete_what = COMPLETION_XFERFINISH;
- if (cmd->data->flags & MMC_DATA_WRITE)
- mmci_start_data(host);
- }
- }
-
- /* errors handled after this point are only relevant
- when a data transfer is in progress */
- if (!cmd->data)
- goto clear_status_bits;
-
- if (mmci_status & MCI_DATACRCFAIL) {
- dev_dbg(mmc_dev(host->mmc), "error: DATACRCFAIL\n");
- host->stat.nb_datacrcfail++;
- cmd->data->error = -EILSEQ;
- goto fail_transfer;
- }
- if (mmci_status & MCI_DATATIMEOUT) {
- dev_dbg(mmc_dev(host->mmc), "error: DATATIMEOUT\n");
- host->stat.nb_datatimeout++;
- cmd->data->error = -ETIMEDOUT;
- goto fail_transfer;
- }
- if (mmci_status & MCI_RXOVERRUN) {
- dev_dbg(mmc_dev(host->mmc), "error: RXOVERRUN\n");
- host->stat.nb_rxoverrun++;
- cmd->data->error = -EIO;
- goto fail_transfer;
- }
- if (mmci_status & MCI_TXUNDERRUN) {
- dev_dbg(mmc_dev(host->mmc), "error: TXUNDERRUN\n");
- host->stat.nb_txunderrun++;
- cmd->data->error = -EIO;
- goto fail_transfer;
- }
- if (mmci_status & MCI_STARTBITERR) {
- dev_dbg(mmc_dev(host->mmc), "error: STARTBITERR\n");
- host->stat.nb_startbiterr++;
- cmd->data->error = -EIO;
- goto fail_transfer;
- }
-
- if (mmci_status & MCI_DATAEND) {
- if (host->complete_what == COMPLETION_XFERFINISH) {
- dev_dbg(mmc_dev(host->mmc),
- "ok: data transfer completed\n");
- mmci_iclear |= MCI_DATAENDCLR;
- goto close_transfer;
- }
- if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
- dev_dbg(mmc_dev(host->mmc),
- "data transfer completed,"
- "wait cmd respend\n");
- host->complete_what = COMPLETION_RSPFIN;
- }
- mmci_iclear |= MCI_DATAENDCLR;
- }
-
-clear_status_bits:
- writel(mmci_iclear & 0xFFF, host->base + MMCICLEAR);
- goto irq_out;
-
-fail_transfer:
- host->pio_active = XFER_NONE;
-
-close_transfer:
- host->complete_what = COMPLETION_FINALIZE;
- clear_imasks(host);
- tasklet_schedule(&host->mmci_tasklet);
-
-irq_out:
- dev_dbg(mmc_dev(host->mmc), "[%s] mmci_status:%04x mmci_mask0:%04x\n",
- __func__, mmci_status, mmci_mask);
- spin_unlock_irqrestore(&host->lock, iflags);
-
- if (sdio_irq)
- mmc_signal_sdio_irq(host->mmc);
-
- return IRQ_HANDLED;
-}
-
-static int mmci_setup_data(struct mmci_host *host, struct mmc_data *data)
-{
- unsigned int clkcycle_ns;
- int blksz_bits;
-
- dev_dbg(mmc_dev(host->mmc), "[%s] blksz:%d nb_blk:%d sg_len:%d "
- "ptr_sg:%p flags %08x\n", __func__, data->blksz,
- data->blocks, data->sg_len, data->sg, data->flags);
-
- host->size = data->blksz * data->blocks;
- host->pio_active = XFER_NONE;
+ if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
+ break;
- clkcycle_ns = 1000000000 / host->cclk;
- host->mmci_datatimer = data->timeout_ns / clkcycle_ns;
- host->mmci_datatimer += data->timeout_clks;
+ if (!sg_miter_next(sg_miter))
+ break;
- blksz_bits = ffs(data->blksz) - 1;
+ buffer = sg_miter->addr;
+ remain = sg_miter->length;
- /*FIXME shift must be define */
-#ifdef CONFIG_ARCH_U8500
- /* Temporary solution for db8500v2. */
- if (cpu_is_u8500v20_or_later())
- host->mmci_datactrl = data->blksz << 16;
- else
-#endif
- host->mmci_datactrl = blksz_bits << 4;
+ 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);
- if (data->flags & MMC_DATA_READ) {
- host->mmci_datactrl |= MCI_DPSM_DIRECTION;
- host->mmci_datatimer += dataread_delay_clks;
- }
+ sg_miter->consumed = len;
- host->mmci_datactrl |= MCI_DPSM_ENABLE;
+ host->size -= len;
+ remain -= len;
- /* Needed for DDR */
- if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
- host->mmci_datactrl |= MCI_ST_DPSM_DDRMODE;
- host->mmci_clockctrl |= MCI_NEG_EDGE;
- }
+ if (remain)
+ break;
- host->mmci_datalenght = data->blksz * data->blocks;
+ if (status & MCI_RXACTIVE)
+ flush_dcache_page(sg_miter->page);
- /* irq mask */
- host->mmci_mask0 = MCI_DATACRCFAILMASK | MCI_DATATIMEOUTMASK |
- MCI_TXUNDERRUNMASK | MCI_RXOVERRUNMASK | MCI_STARTBITERRMASK;
+ status = readl(base + MMCISTATUS);
+ } while (1);
- return 0;
-}
+ sg_miter_stop(sg_miter);
-static inline void mmci_prepare_sdio(struct mmci_host *host,
- struct mmc_data *data)
-{
- struct variant_data *variant = host->variant;
+ local_irq_restore(flags);
- /* The ST Micro variants:
- * -has a special bit to enable SDIO.
- * -for SDIO transfer sizes less then 8 bytes
- * should have clock H/W flow control disabled.
+ /*
+ * If we're nearing the end of the read, switch to
+ * "any data available" mode.
*/
- host->mmci_clockctrl |= variant->clkreg_enable;
- if (variant->sdio && host->mmc->card) {
- if (mmc_card_sdio(host->mmc->card)) {
- host->mmci_datactrl |= MCI_ST_DPSM_SDIOEN;
- if ((host->size <= variant->fifosize) &&
- (data->flags & MMC_DATA_WRITE))
- host->mmci_clockctrl &= ~variant->clkreg_enable;
- else
- host->mmci_clockctrl |= variant->clkreg_enable;
- }
- }
-}
-
-static inline void mmci_prepare_pio(struct mmci_host *host,
- struct mmc_data *data)
-{
- struct variant_data *variant = host->variant;
- u32 irqmask1 = 0;
- int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
- host->cache_len = 0;
- host->cache = 0;
-
- host->pio_active = rw ? XFER_WRITE : XFER_READ;
+ if (status & MCI_RXACTIVE && host->size < variant->fifosize)
+ mmci_set_mask1(host, MCI_RXDATAAVLBLMASK);
- /* IRQ mode, map the SG list for CPU reading/writing */
- mmci_init_sg(host, data);
+ /* If we run out of data, disable the data IRQs. */
+ if (host->size == 0) {
+ mmci_set_mask1(host, 0);
- /* FIXME
- * if write case, perhaps we can preload the fifo
- */
- if (data->flags & MMC_DATA_READ) {
- irqmask1 |= MCI_RXFIFOHALFFULLMASK;
/*
- * If we have less than a FIFOSIZE of bytes to
- * transfer, trigger a PIO interrupt as soon as any
- * data is available.
+ * If we already received MCI_DATAEND and the last
+ * MCI_DATABLOCKEND, the entire data transfer shall
+ * be completed.
*/
- if (host->size < variant->fifosize)
- irqmask1 |= MCI_RXDATAAVLBLMASK;
- } else {
- /*
- * We don't actually need to include "FIFO empty" here
- * since its implicit in "FIFO half empty".
- */
- irqmask1 |= MCI_TXFIFOHALFEMPTYMASK;
+ if (host->dataend && host->last_blockend)
+ mmci_complete_data_xfer(host);
}
- host->mmci_mask0 &= ~MCI_DATAENDMASK;
- mmci_set_mask1(host, irqmask1);
-}
-
-static void mmci_prepare_data(struct mmci_host *host,
- struct mmc_data *data)
-{
- int res = 0;
-
- mmci_prepare_sdio(host, data);
-
- /*
- * Attempt to use DMA operation mode, if this
- * should fail, fall back to PIO mode
- */
- if (host->dma_enable) {
- res = mmci_prepare_dma(host, data);
- if (!res) {
- host->dma_complete = COMPLETION_DMA_START;
- return;
- } else {
- host->dma_complete = COMPLETION_DMA_NONE;
- dev_dbg(mmc_dev(host->mmc),
- "prepare dma error %d.\n", res);
- host->mmci_datactrl &= ~MCI_DPSM_DMAENABLE;
- host->mmci_datactrl &= ~host->variant->dmareg_enable;
- }
- }
-
- mmci_prepare_pio(host, data);
+ return IRQ_HANDLED;
}
-static void mmci_prepare_command(struct mmci_host *host,
- struct mmc_command *cmd)
+/*
+ * Handle completion of command and data transfers.
+ */
+static irqreturn_t mmci_irq(int irq, void *dev_id)
{
- host->mmci_mask0 |= MCI_CMDTIMEOUTMASK | MCI_CMDCRCFAILMASK;
+ struct mmci_host *host = dev_id;
+ u32 status;
+ int sdio_irq = 0;
+ int ret = 0;
- if (cmd->data)
- host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
- else if (cmd->flags & MMC_RSP_PRESENT)
- host->complete_what = COMPLETION_RSPFIN;
- else
- host->complete_what = COMPLETION_CMDSENT;
+ spin_lock(&host->lock);
- host->mmci_argument = cmd->arg;
- host->mmci_command = cmd->opcode | MCI_CPSM_ENABLE;
+ do {
+ struct mmc_command *cmd;
+ struct mmc_data *data;
- if (cmd->flags & MMC_RSP_PRESENT) {
- host->mmci_command |= MCI_CPSM_RESPONSE;
- host->mmci_mask0 |= MCI_CMDRESPENDMASK;
- if (cmd->flags & MMC_RSP_136)
- host->mmci_command |= MCI_CPSM_LONGRSP;
- } else
- host->mmci_mask0 |= MCI_CMDSENTMASK;
-}
+ status = readl(host->base + MMCISTATUS);
-static void mmci_send_command(struct mmci_host *host)
-{
- writel(host->mmci_argument, host->base + MMCIARGUMENT);
- writel(host->mmci_command, host->base + MMCICOMMAND);
-}
+ if (host->singleirq) {
+ if (status & readl(host->base + MMCIMASK1))
+ mmci_pio_irq(irq, dev_id);
-static void mmci_send_request(struct mmc_host *mmc)
-{
- struct mmci_host *host = mmc_priv(mmc);
- struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
- int res;
- unsigned long flags;
+ status &= ~MCI_IRQ1MASK;
+ }
- spin_lock_irqsave(&host->lock, flags);
+ status &= readl(host->base + MMCIMASK0);
+ writel(status, host->base + MMCICLEAR);
- /* Clear mmci status and mask registers */
- writel(0x7ff, host->base + MMCICLEAR);
- clear_imasks(host);
+ dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
- if (cmd->data) {
- res = mmci_setup_data(host, cmd->data);
- if (res) {
- dev_err(mmc_dev(mmc), "data setup error %d.\n", res);
- goto err_data;
- }
+ if (status & MCI_SDIOIT)
+ sdio_irq = 1;
- mmci_prepare_data(host, cmd->data);
+ data = host->data;
+ if (status & MCI_DATA_IRQ && data)
+ mmci_data_irq(host, data, status);
- if (cmd->data->flags & MMC_DATA_READ)
- mmci_start_data(host);
- }
+ cmd = host->cmd;
+ if (status & MCI_CMD_IRQ && cmd)
+ mmci_cmd_irq(host, cmd, status);
- mmci_prepare_command(host, cmd);
- enable_imasks(host);
- mmci_send_command(host);
+ ret = 1;
+ } while (status);
- spin_unlock_irqrestore(&host->lock, flags);
+ spin_unlock(&host->lock);
- return;
+ if (sdio_irq)
+ mmc_signal_sdio_irq(host->mmc);
-err_data:
- cmd->error = res;
- cmd->data->error = res;
- mmc_request_done(mmc, mrq);
- return;
+ return IRQ_RETVAL(ret);
}
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mmci_host *host = mmc_priv(mmc);
- host->cmd_is_stop = 0;
+ struct variant_data *variant = host->variant;
+ unsigned long flags;
WARN_ON(host->mrq != NULL);
if (mrq->data &&
- (!host->variant->non_power_of_2_blksize ||
+ (!variant->non_power_of_2_blksize ||
#ifdef CONFIG_ARCH_U8500
!cpu_is_u8500v20_or_later() ||
#endif
@@ -1247,9 +1215,16 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
return;
}
+ spin_lock_irqsave(&host->lock, flags);
+
host->mrq = mrq;
- host->stat.nb_core_req++;
- mmci_send_request(mmc);
+
+ if (mrq->data && mrq->data->flags & MMC_DATA_READ)
+ mmci_start_data(host, mrq->data);
+
+ mmci_start_command(host, mrq->cmd, 0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
}
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -1349,7 +1324,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->pwr != pwr) {
host->pwr = pwr;
- host->mmci_power = pwr;
writel(pwr, host->base + MMCIPOWER);
}
@@ -1411,9 +1385,10 @@ static int mmci_enable(struct mmc_host *mmc)
spin_lock_irqsave(&host->lock, flags);
- /* Restore registers for POWER and CLOCK. */
- writel(host->mmci_clockctrl, host->base + MMCICLOCK);
- writel(host->mmci_power, host->base + MMCIPOWER);
+ /* Restore registers for POWER, CLOCK and IRQMASK0 */
+ writel(host->clk_reg, host->base + MMCICLOCK);
+ writel(host->pwr_reg, host->base + MMCIPOWER);
+ writel(host->irqmask0_reg, host->base + MMCIMASK0);
if (host->variant->sdio &&
host->mmc->card &&
@@ -1423,13 +1398,9 @@ static int mmci_enable(struct mmc_host *mmc)
* register to enable SDIO mode. This bit must be set otherwise
* no SDIO interrupts can be received.
*/
- host->mmci_datactrl = MCI_ST_DPSM_SDIOEN;
- writel(host->mmci_datactrl, host->base + MMCIDATACTRL);
+ writel(MCI_ST_DPSM_SDIOEN, host->base + MMCIDATACTRL);
}
- /* SDIO IT is re-enabled, if there are any subcribers */
- enable_imasks(host);
-
spin_unlock_irqrestore(&host->lock, flags);
/*
@@ -1456,12 +1427,17 @@ static int mmci_disable(struct mmc_host *mmc, int lazy)
spin_lock_irqsave(&host->lock, flags);
+ /* Save registers for POWER, CLOCK and IRQMASK0 */
+ host->irqmask0_reg = readl(host->base + MMCIMASK0);
+ host->pwr_reg = readl(host->base + MMCIPOWER);
+ host->clk_reg = readl(host->base + MMCICLOCK);
+
/*
* Make sure we do not get any interrupts when we disabled the
* clock and the regulator and as well make sure to clear the
* registers for clock and power.
*/
- disable_imasks(host);
+ writel(0, host->base + MMCIMASK0);
writel(0, host->base + MMCIPOWER);
writel(0, host->base + MMCICLOCK);
@@ -1494,191 +1470,21 @@ static irqreturn_t mmci_cd_irq(int irq, void *dev_id)
static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
unsigned long flags;
+ unsigned int mask0;
struct mmci_host *host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
- if (enable) {
- /*
- * Since the host is not claimed when doing enable
- * we must handle it here.
- */
- host->mmci_mask0 |= MCI_SDIOITMASK;
- } else {
- /* We assumes the host is claimed when doing disable. */
- host->mmci_mask0 &= ~MCI_SDIOITMASK;
- }
- enable_imasks(host);
-
- spin_unlock_irqrestore(&host->lock, flags);
-}
-
-/*
- * Tasklet
- */
-static void mmci_pio_xferdata(struct mmci_host *host)
-{
- struct sg_mapping_iter *sg_miter = &host->sg_miter;
- u32 mmci_status;
- u32 remain, len;
- char *buffer;
- unsigned long flags;
-
- dev_dbg(mmc_dev(host->mmc), "[%s]\n", __func__);
-
- local_irq_save(flags);
-
- do {
- mmci_status = readl(host->base + MMCISTATUS);
- /*
- * 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 (!(mmci_status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
- break;
-
- if (!sg_miter_next(sg_miter))
- break;
-
- buffer = sg_miter->addr;
- remain = sg_miter->length;
- len = 0;
-
- if (mmci_status & MCI_RXACTIVE)
- len = mmci_pio_read(host, buffer, remain);
- if (mmci_status & MCI_TXACTIVE)
- len = mmci_pio_write(host, buffer,
- remain, mmci_status);
-
- sg_miter->consumed = len;
- host->size -= len;
- remain -= len;
-
- if (remain)
- break;
- } while (1);
-
- sg_miter_stop(sg_miter);
-
- local_irq_restore(flags);
-
- if (mmci_status & MCI_RXACTIVE) {
- host->mmci_mask1 |= MCI_RXFIFOHALFFULLMASK;
- /*
- * If we're nearing the end of the read, switch to
- * "any data available" mode.
- */
- if (host->size < host->variant->fifosize)
- host->mmci_mask1 |= MCI_RXDATAAVLBLMASK;
- }
-
- if (mmci_status & MCI_TXACTIVE)
- host->mmci_mask1 |= MCI_TXFIFOHALFEMPTYMASK;
-
- /*
- * If we run out of data, disable the data IRQs; this
- * prevents a race where the FIFO becomes empty before
- * the chip itself has disabled the data path, and
- * stops us racing with our data end IRQ.
- */
- if (host->size == 0) {
- host->mmci_mask1 = 0;
- host->mmci_mask0 |= MCI_DATAENDMASK;
- }
-}
-
-static void mmci_finalize_request(struct mmci_host *host)
-{
- struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd;
-
- if (!mrq) {
- dev_err(mmc_dev(host->mmc), "request Missing!\n");
- return;
- }
-
- cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
- if (cmd->data && !cmd->error && !cmd->data->error)
- if (host->dma_enable &&
- host->dma_complete == COMPLETION_DMA_START)
- return; /* wait dma completion or datatimeout */
-
- /* Read response from controller. */
- cmd->resp[0] = readl(host->base + MMCIRESPONSE0);
- cmd->resp[1] = readl(host->base + MMCIRESPONSE1);
- cmd->resp[2] = readl(host->base + MMCIRESPONSE2);
- cmd->resp[3] = readl(host->base + MMCIRESPONSE3);
-
- /* Cleanup controller */
- writel(0, host->base + MMCICOMMAND);
- writel(0, host->base + MMCIARGUMENT);
- writel(0xFFF, host->base + MMCICLEAR);
- clear_imasks(host);
-
- if (cmd->data && cmd->error)
- cmd->data->error = cmd->error;
-
- /* If we have no data transfer we are finished here */
- if (!mrq->data)
- goto request_done;
-
- mmci_stop_data(host);
-
- if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
- host->cmd_is_stop = 1;
- mmci_send_request(host->mmc);
- return;
- }
-
- /*if dma used and finish or error*/
- if (host->dma_enable && host->dma_complete != COMPLETION_DMA_NONE) {
- if (host->dma_complete == COMPLETION_DMA_START)
- mmci_dma_abort(host); /* dma error */
-
- dma_unmap_sg(mmc_dev(host->mmc), mrq->data->sg,
- mrq->data->sg_len,
- (mrq->data->flags & MMC_DATA_WRITE)
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- host->dma_complete = COMPLETION_DMA_NONE;
- }
-
- /* Calulate the amout of bytes transfer if there was no error */
- if (mrq->data->error == 0)
- mrq->data->bytes_xfered = mrq->data->blocks * mrq->data->blksz;
+ mask0 = readl(host->base + MMCIMASK0);
+ if (enable)
+ mask0 |= MCI_SDIOIT;
else
- mrq->data->bytes_xfered = 0;
-
-request_done:
- host->complete_what = COMPLETION_NONE;
- host->mrq = NULL;
- mmc_request_done(host->mmc, mrq);
- return;
+ mask0 &= ~MCI_SDIOIT;
+ writel(mask0, host->base + MMCIMASK0);
+ spin_unlock_irqrestore(&host->lock, flags);
}
-static void mmci_tasklet(unsigned long data)
-{
- struct mmci_host *host = (struct mmci_host *) data;
-
- dev_dbg(mmc_dev(host->mmc), "[%s]\n", __func__);
-
- if (host->pio_active != XFER_NONE) {
- mmci_pio_xferdata(host);
- mmci_set_mask1(host, host->mmci_mask1);
- }
-
- if (host->complete_what == COMPLETION_FINALIZE)
- mmci_finalize_request(host);
- else
- enable_imasks(host);
-}
-
-/*
- * Init,register,unregister host functions to core mmc
- */
static const struct mmc_host_ops mmci_ops = {
.request = mmci_request,
.set_ios = mmci_set_ios,
@@ -1722,6 +1528,10 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host->gpio_cd = -ENOSYS;
host->gpio_cd_irq = -1;
+ host->irqmask0_reg = 0;
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
+
host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev);
dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer);
@@ -1885,11 +1695,9 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
&& host->gpio_cd_irq < 0)
mmc->caps |= MMC_CAP_NEEDS_POLL;
- ret = mmci_setup_dma(host);
- if (ret)
- mmci_disable_dma(host);
+ mmci_setup_dma(host);
- ret = request_irq(dev->irq[0], mmci_irq, 0,
+ ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED,
DRIVER_NAME " (cmd)", host);
if (ret)
goto unmap;
@@ -1897,12 +1705,17 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
if (dev->irq[1] == NO_IRQ)
host->singleirq = true;
else {
- ret = request_irq(dev->irq[1], mmci_pio_irq, 0,
+ ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED,
DRIVER_NAME " (pio)", host);
if (ret)
goto irq0_free;
}
+ /* Prepare IRQMASK0 */
+ host->irqmask0_reg = MCI_IRQENABLE;
+ if (host->variant->broken_blockend)
+ host->irqmask0_reg &= ~MCI_DATABLOCKEND;
+
amba_set_drvdata(dev, mmc);
pm_runtime_enable(mmc->parent);
@@ -1911,11 +1724,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
if (pm_runtime_put_sync(mmc->parent) < 0)
dev_err(mmc_dev(mmc), "failed pm_runtime_put_sync\n");
- ret = mmc_add_host(mmc);
- if (ret) {
- dev_err(mmc_dev(host->mmc), "failed to add mmc host.\n");
- goto irq0_free;
- }
+ mmc_add_host(mmc);
dev_info(&dev->dev,
"%s: MMCI/PL180 manf %x rev %x cfg %02x at 0x%016llx\n",
@@ -1926,9 +1735,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
/* Ugly hack for u8500_sdio_detect_card, to be removed soon. */
sdio_host_ptr = host;
- tasklet_init(&host->mmci_tasklet,
- mmci_tasklet, (unsigned long) host);
-
mmci_debugfs_create(host);
return 0;
@@ -1976,7 +1782,8 @@ static int __devexit mmci_remove(struct amba_device *dev)
mmci_debugfs_remove(host);
mmc_remove_host(mmc);
- disable_imasks(host);
+ writel(0, host->base + MMCIMASK0);
+ writel(0, host->base + MMCIMASK1);
writel(0, host->base + MMCICOMMAND);
writel(0, host->base + MMCIDATACTRL);
@@ -2043,8 +1850,8 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
mmc_host_enable(mmc);
mmc_power_save_host(mmc);
mmc_host_disable(mmc);
- host->mmci_power = 0;
- host->mmci_clockctrl = 0;
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
}
} else {
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 58107ed4a19..87d35cc1fea 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -78,7 +78,6 @@
#define MCI_CMDRESPEND (1 << 6)
#define MCI_CMDSENT (1 << 7)
#define MCI_DATAEND (1 << 8)
-#define MCI_STARTBITERR (1 << 9)
#define MCI_DATABLOCKEND (1 << 10)
#define MCI_CMDACTIVE (1 << 11)
#define MCI_TXACTIVE (1 << 12)
@@ -104,7 +103,6 @@
#define MCI_CMDRESPENDCLR (1 << 6)
#define MCI_CMDSENTCLR (1 << 7)
#define MCI_DATAENDCLR (1 << 8)
-#define MCI_STARTBITERRCLR (1 << 9)
#define MCI_DATABLOCKENDCLR (1 << 10)
#define MCI_SDIOITC (1 << 22)
#define MCI_CEATAENDC (1 << 23)
@@ -119,7 +117,6 @@
#define MCI_CMDRESPENDMASK (1 << 6)
#define MCI_CMDSENTMASK (1 << 7)
#define MCI_DATAENDMASK (1 << 8)
-#define MCI_STARTBITERRMASK (1 << 9)
#define MCI_DATABLOCKENDMASK (1 << 10)
#define MCI_CMDACTIVEMASK (1 << 11)
#define MCI_TXACTIVEMASK (1 << 12)
@@ -144,6 +141,12 @@
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK)
+#define MCI_DATA_ERR \
+ (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)
+#define MCI_DATA_IRQ (MCI_DATA_ERR|MCI_DATAEND|MCI_DATABLOCKEND)
+#define MCI_CMD_ERR (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)
+#define MCI_CMD_IRQ (MCI_CMD_ERR|MCI_CMDRESPEND|MCI_CMDSENT)
+
/* These interrupts are directed to IRQ1 when two IRQ lines are available */
#define MCI_IRQ1MASK \
(MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \
@@ -156,37 +159,12 @@ struct variant_data;
struct dma_chan;
struct dma_async_tx_descriptor;
-enum mmci_dma_complete{
- COMPLETION_DMA_NONE,
- COMPLETION_DMA_START,
- COMPLETION_DMA_XFERFINISH,
-};
-
-enum mmci_waitfor {
- COMPLETION_NONE,
- COMPLETION_FINALIZE,
- COMPLETION_REQ,
- COMPLETION_CMDSENT,
- COMPLETION_RSPFIN,
- COMPLETION_XFERFINISH,
- COMPLETION_XFERFINISH_RSPFIN,
-};
-
-struct mmci_stat{
- unsigned long nb_core_req;
- unsigned long nb_cmdcrcfail;
- unsigned long nb_rxoverrun;
- unsigned long nb_txunderrun;
- unsigned long nb_datacrcfail;
- unsigned long nb_datatimeout;
- unsigned long nb_startbiterr;
-};
-
struct mmci_host {
phys_addr_t phybase;
void __iomem *base;
struct mmc_request *mrq;
struct mmc_command *cmd;
+ struct mmc_data *data;
struct mmc_host *mmc;
struct clk *clk;
int gpio_cd;
@@ -210,6 +188,14 @@ struct mmci_host {
struct timer_list timer;
unsigned int oldstat;
+ bool last_blockend;
+ bool dataend;
+
+ /* register cache */
+ unsigned int irqmask0_reg;
+ unsigned int pwr_reg;
+ unsigned int clk_reg;
+
/* pio stuff */
struct sg_mapping_iter sg_miter;
unsigned int size;
@@ -228,33 +214,8 @@ struct mmci_host {
struct dma_async_tx_descriptor *dma_desc;
#endif
- struct tasklet_struct mmci_tasklet;
- enum mmci_waitfor complete_what;
- enum mmci_dma_complete dma_complete;
-
- int cmd_is_stop;
-
-#define XFER_NONE 0
-#define XFER_READ 1
-#define XFER_WRITE 2
- u32 pio_active;
-
- /* mmci registers*/
- u32 mmci_command;
- u32 mmci_argument;
-
- u32 mmci_mask0;
- u32 mmci_mask1;
-
- u32 mmci_datatimer;
- u32 mmci_datalenght;
- u32 mmci_datactrl;
- u32 mmci_clockctrl;
- u32 mmci_power;
-
- struct mmci_stat stat;
#ifdef CONFIG_DEBUG_FS
struct dentry *debug_regs;
- struct dentry *debug_stat;
#endif
};
+