Re: [PATCH v6 1/2] mmc: jz4740: add dma infrastructure for data transfers
From: Ulf Hansson
Date: Tue Aug 12 2014 - 12:21:44 EST
On 21 July 2014 06:37, Apelete Seketeli <apelete@xxxxxxxxxxxx> wrote:
> Until now the MMC driver for JZ4740 SoC was relying on PIO mode only
> for data transfers.
> This patch allows the use of DMA for data trasnfers in addition to PIO
> mode by relying on DMA Engine.
>
> DMA tranfers performance might be further improved by taking advantage
> of the asynchronous request capability of the MMC framework.
>
> Signed-off-by: Apelete Seketeli <apelete@xxxxxxxxxxxx>
Thanks! Queued for 3.18.
Kind regards
Uffe
> ---
> drivers/mmc/host/jz4740_mmc.c | 174 +++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 166 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
> index 537d6c7..049b133 100644
> --- a/drivers/mmc/host/jz4740_mmc.c
> +++ b/drivers/mmc/host/jz4740_mmc.c
> @@ -30,7 +30,9 @@
> #include <asm/mach-jz4740/gpio.h>
> #include <asm/cacheflush.h>
> #include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
>
> +#include <asm/mach-jz4740/dma.h>
> #include <asm/mach-jz4740/jz4740_mmc.h>
>
> #define JZ_REG_MMC_STRPCL 0x00
> @@ -122,6 +124,7 @@ struct jz4740_mmc_host {
> int card_detect_irq;
>
> void __iomem *base;
> + struct resource *mem_res;
> struct mmc_request *req;
> struct mmc_command *cmd;
>
> @@ -136,8 +139,138 @@ struct jz4740_mmc_host {
> struct timer_list timeout_timer;
> struct sg_mapping_iter miter;
> enum jz4740_mmc_state state;
> +
> + /* DMA support */
> + struct dma_chan *dma_rx;
> + struct dma_chan *dma_tx;
> + bool use_dma;
> + int sg_len;
> +
> +/* The DMA trigger level is 8 words, that is to say, the DMA read
> + * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
> + * trigger is when data words in MSC_TXFIFO is < 8.
> + */
> +#define JZ4740_MMC_FIFO_HALF_SIZE 8
> };
>
> +/*----------------------------------------------------------------------------*/
> +/* DMA infrastructure */
> +
> +static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
> +{
> + if (!host->use_dma)
> + return;
> +
> + dma_release_channel(host->dma_tx);
> + dma_release_channel(host->dma_rx);
> +}
> +
> +static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
> +{
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_SLAVE, mask);
> +
> + host->dma_tx = dma_request_channel(mask, NULL, host);
> + if (!host->dma_tx) {
> + dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
> + return -ENODEV;
> + }
> +
> + host->dma_rx = dma_request_channel(mask, NULL, host);
> + if (!host->dma_rx) {
> + dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
> + goto free_master_write;
> + }
> +
> + return 0;
> +
> +free_master_write:
> + dma_release_channel(host->dma_tx);
> + return -ENODEV;
> +}
> +
> +static inline int jz4740_mmc_get_dma_dir(struct mmc_data *data)
> +{
> + return (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +}
> +
> +static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
> + struct mmc_data *data)
> +{
> + struct dma_chan *chan;
> + enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
> +
> + if (dir == DMA_TO_DEVICE)
> + chan = host->dma_tx;
> + else
> + chan = host->dma_rx;
> +
> + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
> +}
> +
> +static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
> + struct mmc_data *data)
> +{
> + struct dma_chan *chan;
> + struct dma_async_tx_descriptor *desc;
> + struct dma_slave_config conf = {
> + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
> + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
> + .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
> + .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
> + };
> + enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
> +
> + if (dir == DMA_TO_DEVICE) {
> + conf.direction = DMA_MEM_TO_DEV;
> + conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
> + conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
> + chan = host->dma_tx;
> + } else {
> + conf.direction = DMA_DEV_TO_MEM;
> + conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
> + conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
> + chan = host->dma_rx;
> + }
> +
> + host->sg_len = dma_map_sg(chan->device->dev,
> + data->sg,
> + data->sg_len,
> + dir);
> +
> + if (host->sg_len == 0) {
> + dev_err(mmc_dev(host->mmc),
> + "Failed to map scatterlist for DMA operation\n");
> + return -EINVAL;
> + }
> +
> + dmaengine_slave_config(chan, &conf);
> + desc = dmaengine_prep_slave_sg(chan,
> + data->sg,
> + host->sg_len,
> + conf.direction,
> + DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> + if (!desc) {
> + dev_err(mmc_dev(host->mmc),
> + "Failed to allocate DMA %s descriptor",
> + conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX");
> + goto dma_unmap;
> + }
> +
> + dmaengine_submit(desc);
> + dma_async_issue_pending(chan);
> +
> + return 0;
> +
> +dma_unmap:
> + jz4740_mmc_dma_unmap(host, data);
> + return -ENOMEM;
> +}
> +
> +/*----------------------------------------------------------------------------*/
> +
> static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
> unsigned int irq, bool enabled)
> {
> @@ -442,6 +575,8 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
> cmdat |= JZ_MMC_CMDAT_WRITE;
> if (cmd->data->flags & MMC_DATA_STREAM)
> cmdat |= JZ_MMC_CMDAT_STREAM;
> + if (host->use_dma)
> + cmdat |= JZ_MMC_CMDAT_DMA_EN;
>
> writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
> writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
> @@ -474,6 +609,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
> struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid;
> struct mmc_command *cmd = host->req->cmd;
> struct mmc_request *req = host->req;
> + struct mmc_data *data = cmd->data;
> bool timeout = false;
>
> if (cmd->error)
> @@ -484,23 +620,32 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
> if (cmd->flags & MMC_RSP_PRESENT)
> jz4740_mmc_read_response(host, cmd);
>
> - if (!cmd->data)
> + if (!data)
> break;
>
> jz_mmc_prepare_data_transfer(host);
>
> case JZ4740_MMC_STATE_TRANSFER_DATA:
> - if (cmd->data->flags & MMC_DATA_READ)
> - timeout = jz4740_mmc_read_data(host, cmd->data);
> + if (host->use_dma) {
> + /* Use DMA if enabled, data transfer direction was
> + * defined before in jz_mmc_prepare_data_transfer().
> + */
> + timeout = jz4740_mmc_start_dma_transfer(host, data);
> + data->bytes_xfered = data->blocks * data->blksz;
> + } else if (data->flags & MMC_DATA_READ)
> + /* If DMA is not enabled, rely on data flags
> + * to establish data transfer direction.
> + */
> + timeout = jz4740_mmc_read_data(host, data);
> else
> - timeout = jz4740_mmc_write_data(host, cmd->data);
> + timeout = jz4740_mmc_write_data(host, data);
>
> if (unlikely(timeout)) {
> host->state = JZ4740_MMC_STATE_TRANSFER_DATA;
> break;
> }
>
> - jz4740_mmc_transfer_check_state(host, cmd->data);
> + jz4740_mmc_transfer_check_state(host, data);
>
> timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
> if (unlikely(timeout)) {
> @@ -757,7 +902,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
> struct mmc_host *mmc;
> struct jz4740_mmc_host *host;
> struct jz4740_mmc_platform_data *pdata;
> - struct resource *res;
>
> pdata = pdev->dev.platform_data;
>
> @@ -784,10 +928,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
> goto err_free_host;
> }
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - host->base = devm_ioremap_resource(&pdev->dev, res);
> + host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + host->base = devm_ioremap_resource(&pdev->dev, host->mem_res);
> if (IS_ERR(host->base)) {
> ret = PTR_ERR(host->base);
> + dev_err(&pdev->dev, "Failed to ioremap base memory\n");
> goto err_free_host;
> }
>
> @@ -834,6 +979,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
> /* It is not important when it times out, it just needs to timeout. */
> set_timer_slack(&host->timeout_timer, HZ);
>
> + host->use_dma = true;
> + if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
> + host->use_dma = false;
> +
> platform_set_drvdata(pdev, host);
> ret = mmc_add_host(mmc);
>
> @@ -843,6 +992,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
> }
> dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
>
> + dev_info(&pdev->dev, "Using %s, %d-bit mode\n",
> + host->use_dma ? "DMA" : "PIO",
> + (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
> +
> return 0;
>
> err_free_irq:
> @@ -850,6 +1003,8 @@ err_free_irq:
> err_free_gpios:
> jz4740_mmc_free_gpios(pdev);
> err_gpio_bulk_free:
> + if (host->use_dma)
> + jz4740_mmc_release_dma_channels(host);
> jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
> err_free_host:
> mmc_free_host(mmc);
> @@ -872,6 +1027,9 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
> jz4740_mmc_free_gpios(pdev);
> jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
>
> + if (host->use_dma)
> + jz4740_mmc_release_dma_channels(host);
> +
> mmc_free_host(host->mmc);
>
> return 0;
> --
> 1.7.10.4
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/