mmc: mxcmmc: use dmaengine API
This switches the mxcmmc driver to use the dmaengine API. Unlike the old one this one is always present in the tree, even if no DMA is implemented, hence we can remove all the #ifdefs in from the driver. The driver automatically switches to PIO mode if no DMA support or no suitable channel is available. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
parent
c9b2a06fb0
commit
f53fbde48e
|
@ -32,16 +32,14 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
|
||||||
#include <asm/dma.h>
|
#include <asm/dma.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/sizes.h>
|
#include <asm/sizes.h>
|
||||||
#include <mach/mmc.h>
|
#include <mach/mmc.h>
|
||||||
|
|
||||||
#ifdef CONFIG_ARCH_MX2
|
#include <mach/dma.h>
|
||||||
#include <mach/dma-mx1-mx2.h>
|
|
||||||
#define HAS_DMA
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DRIVER_NAME "mxc-mmc"
|
#define DRIVER_NAME "mxc-mmc"
|
||||||
|
|
||||||
|
@ -118,7 +116,8 @@ struct mxcmci_host {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq;
|
int irq;
|
||||||
int detect_irq;
|
int detect_irq;
|
||||||
int dma;
|
struct dma_chan *dma;
|
||||||
|
struct dma_async_tx_descriptor *desc;
|
||||||
int do_dma;
|
int do_dma;
|
||||||
int default_irq_mask;
|
int default_irq_mask;
|
||||||
int use_sdio;
|
int use_sdio;
|
||||||
|
@ -129,7 +128,6 @@ struct mxcmci_host {
|
||||||
struct mmc_command *cmd;
|
struct mmc_command *cmd;
|
||||||
struct mmc_data *data;
|
struct mmc_data *data;
|
||||||
|
|
||||||
unsigned int dma_nents;
|
|
||||||
unsigned int datasize;
|
unsigned int datasize;
|
||||||
unsigned int dma_dir;
|
unsigned int dma_dir;
|
||||||
|
|
||||||
|
@ -144,6 +142,11 @@ struct mxcmci_host {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
struct regulator *vcc;
|
struct regulator *vcc;
|
||||||
|
|
||||||
|
int burstlen;
|
||||||
|
int dmareq;
|
||||||
|
struct dma_slave_config dma_slave_config;
|
||||||
|
struct imx_dma_data dma_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
|
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
|
||||||
|
@ -206,17 +209,16 @@ static void mxcmci_softreset(struct mxcmci_host *host)
|
||||||
|
|
||||||
writew(0xff, host->base + MMC_REG_RES_TO);
|
writew(0xff, host->base + MMC_REG_RES_TO);
|
||||||
}
|
}
|
||||||
|
static int mxcmci_setup_dma(struct mmc_host *mmc);
|
||||||
|
|
||||||
static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
unsigned int nob = data->blocks;
|
unsigned int nob = data->blocks;
|
||||||
unsigned int blksz = data->blksz;
|
unsigned int blksz = data->blksz;
|
||||||
unsigned int datasize = nob * blksz;
|
unsigned int datasize = nob * blksz;
|
||||||
#ifdef HAS_DMA
|
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
int i;
|
int i, nents;
|
||||||
int ret;
|
|
||||||
#endif
|
|
||||||
if (data->flags & MMC_DATA_STREAM)
|
if (data->flags & MMC_DATA_STREAM)
|
||||||
nob = 0xffff;
|
nob = 0xffff;
|
||||||
|
|
||||||
|
@ -227,7 +229,9 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||||
writew(blksz, host->base + MMC_REG_BLK_LEN);
|
writew(blksz, host->base + MMC_REG_BLK_LEN);
|
||||||
host->datasize = datasize;
|
host->datasize = datasize;
|
||||||
|
|
||||||
#ifdef HAS_DMA
|
if (!mxcmci_use_dma(host))
|
||||||
|
return 0;
|
||||||
|
|
||||||
for_each_sg(data->sg, sg, data->sg_len, i) {
|
for_each_sg(data->sg, sg, data->sg_len, i) {
|
||||||
if (sg->offset & 3 || sg->length & 3) {
|
if (sg->offset & 3 || sg->length & 3) {
|
||||||
host->do_dma = 0;
|
host->do_dma = 0;
|
||||||
|
@ -235,34 +239,30 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->flags & MMC_DATA_READ) {
|
if (data->flags & MMC_DATA_READ)
|
||||||
host->dma_dir = DMA_FROM_DEVICE;
|
host->dma_dir = DMA_FROM_DEVICE;
|
||||||
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
else
|
||||||
data->sg_len, host->dma_dir);
|
|
||||||
|
|
||||||
ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents,
|
|
||||||
datasize,
|
|
||||||
host->res->start + MMC_REG_BUFFER_ACCESS,
|
|
||||||
DMA_MODE_READ);
|
|
||||||
} else {
|
|
||||||
host->dma_dir = DMA_TO_DEVICE;
|
host->dma_dir = DMA_TO_DEVICE;
|
||||||
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
||||||
data->sg_len, host->dma_dir);
|
|
||||||
|
|
||||||
ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents,
|
nents = dma_map_sg(host->dma->device->dev, data->sg,
|
||||||
datasize,
|
data->sg_len, host->dma_dir);
|
||||||
host->res->start + MMC_REG_BUFFER_ACCESS,
|
if (nents != data->sg_len)
|
||||||
DMA_MODE_WRITE);
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
if (ret) {
|
host->desc = host->dma->device->device_prep_slave_sg(host->dma,
|
||||||
dev_err(mmc_dev(host->mmc), "failed to setup DMA : %d\n", ret);
|
data->sg, data->sg_len, host->dma_dir,
|
||||||
return ret;
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
|
||||||
|
if (!host->desc) {
|
||||||
|
dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
|
||||||
|
host->dma_dir);
|
||||||
|
host->do_dma = 0;
|
||||||
|
return 0; /* Fall back to PIO */
|
||||||
}
|
}
|
||||||
wmb();
|
wmb();
|
||||||
|
|
||||||
imx_dma_enable(host->dma);
|
dmaengine_submit(host->desc);
|
||||||
#endif /* HAS_DMA */
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,13 +337,11 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
int data_error;
|
int data_error;
|
||||||
|
|
||||||
#ifdef HAS_DMA
|
|
||||||
if (mxcmci_use_dma(host)) {
|
if (mxcmci_use_dma(host)) {
|
||||||
imx_dma_disable(host->dma);
|
dmaengine_terminate_all(host->dma);
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
|
dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
|
||||||
host->dma_dir);
|
host->dma_dir);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (stat & STATUS_ERR_MASK) {
|
if (stat & STATUS_ERR_MASK) {
|
||||||
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
|
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
|
||||||
|
@ -545,7 +543,6 @@ static void mxcmci_datawork(struct work_struct *work)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_DMA
|
|
||||||
static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
|
static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
|
||||||
{
|
{
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
|
@ -568,7 +565,6 @@ static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
|
||||||
mxcmci_finish_request(host, host->req);
|
mxcmci_finish_request(host, host->req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* HAS_DMA */
|
|
||||||
|
|
||||||
static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
|
static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
|
||||||
{
|
{
|
||||||
|
@ -606,12 +602,10 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
||||||
sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio;
|
sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
#ifdef HAS_DMA
|
|
||||||
if (mxcmci_use_dma(host) &&
|
if (mxcmci_use_dma(host) &&
|
||||||
(stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)))
|
(stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)))
|
||||||
writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
||||||
host->base + MMC_REG_STATUS);
|
host->base + MMC_REG_STATUS);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sdio_irq) {
|
if (sdio_irq) {
|
||||||
writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_REG_STATUS);
|
writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_REG_STATUS);
|
||||||
|
@ -621,14 +615,14 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
||||||
if (stat & STATUS_END_CMD_RESP)
|
if (stat & STATUS_END_CMD_RESP)
|
||||||
mxcmci_cmd_done(host, stat);
|
mxcmci_cmd_done(host, stat);
|
||||||
|
|
||||||
#ifdef HAS_DMA
|
|
||||||
if (mxcmci_use_dma(host) &&
|
if (mxcmci_use_dma(host) &&
|
||||||
(stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE)))
|
(stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE)))
|
||||||
mxcmci_data_done(host, stat);
|
mxcmci_data_done(host, stat);
|
||||||
#endif
|
|
||||||
if (host->default_irq_mask &&
|
if (host->default_irq_mask &&
|
||||||
(stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
|
(stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
|
||||||
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,9 +636,10 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||||
|
|
||||||
host->req = req;
|
host->req = req;
|
||||||
host->cmdat &= ~CMD_DAT_CONT_INIT;
|
host->cmdat &= ~CMD_DAT_CONT_INIT;
|
||||||
#ifdef HAS_DMA
|
|
||||||
host->do_dma = 1;
|
if (host->dma)
|
||||||
#endif
|
host->do_dma = 1;
|
||||||
|
|
||||||
if (req->data) {
|
if (req->data) {
|
||||||
error = mxcmci_setup_data(host, req->data);
|
error = mxcmci_setup_data(host, req->data);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -660,6 +655,7 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
|
||||||
}
|
}
|
||||||
|
|
||||||
error = mxcmci_start_cmd(host, req->cmd, cmdat);
|
error = mxcmci_start_cmd(host, req->cmd, cmdat);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (error)
|
if (error)
|
||||||
mxcmci_finish_request(host, req);
|
mxcmci_finish_request(host, req);
|
||||||
|
@ -698,22 +694,46 @@ static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
|
||||||
prescaler, divider, clk_in, clk_ios);
|
prescaler, divider, clk_in, clk_ios);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mxcmci_setup_dma(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct mxcmci_host *host = mmc_priv(mmc);
|
||||||
|
struct dma_slave_config *config = &host->dma_slave_config;
|
||||||
|
|
||||||
|
config->dst_addr = host->res->start + MMC_REG_BUFFER_ACCESS;
|
||||||
|
config->src_addr = host->res->start + MMC_REG_BUFFER_ACCESS;
|
||||||
|
config->dst_addr_width = 4;
|
||||||
|
config->src_addr_width = 4;
|
||||||
|
config->dst_maxburst = host->burstlen;
|
||||||
|
config->src_maxburst = host->burstlen;
|
||||||
|
|
||||||
|
return dmaengine_slave_config(host->dma, config);
|
||||||
|
}
|
||||||
|
|
||||||
static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct mxcmci_host *host = mmc_priv(mmc);
|
struct mxcmci_host *host = mmc_priv(mmc);
|
||||||
#ifdef HAS_DMA
|
int burstlen, ret;
|
||||||
unsigned int blen;
|
|
||||||
/*
|
/*
|
||||||
* use burstlen of 64 in 4 bit mode (--> reg value 0)
|
* use burstlen of 64 in 4 bit mode (--> reg value 0)
|
||||||
* use burstlen of 16 in 1 bit mode (--> reg value 16)
|
* use burstlen of 16 in 1 bit mode (--> reg value 16)
|
||||||
*/
|
*/
|
||||||
if (ios->bus_width == MMC_BUS_WIDTH_4)
|
if (ios->bus_width == MMC_BUS_WIDTH_4)
|
||||||
blen = 0;
|
burstlen = 64;
|
||||||
else
|
else
|
||||||
blen = 16;
|
burstlen = 16;
|
||||||
|
|
||||||
|
if (mxcmci_use_dma(host) && burstlen != host->burstlen) {
|
||||||
|
host->burstlen = burstlen;
|
||||||
|
ret = mxcmci_setup_dma(mmc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(mmc_dev(host->mmc),
|
||||||
|
"failed to config DMA channel. Falling back to PIO\n");
|
||||||
|
dma_release_channel(host->dma);
|
||||||
|
host->do_dma = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
imx_dma_config_burstlen(host->dma, blen);
|
|
||||||
#endif
|
|
||||||
if (ios->bus_width == MMC_BUS_WIDTH_4)
|
if (ios->bus_width == MMC_BUS_WIDTH_4)
|
||||||
host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
|
host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
|
||||||
else
|
else
|
||||||
|
@ -794,6 +814,18 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card)
|
||||||
host->caps |= MMC_CAP_4_BIT_DATA;
|
host->caps |= MMC_CAP_4_BIT_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool filter(struct dma_chan *chan, void *param)
|
||||||
|
{
|
||||||
|
struct mxcmci_host *host = param;
|
||||||
|
|
||||||
|
if (!imx_dma_is_general_purpose(chan))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
chan->private = &host->dma_data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_host_ops mxcmci_ops = {
|
static const struct mmc_host_ops mxcmci_ops = {
|
||||||
.request = mxcmci_request,
|
.request = mxcmci_request,
|
||||||
.set_ios = mxcmci_set_ios,
|
.set_ios = mxcmci_set_ios,
|
||||||
|
@ -808,6 +840,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||||
struct mxcmci_host *host = NULL;
|
struct mxcmci_host *host = NULL;
|
||||||
struct resource *iores, *r;
|
struct resource *iores, *r;
|
||||||
int ret = 0, irq;
|
int ret = 0, irq;
|
||||||
|
dma_cap_mask_t mask;
|
||||||
|
|
||||||
printk(KERN_INFO "i.MX SDHC driver\n");
|
printk(KERN_INFO "i.MX SDHC driver\n");
|
||||||
|
|
||||||
|
@ -883,29 +916,23 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
|
writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
|
||||||
|
|
||||||
#ifdef HAS_DMA
|
|
||||||
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
|
|
||||||
if (host->dma < 0) {
|
|
||||||
dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto out_clk_put;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||||
if (!r) {
|
if (r) {
|
||||||
ret = -EINVAL;
|
host->dmareq = r->start;
|
||||||
goto out_free_dma;
|
host->dma_data.peripheral_type = IMX_DMATYPE_SDHC;
|
||||||
|
host->dma_data.priority = DMA_PRIO_LOW;
|
||||||
|
host->dma_data.dma_request = host->dmareq;
|
||||||
|
dma_cap_zero(mask);
|
||||||
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
host->dma = dma_request_channel(mask, filter, host);
|
||||||
|
if (host->dma)
|
||||||
|
mmc->max_seg_size = dma_get_max_seg_size(
|
||||||
|
host->dma->device->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = imx_dma_config_channel(host->dma,
|
if (!host->dma)
|
||||||
IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
|
dev_info(mmc_dev(host->mmc), "dma not available. Using PIO\n");
|
||||||
IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
|
|
||||||
r->start, 0);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(mmc_dev(host->mmc), "failed to config DMA channel\n");
|
|
||||||
goto out_free_dma;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
INIT_WORK(&host->datawork, mxcmci_datawork);
|
INIT_WORK(&host->datawork, mxcmci_datawork);
|
||||||
|
|
||||||
ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host);
|
ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host);
|
||||||
|
@ -928,9 +955,8 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||||
out_free_irq:
|
out_free_irq:
|
||||||
free_irq(host->irq, host);
|
free_irq(host->irq, host);
|
||||||
out_free_dma:
|
out_free_dma:
|
||||||
#ifdef HAS_DMA
|
if (host->dma)
|
||||||
imx_dma_free(host->dma);
|
dma_release_channel(host->dma);
|
||||||
#endif
|
|
||||||
out_clk_put:
|
out_clk_put:
|
||||||
clk_disable(host->clk);
|
clk_disable(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
|
@ -960,9 +986,10 @@ static int mxcmci_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
free_irq(host->irq, host);
|
free_irq(host->irq, host);
|
||||||
iounmap(host->base);
|
iounmap(host->base);
|
||||||
#ifdef HAS_DMA
|
|
||||||
imx_dma_free(host->dma);
|
if (host->dma)
|
||||||
#endif
|
dma_release_channel(host->dma);
|
||||||
|
|
||||||
clk_disable(host->clk);
|
clk_disable(host->clk);
|
||||||
clk_put(host->clk);
|
clk_put(host->clk);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue