aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorLudovic Barre <ludovic.barre@stericsson.com>2011-03-25 09:23:57 +0100
committerSebastian RASMUSSEN <sebastian.rasmussen@stericsson.com>2011-04-07 16:06:16 +0200
commitbe851980f0f3170bb73086feb170fe46b5009c6b (patch)
treecf292266cf30060a693e1173cd490261421e769c /drivers/mmc
parentcc21061ee4be97fec29cba762411d4948fc2f008 (diff)
mmci: tasklet context, data transfer and finalize
ST-Ericsson Linux next: N/A ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I5b9ed019d249491e6194674ab8396269ebfae8b2 Signed-off-by: Ludovic Barre <ludovic.barre@stericsson.com> Change-Id: I5b9ed019d249491e6194674ab8396269ebfae8b2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17180 Reviewed-by: Sebastian RASMUSSEN <sebastian.rasmussen@stericsson.com> Tested-by: Sebastian RASMUSSEN <sebastian.rasmussen@stericsson.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/mmci.c229
1 files changed, 172 insertions, 57 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 08a3de80466..36d1391f891 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -112,6 +112,7 @@ static struct variant_data variant_ux500 = {
.signal_direction = true,
.non_power_of_2_blksize = true,
};
+
/*
* Debugfs
*/
@@ -267,29 +268,6 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
writel(clk, host->base + MMCICLOCK);
}
-static void
-mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
-{
- writel(0, host->base + MMCICOMMAND);
-
- BUG_ON(host->data);
-
- 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 void mmci_set_mask1(struct mmci_host *host, u32 mask)
{
if (host->singleirq) {
@@ -322,8 +300,7 @@ static inline void clear_imasks(struct mmci_host *host)
static void mmci_stop_data(struct mmci_host *host)
{
- u32 clk;
- unsigned int datactrl = 0;
+ host->mmci_datactrl = 0;
/*
* The ST Micro variants has a special bit
@@ -334,42 +311,14 @@ static void mmci_stop_data(struct mmci_host *host)
if (host->variant->sdio &&
host->mmc->card &&
mmc_card_sdio(host->mmc->card))
- datactrl |= MCI_ST_DPSM_SDIOEN;
+ host->mmci_datactrl |= MCI_ST_DPSM_SDIOEN;
- writel(datactrl, host->base + MMCIDATACTRL);
- mmci_set_mask1(host, 0);
+ writel(host->mmci_datactrl, host->base + MMCIDATACTRL);
/* Needed for DDR */
if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
- clk = readl(host->base + MMCICLOCK);
- clk &= ~(MCI_NEG_EDGE);
-
- writel(clk, (host->base + MMCICLOCK));
- }
-
- host->data = NULL;
-}
-
-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);
+ host->mmci_clockctrl &= ~(MCI_NEG_EDGE);
+ writel(host->mmci_clockctrl, (host->base + MMCICLOCK));
}
}
@@ -1445,6 +1394,172 @@ static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
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;
+ else
+ mrq->data->bytes_xfered = 0;
+
+request_done:
+ host->complete_what = COMPLETION_NONE;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+ return;
+
+}
+
+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,