Commit 2680fd5a authored by Nicolas Ferre's avatar Nicolas Ferre Committed by james toy

This new revision of the IP adds some improvements to the MCI already

present in several Atmel SOC.

Some new registers are added and a particular way of handling DMA
interaction lead to a new sequence in function call which is backward
compatible: On MCI2, we must set the DMAEN bit to enable the DMA
handshaking interface.  This must happen before the data transfer command
is sent.

A new function is able to differentiate MCI2 code and is based on
knowledge of processor id (cpu_is_xxx()).
Signed-off-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: default avatarHaavard Skinnemoen <haavard.skinnemoen@atmel.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 847029fb
...@@ -92,6 +92,7 @@ struct atmel_mci_dma { ...@@ -92,6 +92,7 @@ struct atmel_mci_dma {
* @need_clock_update: Update the clock rate before the next request. * @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request. * @need_reset: Reset controller before next request.
* @mode_reg: Value of the MR register. * @mode_reg: Value of the MR register.
* @cfg_reg: Value of the CFG register.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations. * rate and timeout calculations.
* @mapbase: Physical address of the MMIO registers. * @mapbase: Physical address of the MMIO registers.
...@@ -155,6 +156,7 @@ struct atmel_mci { ...@@ -155,6 +156,7 @@ struct atmel_mci {
bool need_clock_update; bool need_clock_update;
bool need_reset; bool need_reset;
u32 mode_reg; u32 mode_reg;
u32 cfg_reg;
unsigned long bus_hz; unsigned long bus_hz;
unsigned long mapbase; unsigned long mapbase;
struct clk *mck; struct clk *mck;
...@@ -222,6 +224,19 @@ static bool mci_has_rwproof(void) ...@@ -222,6 +224,19 @@ static bool mci_has_rwproof(void)
return true; return true;
} }
/*
* The new MCI2 module isn't 100% compatible with the old MCI module,
* and it has a few nice features which we want to use...
*/
static inline bool atmci_is_mci2(void)
{
if (cpu_is_at91sam9g45())
return true;
return false;
}
/* /*
* The debugfs stuff below is mostly optimized away when * The debugfs stuff below is mostly optimized away when
* CONFIG_DEBUG_FS is not set. * CONFIG_DEBUG_FS is not set.
...@@ -357,12 +372,33 @@ static int atmci_regs_show(struct seq_file *s, void *v) ...@@ -357,12 +372,33 @@ static int atmci_regs_show(struct seq_file *s, void *v)
buf[MCI_BLKR / 4], buf[MCI_BLKR / 4],
buf[MCI_BLKR / 4] & 0xffff, buf[MCI_BLKR / 4] & 0xffff,
(buf[MCI_BLKR / 4] >> 16) & 0xffff); (buf[MCI_BLKR / 4] >> 16) & 0xffff);
if (atmci_is_mci2())
seq_printf(s, "CSTOR:\t0x%08x\n", buf[MCI_CSTOR / 4]);
/* Don't read RSPR and RDR; it will consume the data there */ /* Don't read RSPR and RDR; it will consume the data there */
atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]); atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]); atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
if (atmci_is_mci2()) {
u32 val;
val = buf[MCI_DMA / 4];
seq_printf(s, "DMA:\t0x%08x OFFSET=%u CHKSIZE=%u%s\n",
val, val & 3,
((val >> 4) & 3) ?
1 << (((val >> 4) & 3) + 1) : 1,
val & MCI_DMAEN ? " DMAEN" : "");
val = buf[MCI_CFG / 4];
seq_printf(s, "CFG:\t0x%08x%s%s%s%s\n",
val,
val & MCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
val & MCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
val & MCI_CFG_HSMODE ? " HSMODE" : "",
val & MCI_CFG_LSYNC ? " LSYNC" : "");
}
kfree(buf); kfree(buf);
return 0; return 0;
...@@ -557,6 +593,10 @@ static void atmci_dma_complete(void *arg) ...@@ -557,6 +593,10 @@ static void atmci_dma_complete(void *arg)
dev_vdbg(&host->pdev->dev, "DMA complete\n"); dev_vdbg(&host->pdev->dev, "DMA complete\n");
if (atmci_is_mci2())
/* Disable DMA hardware handshaking on MCI */
mci_writel(host, DMA, mci_readl(host, DMA) & ~MCI_DMAEN);
atmci_dma_cleanup(host); atmci_dma_cleanup(host);
/* /*
...@@ -592,7 +632,7 @@ static void atmci_dma_complete(void *arg) ...@@ -592,7 +632,7 @@ static void atmci_dma_complete(void *arg)
} }
static int static int
atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
{ {
struct dma_chan *chan; struct dma_chan *chan;
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
...@@ -624,6 +664,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) ...@@ -624,6 +664,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
if (!chan) if (!chan)
return -ENODEV; return -ENODEV;
if (atmci_is_mci2())
mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN);
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ)
direction = DMA_FROM_DEVICE; direction = DMA_FROM_DEVICE;
else else
...@@ -641,10 +684,6 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) ...@@ -641,10 +684,6 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
host->dma.data_desc = desc; host->dma.data_desc = desc;
desc->callback = atmci_dma_complete; desc->callback = atmci_dma_complete;
desc->callback_param = host; desc->callback_param = host;
desc->tx_submit(desc);
/* Go! */
chan->device->device_issue_pending(chan);
return 0; return 0;
unmap_exit: unmap_exit:
...@@ -652,13 +691,26 @@ unmap_exit: ...@@ -652,13 +691,26 @@ unmap_exit:
return -ENOMEM; return -ENOMEM;
} }
static void atmci_submit_data(struct atmel_mci *host)
{
struct dma_chan *chan = host->data_chan;
struct dma_async_tx_descriptor *desc = host->dma.data_desc;
if (chan) {
desc->tx_submit(desc);
chan->device->device_issue_pending(chan);
}
}
#else /* CONFIG_MMC_ATMELMCI_DMA */ #else /* CONFIG_MMC_ATMELMCI_DMA */
static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) static int atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
{ {
return -ENOSYS; return -ENOSYS;
} }
static void atmci_submit_data(struct atmel_mci *host) {}
static void atmci_stop_dma(struct atmel_mci *host) static void atmci_stop_dma(struct atmel_mci *host)
{ {
/* Data transfer was stopped by the interrupt handler */ /* Data transfer was stopped by the interrupt handler */
...@@ -672,7 +724,7 @@ static void atmci_stop_dma(struct atmel_mci *host) ...@@ -672,7 +724,7 @@ static void atmci_stop_dma(struct atmel_mci *host)
* Returns a mask of interrupt flags to be enabled after the whole * Returns a mask of interrupt flags to be enabled after the whole
* request has been prepared. * request has been prepared.
*/ */
static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data) static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
{ {
u32 iflags; u32 iflags;
...@@ -683,7 +735,7 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data) ...@@ -683,7 +735,7 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
host->data = data; host->data = data;
iflags = ATMCI_DATA_ERROR_FLAGS; iflags = ATMCI_DATA_ERROR_FLAGS;
if (atmci_submit_data_dma(host, data)) { if (atmci_prepare_data_dma(host, data)) {
host->data_chan = NULL; host->data_chan = NULL;
/* /*
...@@ -729,6 +781,8 @@ static void atmci_start_request(struct atmel_mci *host, ...@@ -729,6 +781,8 @@ static void atmci_start_request(struct atmel_mci *host,
mci_writel(host, CR, MCI_CR_SWRST); mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN); mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg); mci_writel(host, MR, host->mode_reg);
if (atmci_is_mci2())
mci_writel(host, CFG, host->cfg_reg);
host->need_reset = false; host->need_reset = false;
} }
mci_writel(host, SDCR, slot->sdc_reg); mci_writel(host, SDCR, slot->sdc_reg);
...@@ -744,6 +798,7 @@ static void atmci_start_request(struct atmel_mci *host, ...@@ -744,6 +798,7 @@ static void atmci_start_request(struct atmel_mci *host,
while (!(mci_readl(host, SR) & MCI_CMDRDY)) while (!(mci_readl(host, SR) & MCI_CMDRDY))
cpu_relax(); cpu_relax();
} }
iflags = 0;
data = mrq->data; data = mrq->data;
if (data) { if (data) {
atmci_set_timeout(host, slot, data); atmci_set_timeout(host, slot, data);
...@@ -753,15 +808,17 @@ static void atmci_start_request(struct atmel_mci *host, ...@@ -753,15 +808,17 @@ static void atmci_start_request(struct atmel_mci *host,
| MCI_BLKLEN(data->blksz)); | MCI_BLKLEN(data->blksz));
dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n", dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
iflags |= atmci_prepare_data(host, data);
} }
iflags = MCI_CMDRDY; iflags |= MCI_CMDRDY;
cmd = mrq->cmd; cmd = mrq->cmd;
cmdflags = atmci_prepare_command(slot->mmc, cmd); cmdflags = atmci_prepare_command(slot->mmc, cmd);
atmci_start_command(host, cmd, cmdflags); atmci_start_command(host, cmd, cmdflags);
if (data) if (data)
iflags |= atmci_submit_data(host, data); atmci_submit_data(host);
if (mrq->stop) { if (mrq->stop) {
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop); host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
...@@ -857,6 +914,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -857,6 +914,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_enable(host->mck); clk_enable(host->mck);
mci_writel(host, CR, MCI_CR_SWRST); mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN); mci_writel(host, CR, MCI_CR_MCIEN);
if (atmci_is_mci2())
mci_writel(host, CFG, host->cfg_reg);
} }
/* /*
...@@ -1095,6 +1154,8 @@ static void atmci_detect_change(unsigned long data) ...@@ -1095,6 +1154,8 @@ static void atmci_detect_change(unsigned long data)
mci_writel(host, CR, MCI_CR_SWRST); mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN); mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg); mci_writel(host, MR, host->mode_reg);
if (atmci_is_mci2())
mci_writel(host, CFG, host->cfg_reg);
host->data = NULL; host->data = NULL;
host->cmd = NULL; host->cmd = NULL;
...@@ -1644,6 +1705,10 @@ static void atmci_configure_dma(struct atmel_mci *host) ...@@ -1644,6 +1705,10 @@ static void atmci_configure_dma(struct atmel_mci *host)
} }
if (!host->dma.chan) if (!host->dma.chan)
dev_notice(&host->pdev->dev, "DMA not available, using PIO\n"); dev_notice(&host->pdev->dev, "DMA not available, using PIO\n");
else
dev_info(&host->pdev->dev,
"Using %s for DMA transfers\n",
dma_chan_name(host->dma.chan));
} }
#else #else
static void atmci_configure_dma(struct atmel_mci *host) {} static void atmci_configure_dma(struct atmel_mci *host) {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment