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

Commit 6464b714 authored by Daniel Mack's avatar Daniel Mack Committed by Ulf Hansson
Browse files

mmc: pxamci: switch over to dmaengine use



Switch over pxamci to dmaengine. This prepares the devicetree full
support of pxamci.

This was successfully tested on a PXA3xx board, as well as PXA27x.

Signed-off-by: default avatarDaniel Mack <zonque@gmail.com>
[adapted to pxa-dma]
Signed-off-by: default avatarRobert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 642c28ab
Loading
Loading
Loading
Loading
+114 −86
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/pxa-dma.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
@@ -37,7 +39,6 @@
#include <asm/sizes.h>

#include <mach/hardware.h>
#include <mach/dma.h>
#include <linux/platform_data/mmc-pxamci.h>

#include "pxamci.h"
@@ -58,7 +59,6 @@ struct pxamci_host {
	struct clk		*clk;
	unsigned long		clkrate;
	int			irq;
	int			dma;
	unsigned int		clkrt;
	unsigned int		cmdat;
	unsigned int		imask;
@@ -69,8 +69,10 @@ struct pxamci_host {
	struct mmc_command	*cmd;
	struct mmc_data		*data;

	struct dma_chan		*dma_chan_rx;
	struct dma_chan		*dma_chan_tx;
	dma_cookie_t		dma_cookie;
	dma_addr_t		sg_dma;
	struct pxa_dma_desc	*sg_cpu;
	unsigned int		dma_len;

	unsigned int		dma_dir;
@@ -173,14 +175,18 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
	spin_unlock_irqrestore(&host->lock, flags);
}

static void pxamci_dma_irq(void *param);

static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
{
	struct dma_async_tx_descriptor *tx;
	enum dma_data_direction direction;
	struct dma_slave_config	config;
	struct dma_chan *chan;
	unsigned int nob = data->blocks;
	unsigned long long clks;
	unsigned int timeout;
	bool dalgn = 0;
	u32 dcmd;
	int i;
	int ret;

	host->data = data;

@@ -195,54 +201,48 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
	timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
	writel((timeout + 255) / 256, host->base + MMC_RDTO);

	memset(&config, 0, sizeof(config));
	config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
	config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
	config.src_addr = host->res->start + MMC_RXFIFO;
	config.dst_addr = host->res->start + MMC_TXFIFO;
	config.src_maxburst = 32;
	config.dst_maxburst = 32;

	if (data->flags & MMC_DATA_READ) {
		host->dma_dir = DMA_FROM_DEVICE;
		dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC;
		DRCMR(host->dma_drcmrtx) = 0;
		DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;
		direction = DMA_DEV_TO_MEM;
		chan = host->dma_chan_rx;
	} else {
		host->dma_dir = DMA_TO_DEVICE;
		dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
		DRCMR(host->dma_drcmrrx) = 0;
		DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
		direction = DMA_MEM_TO_DEV;
		chan = host->dma_chan_tx;
	}

	dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
	config.direction = direction;

	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
	ret = dmaengine_slave_config(chan, &config);
	if (ret < 0) {
		dev_err(mmc_dev(host->mmc), "dma slave config failed\n");
		return;
	}

	host->dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
				   host->dma_dir);

	for (i = 0; i < host->dma_len; i++) {
		unsigned int length = sg_dma_len(&data->sg[i]);
		host->sg_cpu[i].dcmd = dcmd | length;
		if (length & 31 && !(data->flags & MMC_DATA_READ))
			host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
		/* Not aligned to 8-byte boundary? */
		if (sg_dma_address(&data->sg[i]) & 0x7)
			dalgn = 1;
		if (data->flags & MMC_DATA_READ) {
			host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
			host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
		} else {
			host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
			host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
	tx = dmaengine_prep_slave_sg(chan, data->sg, host->dma_len, direction,
				     DMA_PREP_INTERRUPT);
	if (!tx) {
		dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
		return;
	}
		host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
					sizeof(struct pxa_dma_desc);

	if (!(data->flags & MMC_DATA_READ)) {
		tx->callback = pxamci_dma_irq;
		tx->callback_param = host;
	}
	host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
	wmb();

	/*
	 * The PXA27x DMA controller encounters overhead when working with
	 * unaligned (to 8-byte boundaries) data, so switch on byte alignment
	 * mode only if we have unaligned data.
	 */
	if (dalgn)
		DALGN |= (1 << host->dma);
	else
		DALGN &= ~(1 << host->dma);
	DDADR(host->dma) = host->sg_dma;
	host->dma_cookie = dmaengine_submit(tx);

	/*
	 * workaround for erratum #91:
@@ -251,7 +251,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
	 * before starting DMA.
	 */
	if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ)
		DCSR(host->dma) = DCSR_RUN;
		dma_async_issue_pending(chan);
}

static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
@@ -343,7 +343,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
		 * enable DMA late
		 */
		if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE)
			DCSR(host->dma) = DCSR_RUN;
			dma_async_issue_pending(host->dma_chan_tx);
	} else {
		pxamci_finish_request(host, host->mrq);
	}
@@ -354,13 +354,17 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
{
	struct mmc_data *data = host->data;
	struct dma_chan *chan;

	if (!data)
		return 0;

	DCSR(host->dma) = 0;
	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
		     host->dma_dir);
	if (data->flags & MMC_DATA_READ)
		chan = host->dma_chan_rx;
	else
		chan = host->dma_chan_tx;
	dma_unmap_sg(chan->device->dev,
		     data->sg, data->sg_len, host->dma_dir);

	if (stat & STAT_READ_TIME_OUT)
		data->error = -ETIMEDOUT;
@@ -552,20 +556,37 @@ static const struct mmc_host_ops pxamci_ops = {
	.enable_sdio_irq	= pxamci_enable_sdio_irq,
};

static void pxamci_dma_irq(int dma, void *devid)
static void pxamci_dma_irq(void *param)
{
	struct pxamci_host *host = devid;
	int dcsr = DCSR(dma);
	DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
	struct pxamci_host *host = param;
	struct dma_tx_state state;
	enum dma_status status;
	struct dma_chan *chan;
	unsigned long flags;

	spin_lock_irqsave(&host->lock, flags);

	if (!host->data)
		goto out_unlock;

	if (dcsr & DCSR_ENDINTR) {
	if (host->data->flags & MMC_DATA_READ)
		chan = host->dma_chan_rx;
	else
		chan = host->dma_chan_tx;

	status = dmaengine_tx_status(chan, host->dma_cookie, &state);

	if (likely(status == DMA_COMPLETE)) {
		writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
	} else {
		pr_err("%s: DMA error on channel %d (DCSR=%#x)\n",
		       mmc_hostname(host->mmc), dma, dcsr);
		pr_err("%s: DMA error on %s channel\n", mmc_hostname(host->mmc),
			host->data->flags & MMC_DATA_READ ? "rx" : "tx");
		host->data->error = -EIO;
		pxamci_data_done(host, 0);
	}

out_unlock:
	spin_unlock_irqrestore(&host->lock, flags);
}

static irqreturn_t pxamci_detect_irq(int irq, void *devid)
@@ -625,7 +646,9 @@ static int pxamci_probe(struct platform_device *pdev)
	struct mmc_host *mmc;
	struct pxamci_host *host = NULL;
	struct resource *r, *dmarx, *dmatx;
	struct pxad_param param_rx, param_tx;
	int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
	dma_cap_mask_t mask;

	ret = pxamci_of_init(pdev);
	if (ret)
@@ -671,7 +694,6 @@ static int pxamci_probe(struct platform_device *pdev)

	host = mmc_priv(mmc);
	host->mmc = mmc;
	host->dma = -1;
	host->pdata = pdev->dev.platform_data;
	host->clkrt = CLKRT_OFF;

@@ -702,12 +724,6 @@ static int pxamci_probe(struct platform_device *pdev)
				     MMC_CAP_SD_HIGHSPEED;
	}

	host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
	if (!host->sg_cpu) {
		ret = -ENOMEM;
		goto out;
	}

	spin_lock_init(&host->lock);
	host->res = r;
	host->irq = irq;
@@ -728,32 +744,45 @@ static int pxamci_probe(struct platform_device *pdev)
	writel(64, host->base + MMC_RESTO);
	writel(host->imask, host->base + MMC_I_MASK);

	host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
				    pxamci_dma_irq, host);
	if (host->dma < 0) {
		ret = -EBUSY;
		goto out;
	}

	ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
	if (ret)
		goto out;

	platform_set_drvdata(pdev, mmc);

	if (!pdev->dev.of_node) {
		dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
	if (!dmarx) {
		dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
		if (!dmarx || !dmatx) {
			ret = -ENXIO;
			goto out;
		}
	host->dma_drcmrrx = dmarx->start;
		param_rx.prio = PXAD_PRIO_LOWEST;
		param_rx.drcmr = dmarx->start;
		param_tx.prio = PXAD_PRIO_LOWEST;
		param_tx.drcmr = dmatx->start;
	}

	dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
	if (!dmatx) {
		ret = -ENXIO;
	dma_cap_zero(mask);
	dma_cap_set(DMA_SLAVE, mask);

	host->dma_chan_rx =
		dma_request_slave_channel_compat(mask, pxad_filter_fn,
						 &param_rx, &pdev->dev, "rx");
	if (host->dma_chan_rx == NULL) {
		dev_err(&pdev->dev, "unable to request rx dma channel\n");
		ret = -ENODEV;
		goto out;
	}

	host->dma_chan_tx =
		dma_request_slave_channel_compat(mask, pxad_filter_fn,
						 &param_tx,  &pdev->dev, "tx");
	if (host->dma_chan_tx == NULL) {
		dev_err(&pdev->dev, "unable to request tx dma channel\n");
		ret = -ENODEV;
		goto out;
	}
	host->dma_drcmrtx = dmatx->start;

	if (host->pdata) {
		gpio_cd = host->pdata->gpio_card_detect;
@@ -814,12 +843,12 @@ err_gpio_ro:
	gpio_free(gpio_power);
 out:
	if (host) {
		if (host->dma >= 0)
			pxa_free_dma(host->dma);
		if (host->dma_chan_rx)
			dma_release_channel(host->dma_chan_rx);
		if (host->dma_chan_tx)
			dma_release_channel(host->dma_chan_tx);
		if (host->base)
			iounmap(host->base);
		if (host->sg_cpu)
			dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
		if (host->clk)
			clk_put(host->clk);
	}
@@ -863,13 +892,12 @@ static int pxamci_remove(struct platform_device *pdev)
		       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
		       host->base + MMC_I_MASK);

		DRCMR(host->dma_drcmrrx) = 0;
		DRCMR(host->dma_drcmrtx) = 0;

		free_irq(host->irq, host);
		pxa_free_dma(host->dma);
		dmaengine_terminate_all(host->dma_chan_rx);
		dmaengine_terminate_all(host->dma_chan_tx);
		dma_release_channel(host->dma_chan_rx);
		dma_release_channel(host->dma_chan_tx);
		iounmap(host->base);
		dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);

		clk_put(host->clk);