Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74791a2d authored by Nicolas Ferre's avatar Nicolas Ferre Committed by Linus Torvalds
Browse files

mmc: atmel-mci: new MCI2 module support in atmel-mci driver



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>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 2635d1ba
Loading
Loading
Loading
Loading
+75 −10
Original line number Original line Diff line number Diff line
@@ -94,6 +94,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.
@@ -157,6 +158,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;
@@ -224,6 +226,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.
@@ -359,12 +374,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;
@@ -559,6 +595,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);


	/*
	/*
@@ -594,7 +634,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;
@@ -626,6 +666,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
@@ -643,10 +686,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:
@@ -654,13 +693,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 */
@@ -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
 * 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;


@@ -685,7 +737,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;


		/*
		/*
@@ -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_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);
@@ -746,6 +800,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);
@@ -755,15 +810,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);
@@ -859,6 +916,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);
		}
		}


		/*
		/*
@@ -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_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;
@@ -1620,6 +1681,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) {}