Skip to content
This repository has been archived by the owner on Sep 24, 2020. It is now read-only.

Commit

Permalink
mmc: pxamci: switch over to dmaengine use
Browse files Browse the repository at this point in the history
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: Daniel Mack <zonque@gmail.com>
[adapted to pxa-dma]
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
  • Loading branch information
zonque authored and storulf committed Aug 17, 2015
1 parent 642c28a commit 6464b71
Showing 1 changed file with 114 additions and 86 deletions.
200 changes: 114 additions & 86 deletions drivers/mmc/host/pxamci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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"
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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;

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(mmc_dev(host->mmc), data->sg, data->sg_len,
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;
}
host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
sizeof(struct pxa_dma_desc);
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[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;
if (!(data->flags & MMC_DATA_READ)) {
tx->callback = pxamci_dma_irq;
tx->callback_param = host;
}

host->dma_cookie = dmaengine_submit(tx);

/*
* workaround for erratum #91:
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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);

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

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_drcmrrx = dmarx->start;

dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!dmatx) {
ret = -ENXIO;
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;
Expand Down Expand Up @@ -814,12 +843,12 @@ static int pxamci_probe(struct platform_device *pdev)
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);
}
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 6464b71

Please sign in to comment.