diff options
author | Ulf Hansson <ulf.hansson@stericsson.com> | 2011-04-29 10:30:44 +0200 |
---|---|---|
committer | Ulf HANSSON <ulf.hansson@stericsson.com> | 2011-04-29 10:36:07 +0200 |
commit | 69f7772b447f4cddc37a096de2c98a7dbf2b045d (patch) | |
tree | d1c92f7b31418db5d91194764a6109d92eb865c4 | |
parent | 947b39ca1cc39caa60049e8083fc95929f78a609 (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.c | 1347 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.h | 71 |
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 }; + |