Skip to content

Commit

Permalink
mmc: atmel-mci: new MCI2 module support in atmel-mci driver
Browse files Browse the repository at this point in the history
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: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Nicolas Ferre authored and Linus Torvalds committed Dec 15, 2009
1 parent 2635d1b commit 74791a2
Showing 1 changed file with 75 additions and 10 deletions.
85 changes: 75 additions & 10 deletions drivers/mmc/host/atmel-mci.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct atmel_mci_dma {
* @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request.
* @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
* rate and timeout calculations.
* @mapbase: Physical address of the MMIO registers.
Expand Down Expand Up @@ -157,6 +158,7 @@ struct atmel_mci {
bool need_clock_update;
bool need_reset;
u32 mode_reg;
u32 cfg_reg;
unsigned long bus_hz;
unsigned long mapbase;
struct clk *mck;
Expand Down Expand Up @@ -224,6 +226,19 @@ static bool mci_has_rwproof(void)
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
* CONFIG_DEBUG_FS is not set.
Expand Down Expand Up @@ -359,12 +374,33 @@ static int atmci_regs_show(struct seq_file *s, void *v)
buf[MCI_BLKR / 4],
buf[MCI_BLKR / 4] & 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 */

atmci_show_status_reg(s, "SR", buf[MCI_SR / 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);

return 0;
Expand Down Expand Up @@ -559,6 +595,10 @@ static void atmci_dma_complete(void *arg)

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);

/*
Expand Down Expand Up @@ -594,7 +634,7 @@ static void atmci_dma_complete(void *arg)
}

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_async_tx_descriptor *desc;
Expand Down Expand Up @@ -626,6 +666,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
if (!chan)
return -ENODEV;

if (atmci_is_mci2())
mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN);

if (data->flags & MMC_DATA_READ)
direction = DMA_FROM_DEVICE;
else
Expand All @@ -643,24 +686,33 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
host->dma.data_desc = desc;
desc->callback = atmci_dma_complete;
desc->callback_param = host;
desc->tx_submit(desc);

/* Go! */
chan->device->device_issue_pending(chan);

return 0;
unmap_exit:
dma_unmap_sg(&host->pdev->dev, data->sg, sglen, direction);
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 */

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;
}

static void atmci_submit_data(struct atmel_mci *host) {}

static void atmci_stop_dma(struct atmel_mci *host)
{
/* Data transfer was stopped by the interrupt handler */
Expand All @@ -674,7 +726,7 @@ static void atmci_stop_dma(struct atmel_mci *host)
* Returns a mask of interrupt flags to be enabled after the whole
* 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;

Expand All @@ -685,7 +737,7 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
host->data = data;

iflags = ATMCI_DATA_ERROR_FLAGS;
if (atmci_submit_data_dma(host, data)) {
if (atmci_prepare_data_dma(host, data)) {
host->data_chan = NULL;

/*
Expand Down Expand Up @@ -731,6 +783,8 @@ static void atmci_start_request(struct atmel_mci *host,
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg);
if (atmci_is_mci2())
mci_writel(host, CFG, host->cfg_reg);
host->need_reset = false;
}
mci_writel(host, SDCR, slot->sdc_reg);
Expand All @@ -746,6 +800,7 @@ static void atmci_start_request(struct atmel_mci *host,
while (!(mci_readl(host, SR) & MCI_CMDRDY))
cpu_relax();
}
iflags = 0;
data = mrq->data;
if (data) {
atmci_set_timeout(host, slot, data);
Expand All @@ -755,15 +810,17 @@ static void atmci_start_request(struct atmel_mci *host,
| MCI_BLKLEN(data->blksz));
dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));

iflags |= atmci_prepare_data(host, data);
}

iflags = MCI_CMDRDY;
iflags |= MCI_CMDRDY;
cmd = mrq->cmd;
cmdflags = atmci_prepare_command(slot->mmc, cmd);
atmci_start_command(host, cmd, cmdflags);

if (data)
iflags |= atmci_submit_data(host, data);
atmci_submit_data(host);

if (mrq->stop) {
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
Expand Down Expand Up @@ -859,6 +916,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_enable(host->mck);
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN);
if (atmci_is_mci2())
mci_writel(host, CFG, host->cfg_reg);
}

/*
Expand Down Expand Up @@ -1097,6 +1156,8 @@ static void atmci_detect_change(unsigned long data)
mci_writel(host, CR, MCI_CR_SWRST);
mci_writel(host, CR, MCI_CR_MCIEN);
mci_writel(host, MR, host->mode_reg);
if (atmci_is_mci2())
mci_writel(host, CFG, host->cfg_reg);

host->data = NULL;
host->cmd = NULL;
Expand Down Expand Up @@ -1620,6 +1681,10 @@ static void atmci_configure_dma(struct atmel_mci *host)
}
if (!host->dma.chan)
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
static void atmci_configure_dma(struct atmel_mci *host) {}
Expand Down

0 comments on commit 74791a2

Please sign in to comment.