From 411fdaf846afb0be1b54383c184f58a42fa416ff Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Feb 2015 01:46:49 +0000 Subject: dmaengine: shdma: use normal interface for passing slave id in dma_slave_config, which is incompatible with the way that the dmaengine API normally works. I've had a closer look at the existing code now and found that all slave drivers that pass a slave_id in dma_slave_config for SH do that right after passing the same ID into shdma_chan_filter, so we can just rely on that. However, the various shdma drivers currently do not remember the slave ID that was passed into the filter function when used in non-DT mode and only check the value to find a matching channel, unlike all other drivers. There might still be drivers that are not part of the kernel that rely on setting the slave_id to some other value, so to be on the safe side, this adds another 'real_slave_id' field to shdma_chan that remembers the ID and uses it when a driver passes a zero slave_id in dma_slave_config, like most drivers do. Eventually, the real_slave_id and slave_id fields should just get merged into one field, but that requires other changes. Signed-off-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Signed-off-by: Vinod Koul diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 8ee383d..10fcaba 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -171,8 +171,7 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan) return NULL; } -static int shdma_setup_slave(struct shdma_chan *schan, int slave_id, - dma_addr_t slave_addr) +static int shdma_setup_slave(struct shdma_chan *schan, dma_addr_t slave_addr) { struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; @@ -183,25 +182,23 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id, ret = ops->set_slave(schan, match, slave_addr, true); if (ret < 0) return ret; - - slave_id = schan->slave_id; } else { - match = slave_id; + match = schan->real_slave_id; } - if (slave_id < 0 || slave_id >= slave_num) + if (schan->real_slave_id < 0 || schan->real_slave_id >= slave_num) return -EINVAL; - if (test_and_set_bit(slave_id, shdma_slave_used)) + if (test_and_set_bit(schan->real_slave_id, shdma_slave_used)) return -EBUSY; ret = ops->set_slave(schan, match, slave_addr, false); if (ret < 0) { - clear_bit(slave_id, shdma_slave_used); + clear_bit(schan->real_slave_id, shdma_slave_used); return ret; } - schan->slave_id = slave_id; + schan->slave_id = schan->real_slave_id; return 0; } @@ -221,10 +218,12 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) */ if (slave) { /* Legacy mode: .private is set in filter */ - ret = shdma_setup_slave(schan, slave->slave_id, 0); + schan->real_slave_id = slave->slave_id; + ret = shdma_setup_slave(schan, 0); if (ret < 0) goto esetslave; } else { + /* Normal mode: real_slave_id was set by filter */ schan->slave_id = -EINVAL; } @@ -258,11 +257,14 @@ esetslave: /* * This is the standard shdma filter function to be used as a replacement to the - * "old" method, using the .private pointer. If for some reason you allocate a - * channel without slave data, use something like ERR_PTR(-EINVAL) as a filter + * "old" method, using the .private pointer. + * You always have to pass a valid slave id as the argument, old drivers that + * pass ERR_PTR(-EINVAL) as a filter parameter and set it up in dma_slave_config + * need to be updated so we can remove the slave_id field from dma_slave_config. * parameter. If this filter is used, the slave driver, after calling * dma_request_channel(), will also have to call dmaengine_slave_config() with - * .slave_id, .direction, and either .src_addr or .dst_addr set. + * .direction, and either .src_addr or .dst_addr set. + * * NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE * capability! If this becomes a requirement, hardware glue drivers, using this * services would have to provide their own filters, which first would check @@ -276,7 +278,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg) { struct shdma_chan *schan; struct shdma_dev *sdev; - int match = (long)arg; + int slave_id = (long)arg; int ret; /* Only support channels handled by this driver. */ @@ -284,19 +286,39 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg) shdma_alloc_chan_resources) return false; - if (match < 0) + schan = to_shdma_chan(chan); + sdev = to_shdma_dev(chan->device); + + /* + * For DT, the schan->slave_id field is generated by the + * set_slave function from the slave ID that is passed in + * from xlate. For the non-DT case, the slave ID is + * directly passed into the filter function by the driver + */ + if (schan->dev->of_node) { + ret = sdev->ops->set_slave(schan, slave_id, 0, true); + if (ret < 0) + return false; + + schan->real_slave_id = schan->slave_id; + return true; + } + + if (slave_id < 0) { /* No slave requested - arbitrary channel */ + dev_warn(sdev->dma_dev.dev, "invalid slave ID passed to dma_request_slave\n"); return true; + } - schan = to_shdma_chan(chan); - if (!schan->dev->of_node && match >= slave_num) + if (slave_id >= slave_num) return false; - sdev = to_shdma_dev(schan->dma_chan.device); - ret = sdev->ops->set_slave(schan, match, 0, true); + ret = sdev->ops->set_slave(schan, slave_id, 0, true); if (ret < 0) return false; + schan->real_slave_id = slave_id; + return true; } EXPORT_SYMBOL(shdma_chan_filter); @@ -452,6 +474,8 @@ static void shdma_free_chan_resources(struct dma_chan *chan) chan->private = NULL; } + schan->real_slave_id = 0; + spin_lock_irq(&schan->chan_lock); list_splice_init(&schan->ld_free, &list); @@ -764,11 +788,20 @@ static int shdma_config(struct dma_chan *chan, */ if (!config) return -EINVAL; + + /* + * overriding the slave_id through dma_slave_config is deprecated, + * but possibly some out-of-tree drivers still do it. + */ + if (WARN_ON_ONCE(config->slave_id && + config->slave_id != schan->real_slave_id)) + schan->real_slave_id = config->slave_id; + /* * We could lock this, but you shouldn't be configuring the * channel, while using it... */ - return shdma_setup_slave(schan, config->slave_id, + return shdma_setup_slave(schan, config->direction == DMA_DEV_TO_MEM ? config->src_addr : config->dst_addr); } diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index abdf1f2..dd0ba50 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -69,6 +69,7 @@ struct shdma_chan { int id; /* Raw id of this channel */ int irq; /* Channel IRQ */ int slave_id; /* Client ID for slave DMA */ + int real_slave_id; /* argument passed to filter function */ int hw_req; /* DMA request line for slave DMA - same * as MID/RID, used with DT */ enum shdma_pm_state pm_state; -- cgit v0.10.2 From 5f48dd0690cbcea3f35b9ef2f05d5468dedc80b0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 17 Feb 2015 01:47:01 +0000 Subject: mmc: sh_mmcif: remove slave_id settings for DMAEngine Current sh_mmcif sets dma_slave_config :: slave_id field for DMAEngine, but it is no longer needed. Let's remove it. Signed-off-by: Kuninori Morimoto Acked-by: Arnd Bergmann Acked-by: Ulf Hansson Signed-off-by: Vinod Koul diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 7d9d6a3..26b7889 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -388,7 +388,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, { struct dma_slave_config cfg = { 0, }; struct dma_chan *chan; - unsigned int slave_id; + void *slave_data = NULL; struct resource *res; dma_cap_mask_t mask; int ret; @@ -397,13 +397,12 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, dma_cap_set(DMA_SLAVE, mask); if (pdata) - slave_id = direction == DMA_MEM_TO_DEV - ? pdata->slave_id_tx : pdata->slave_id_rx; - else - slave_id = 0; + slave_data = direction == DMA_MEM_TO_DEV ? + (void *)pdata->slave_id_tx : + (void *)pdata->slave_id_rx; chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - (void *)(unsigned long)slave_id, &host->pd->dev, + slave_data, &host->pd->dev, direction == DMA_MEM_TO_DEV ? "tx" : "rx"); dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__, @@ -414,8 +413,6 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, res = platform_get_resource(host->pd, IORESOURCE_MEM, 0); - /* In the OF case the driver will get the slave ID from the DT */ - cfg.slave_id = slave_id; cfg.direction = direction; if (direction == DMA_DEV_TO_MEM) { -- cgit v0.10.2 From b76924a0193fe1f221e3f52a410247e4a1da5c5a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 17 Feb 2015 01:47:11 +0000 Subject: mmc: tmio_mmc: remove slave_id settings for DMAEngine Current tmio_mmc sets dma_slave_config :: slave_id field for DMAEngine, but it is no longer needed. Let's remove it. Signed-off-by: Kuninori Morimoto Acked-by: Ulf Hansson Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 6906a90..11991f5 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -261,8 +261,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ dma_priv->chan_priv_tx = (void *)p->dma_slave_tx; dma_priv->chan_priv_rx = (void *)p->dma_slave_rx; - dma_priv->slave_id_tx = p->dma_slave_tx; - dma_priv->slave_id_rx = p->dma_slave_rx; } } dma_priv->filter = shdma_chan_filter; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index fc3805e..1aea5c6 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -45,8 +45,6 @@ struct tmio_mmc_host; struct tmio_mmc_dma { void *chan_priv_tx; void *chan_priv_rx; - int slave_id_tx; - int slave_id_rx; enum dma_slave_buswidth dma_buswidth; bool (*filter)(struct dma_chan *chan, void *arg); void (*enable)(struct tmio_mmc_host *host, bool enable); diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 331bb61..8dbd785 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -286,8 +286,6 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_tx) return; - if (host->dma->chan_priv_tx) - cfg.slave_id = host->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); cfg.dst_addr_width = host->dma->dma_buswidth; @@ -307,8 +305,6 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_rx) goto ereqrx; - if (host->dma->chan_priv_rx) - cfg.slave_id = host->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; cfg.src_addr_width = host->dma->dma_buswidth; -- cgit v0.10.2 From 4b93d9d4e5b6c90d6154bdb22a3544abe5e4e41a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Feb 2015 01:47:27 +0000 Subject: mtd: sh_flctl: remove slave_id settings for DMAEngine Current sh_flctl sets dma_slave_config :: slave_id field for DMAEngine, but it is no longer needed. Let's remove it. Signed-off-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Signed-off-by: Vinod Koul diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index a21c378..c3ce81c 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -159,7 +159,6 @@ static void flctl_setup_dma(struct sh_flctl *flctl) return; memset(&cfg, 0, sizeof(cfg)); - cfg.slave_id = pdata->slave_id_fifo0_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl); cfg.src_addr = 0; @@ -175,7 +174,6 @@ static void flctl_setup_dma(struct sh_flctl *flctl) if (!flctl->chan_fifo0_rx) goto err; - cfg.slave_id = pdata->slave_id_fifo0_rx; cfg.direction = DMA_DEV_TO_MEM; cfg.dst_addr = 0; cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl); -- cgit v0.10.2 From 118dad437f8dc8af6f531189d37c7db5ac7d51d4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Feb 2015 01:47:41 +0000 Subject: spi: rspi: remove slave_id settings for DMAEngine Current rspi sets dma_slave_config :: slave_id field for DMAEngine, but it is no longer needed. Let's remove it. Signed-off-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Acked-by: Mark Brown Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 46ce470..3f41106 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -913,7 +913,6 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev, } memset(&cfg, 0, sizeof(cfg)); - cfg.slave_id = id; cfg.direction = dir; if (dir == DMA_MEM_TO_DEV) { cfg.dst_addr = port_addr; -- cgit v0.10.2 From e95ddb5bda7333f0269347dc48ff214f9c216d5b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Feb 2015 01:47:53 +0000 Subject: spi: sh-msiof: remove slave_id settings for DMAEngine Current sh-msiof sets dma_slave_config :: slave_id field for DMAEngine, but it is no longer needed. Let's remove it. Signed-off-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Acked-by: Mark Brown Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index e57eec0..bcc7c63 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1030,7 +1030,6 @@ static struct dma_chan *sh_msiof_request_dma_chan(struct device *dev, } memset(&cfg, 0, sizeof(cfg)); - cfg.slave_id = id; cfg.direction = dir; if (dir == DMA_MEM_TO_DEV) { cfg.dst_addr = port_addr; -- cgit v0.10.2 From 5b7cdc8068e3f02ff4c6ef75bd7398af244869d4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 17 Feb 2015 01:48:08 +0000 Subject: ASoC: fsi: remove slave_id settings for DMAEngine Current fsi sets dma_slave_config :: slave_id field for DMAEngine, but it is no longer needed. Let's remove it. Signed-off-by: Kuninori Morimoto Acked-by: Mark Brown Signed-off-by: Vinod Koul diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index b87b22e..dc28b03 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1371,10 +1371,9 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev shdma_chan_filter, (void *)io->dma_id, dev, is_play ? "tx" : "rx"); if (io->chan) { - struct dma_slave_config cfg; + struct dma_slave_config cfg = {}; int ret; - cfg.slave_id = io->dma_id; cfg.dst_addr = 0; /* use default addr */ cfg.src_addr = 0; /* use default addr */ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; -- cgit v0.10.2 From 7c6cc8f2012f4146b05b8ec7238f98884100db8c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 17 Feb 2015 01:48:19 +0000 Subject: ASoC: fsi: Configure DMA slave settings Current FSI driver is assuming that dst_addr/src_addr of DMAEngine will be set by platform data. But it should be set via dmaengine_slave_config(). Special thanks to Arnd Reported-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Acked-by: Mark Brown Signed-off-by: Vinod Koul diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index dc28b03..bb20550 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -250,6 +250,7 @@ struct fsi_clk { struct fsi_priv { void __iomem *base; + phys_addr_t phys; struct fsi_master *master; struct fsi_stream playback; @@ -1374,9 +1375,15 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev struct dma_slave_config cfg = {}; int ret; - cfg.dst_addr = 0; /* use default addr */ - cfg.src_addr = 0; /* use default addr */ - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + if (is_play) { + cfg.dst_addr = fsi->phys + REG_DODT; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.direction = DMA_MEM_TO_DEV; + } else { + cfg.src_addr = fsi->phys + REG_DIDT; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.direction = DMA_DEV_TO_MEM; + } ret = dmaengine_slave_config(io->chan, &cfg); if (ret < 0) { @@ -1940,6 +1947,7 @@ static int fsi_probe(struct platform_device *pdev) /* FSI A setting */ fsi = &master->fsia; fsi->base = master->base; + fsi->phys = res->start; fsi->master = master; fsi_port_info_init(fsi, &info.port_a); fsi_handler_init(fsi, &info.port_a); @@ -1952,6 +1960,7 @@ static int fsi_probe(struct platform_device *pdev) /* FSI B setting */ fsi = &master->fsib; fsi->base = master->base + 0x40; + fsi->phys = res->start + 0x40; fsi->master = master; fsi_port_info_init(fsi, &info.port_b); fsi_handler_init(fsi, &info.port_b); -- cgit v0.10.2 From 81ceefa49e26069d05f2c3350930d2896de0baeb Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Fri, 6 Feb 2015 16:51:44 +0530 Subject: dmaengine: qcom_bam_dma: Fix error path in probe function Calls tasklet_kill() in error path of the probe function were missing. Add the same in error path. Signed-off-by: Pramod Gurav Signed-off-by: Vinod Koul diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c index d7a33b3..cbacee5 100644 --- a/drivers/dma/qcom_bam_dma.c +++ b/drivers/dma/qcom_bam_dma.c @@ -1113,7 +1113,7 @@ static int bam_dma_probe(struct platform_device *pdev) if (!bdev->channels) { ret = -ENOMEM; - goto err_disable_clk; + goto err_tasklet_kill; } /* allocate and initialize channels */ @@ -1125,7 +1125,7 @@ static int bam_dma_probe(struct platform_device *pdev) ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq, IRQF_TRIGGER_HIGH, "bam_dma", bdev); if (ret) - goto err_disable_clk; + goto err_bam_channel_exit; /* set max dma segment size */ bdev->common.dev = bdev->dev; @@ -1133,7 +1133,7 @@ static int bam_dma_probe(struct platform_device *pdev) ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE); if (ret) { dev_err(bdev->dev, "cannot set maximum segment size\n"); - goto err_disable_clk; + goto err_bam_channel_exit; } platform_set_drvdata(pdev, bdev); @@ -1157,7 +1157,7 @@ static int bam_dma_probe(struct platform_device *pdev) ret = dma_async_device_register(&bdev->common); if (ret) { dev_err(bdev->dev, "failed to register dma async device\n"); - goto err_disable_clk; + goto err_bam_channel_exit; } ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate, @@ -1169,8 +1169,14 @@ static int bam_dma_probe(struct platform_device *pdev) err_unregister_dma: dma_async_device_unregister(&bdev->common); +err_bam_channel_exit: + for (i = 0; i < bdev->num_channels; i++) + tasklet_kill(&bdev->channels[i].vc.task); +err_tasklet_kill: + tasklet_kill(&bdev->task); err_disable_clk: clk_disable_unprepare(bdev->bamclk); + return ret; } -- cgit v0.10.2 From ee655c2968a0d224484c91f07456d3a213463af6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 28 Feb 2015 22:25:06 +0000 Subject: dmaengine: s3c24xx: Fix spelling mistake in dev_err mistake Fix spelling mistake, "aquire" -> "acquire" and missing newline (as spotted by Joe Perches. Signed-off-by: Colin Ian King Signed-off-by: Vinod Koul diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 2f91da3..110a0cd 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -1238,7 +1238,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) if (!s3cdma->phy_chans) return -ENOMEM; - /* aquire irqs and clocks for all physical channels */ + /* acquire irqs and clocks for all physical channels */ for (i = 0; i < pdata->num_phy_channels; i++) { struct s3c24xx_dma_phy *phy = &s3cdma->phy_chans[i]; char clk_name[6]; @@ -1266,7 +1266,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) sprintf(clk_name, "dma.%d", i); phy->clk = devm_clk_get(&pdev->dev, clk_name); if (IS_ERR(phy->clk) && sdata->has_clocks) { - dev_err(&pdev->dev, "unable to aquire clock for channel %d, error %lu", + dev_err(&pdev->dev, "unable to acquire clock for channel %d, error %lu\n", i, PTR_ERR(phy->clk)); continue; } -- cgit v0.10.2 From e921eea8e7d4457f424bc3f821cb836e35b91f88 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 3 Mar 2015 12:05:00 +0100 Subject: dmaengine: Remove memset leftovers Commit 48a9db462d99 ("drivers/dma: remove unused support for MEMSET operations") removed support for the memset operation in dmaengine, but left the fill_aligned field that was supposed to set the buffer alignment for the memset operations. Remove that field too. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b6997a0..db0104b 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -574,7 +574,6 @@ struct dma_tx_state { * @copy_align: alignment shift for memcpy operations * @xor_align: alignment shift for xor operations * @pq_align: alignment shift for pq operations - * @fill_align: alignment shift for memset operations * @dev_id: unique device ID * @dev: struct device reference for dma mapping api * @src_addr_widths: bit mask of src addr widths the device supports @@ -625,7 +624,6 @@ struct dma_device { u8 copy_align; u8 xor_align; u8 pq_align; - u8 fill_align; #define DMA_HAS_PQ_CONTINUE (1 << 15) int dev_id; @@ -826,12 +824,6 @@ static inline bool is_dma_pq_aligned(struct dma_device *dev, size_t off1, return dmaengine_check_align(dev->pq_align, off1, off2, len); } -static inline bool is_dma_fill_aligned(struct dma_device *dev, size_t off1, - size_t off2, size_t len) -{ - return dmaengine_check_align(dev->fill_align, off1, off2, len); -} - static inline void dma_set_maxpq(struct dma_device *dma, int maxpq, int has_pq_continue) { -- cgit v0.10.2 From 68c062eaa87b7b85b65f20f25c54524437715a95 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 5 Mar 2015 11:42:43 +0100 Subject: dmaengine: Remove net_dma leftovers Commit 7bce d397 510a ("net_dma: simple removal") removed the net_dma support entirely but left some functions and prototypes in the dmaengine header. Remove them. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index db0104b..f5cc5d4 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1108,27 +1108,4 @@ static inline struct dma_chan return __dma_request_channel(mask, fn, fn_param); } - -/* --- Helper iov-locking functions --- */ - -struct dma_page_list { - char __user *base_address; - int nr_pages; - struct page **pages; -}; - -struct dma_pinned_list { - int nr_iovecs; - struct dma_page_list page_list[0]; -}; - -struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len); -void dma_unpin_iovec_pages(struct dma_pinned_list* pinned_list); - -dma_cookie_t dma_memcpy_to_iovec(struct dma_chan *chan, struct iovec *iov, - struct dma_pinned_list *pinned_list, unsigned char *kdata, size_t len); -dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov, - struct dma_pinned_list *pinned_list, struct page *page, - unsigned int offset, size_t len); - #endif /* DMAENGINE_H */ -- cgit v0.10.2 From bfde98bd762346639f0a5a557e02c4828dd6273b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 5 Mar 2015 11:48:50 +0100 Subject: dmaengine: Remove net_dma_find_channel Since commit 7bced397510a ("net_dma: simple removal") removed the net_dma support entirely, net_dma_find_channel has no users left. Remove the function entirely. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index f15712f..344b0ac 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -355,20 +355,6 @@ struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type) } EXPORT_SYMBOL(dma_find_channel); -/* - * net_dma_find_channel - find a channel for net_dma - * net_dma has alignment requirements - */ -struct dma_chan *net_dma_find_channel(void) -{ - struct dma_chan *chan = dma_find_channel(DMA_MEMCPY); - if (chan && !is_dma_copy_aligned(chan->device, 1, 1, 1)) - return NULL; - - return chan; -} -EXPORT_SYMBOL(net_dma_find_channel); - /** * dma_issue_pending_all - flush all pending operations across all channels */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index f5cc5d4..2bff9ab 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1090,7 +1090,6 @@ void dma_async_device_unregister(struct dma_device *device); void dma_run_dependencies(struct dma_async_tx_descriptor *tx); struct dma_chan *dma_get_slave_channel(struct dma_chan *chan); struct dma_chan *dma_get_any_slave_channel(struct dma_device *device); -struct dma_chan *net_dma_find_channel(void); #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y) #define dma_request_slave_channel_compat(mask, x, y, dev, name) \ __dma_request_slave_channel_compat(&(mask), x, y, dev, name) -- cgit v0.10.2 From f33c9d655893d8632460696bbbdee737cb315711 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 24 Feb 2015 02:06:43 +0000 Subject: mmc: tmio: mmc: tmio: tmio_mmc_data has .chan_priv_?x dma_request_slave_channel_compat() in tmio_mmc_dma needs .chan_priv_tx/.chan_priv_rx. But these are copied from sh_mobile_sdhi only, and sh_mobile_sdhi_info is now almost same as tmio_mmc_data except .chan_priv_?x. sh_mobile_sdhi_info can be replaced to tmio_mmc_data, but it is used from ${LINUX}/arch/arm/mach-shmobile, ${LINUX}/arch/sh. So, this patch adds .chan_priv_?x into tmio_mmc_data as 1st step, and sh_mobile_sdhi driver has dummy operation for now. It will be replaced/removed together with platform data replace. Signed-off-by: Kuninori Morimoto Acked-by: Arnd Bergmann Acked-by: Ulf Hansson Acked-by: Lee Jones Signed-off-by: Vinod Koul diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 11991f5..3137e29 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -201,6 +201,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) of_match_device(sh_mobile_sdhi_of_match, &pdev->dev); struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; + struct tmio_mmc_data *mmd = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct tmio_mmc_host *host; struct resource *res; @@ -245,28 +246,37 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) else host->bus_shift = 0; - mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; - if (p) { - mmc_data->flags = p->tmio_flags; - mmc_data->ocr_mask = p->tmio_ocr_mask; - mmc_data->capabilities |= p->tmio_caps; - mmc_data->capabilities2 |= p->tmio_caps2; - mmc_data->cd_gpio = p->cd_gpio; - - if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { - /* - * Yes, we have to provide slave IDs twice to TMIO: - * once as a filter parameter and once for channel - * configuration as an explicit slave ID - */ - dma_priv->chan_priv_tx = (void *)p->dma_slave_tx; - dma_priv->chan_priv_rx = (void *)p->dma_slave_rx; - } + if (mmd) { + /* + * FIXME + * + * sh_mobile_sdhi_info will be replaced to tmio_mmc_data soon. + * But, sh_mobile_sdhi_info is used under + * ${LINUX}/arch/arm/mach-shmobile/ + * ${LINUX}/arch/sh/ + * To separate large patch into "tmio_mmc_data has .chan_priv_?x" + * and "replace sh_mobile_sdhi_info in tmio_mmc_data", + * here has dummy method. + * These should be removed. + */ + struct tmio_mmc_data m; + + mmd = &m; + m.flags = p->tmio_flags; + m.ocr_mask = p->tmio_ocr_mask; + m.capabilities = p->tmio_caps; + m.capabilities2 = p->tmio_caps2; + m.cd_gpio = p->cd_gpio; + m.chan_priv_tx = (void *)p->dma_slave_tx; + m.chan_priv_rx = (void *)p->dma_slave_rx; + + *mmc_data = *mmd; } dma_priv->filter = shdma_chan_filter; dma_priv->enable = sh_mobile_sdhi_enable_dma; mmc_data->alignment_shift = 1; /* 2-byte alignment */ + mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED; /* * All SDHI blocks support 2-byte and larger block sizes in 4-bit diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 1aea5c6..4a597f5a 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -43,8 +43,6 @@ struct tmio_mmc_data; struct tmio_mmc_host; struct tmio_mmc_dma { - void *chan_priv_tx; - void *chan_priv_rx; enum dma_slave_buswidth dma_buswidth; bool (*filter)(struct dma_chan *chan, void *arg); void (*enable)(struct tmio_mmc_host *host, bool enable); diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 8dbd785..e4b05db 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -261,7 +261,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat { /* We can only either use DMA for both Tx and Rx or not use it at all */ if (!host->dma || (!host->pdev->dev.of_node && - (!host->dma->chan_priv_tx || !host->dma->chan_priv_rx))) + (!pdata->chan_priv_tx || !pdata->chan_priv_rx))) return; if (!host->chan_tx && !host->chan_rx) { @@ -278,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat dma_cap_set(DMA_SLAVE, mask); host->chan_tx = dma_request_slave_channel_compat(mask, - host->dma->filter, host->dma->chan_priv_tx, + host->dma->filter, pdata->chan_priv_tx, &host->pdev->dev, "tx"); dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); @@ -297,7 +297,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat goto ecfgtx; host->chan_rx = dma_request_slave_channel_compat(mask, - host->dma->filter, host->dma->chan_priv_rx, + host->dma->filter, pdata->chan_priv_rx, &host->pdev->dev, "rx"); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 6058128..24b86d5 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -111,6 +111,8 @@ struct dma_chan; * data for the MMC controller */ struct tmio_mmc_data { + void *chan_priv_tx; + void *chan_priv_rx; unsigned int hclk; unsigned long capabilities; unsigned long capabilities2; -- cgit v0.10.2 From 84f11d5b1f2abc0e22895b7e12e037f0ec03caeb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 24 Feb 2015 02:07:07 +0000 Subject: mmc: sh_mobile_sdhi: remove sh_mobile_sdhi_info Current sh_mobile_sdhi's platform data is set via sh_mobile_sdhi_info and it is just copied to tmio_mmc_data. Now, tmio mmc platform data is specified via tmio_mmc_data. This patch replace sh_mobile_sdhi_info to tmio_mmc_data struct sh_mobile_sdhi_info { -> struct tmio_mmc_data { int dma_slave_tx; -> void *chan_priv_tx; int dma_slave_rx; -> void *chan_priv_rx; unsigned long tmio_flags; -> unsigned long flags; unsigned long tmio_caps; -> unsigned long capabilities; unsigned long tmio_caps2; -> unsigned long capabilities2; u32 tmio_ocr_mask; -> u32 ocr_mask; unsigned int cd_gpio; -> unsigned int cd_gpio; }; unsigned int hclk; void (*set_pwr)(...); void (*set_clk_div)(...); }; Signed-off-by: Kuninori Morimoto Acked-by: Arnd Bergmann Acked-by: Ulf Hansson Signed-off-by: Vinod Koul diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 6d949f1..7eac846 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -754,12 +754,12 @@ static struct platform_device vcc_sdhi1 = { }; /* SDHI0 */ -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, .cd_gpio = 167, }; @@ -796,12 +796,12 @@ static struct platform_device sdhi0_device = { }; /* SDHI1 */ -static struct sh_mobile_sdhi_info sdhi1_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi1_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD, /* Port72 cannot generate IRQs, will be used in polling mode. */ .cd_gpio = 72, }; diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c index f27b5a8..25558d1 100644 --- a/arch/arm/mach-shmobile/board-bockw.c +++ b/arch/arm/mach-shmobile/board-bockw.c @@ -201,12 +201,12 @@ static struct rcar_phy_platform_data usb_phy_platform_data __initdata = /* SDHI */ -static struct sh_mobile_sdhi_info sdhi0_info __initdata = { - .dma_slave_tx = HPBDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = HPBDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED, - .tmio_ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, +static struct tmio_mmc_data sdhi0_info __initdata = { + .chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED, + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34, + .flags = TMIO_MMC_HAS_IDLE_WAIT, }; static struct resource sdhi0_resources[] __initdata = { @@ -683,7 +683,7 @@ static void __init bockw_init(void) platform_device_register_resndata( NULL, "sh_mobile_sdhi", 0, sdhi0_resources, ARRAY_SIZE(sdhi0_resources), - &sdhi0_info, sizeof(struct sh_mobile_sdhi_info)); + &sdhi0_info, sizeof(struct tmio_mmc_data)); } /* for Audio */ diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index 7c9b63b..260d831 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -442,11 +442,11 @@ static struct platform_device vcc_sdhi2 = { }; /* SDHI */ -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_HAS_IDLE_WAIT, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD, }; @@ -484,13 +484,13 @@ static struct platform_device sdhi0_device = { }; /* Micro SD */ -static struct sh_mobile_sdhi_info sdhi2_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI2_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI2_RX, - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | +static struct tmio_mmc_data sdhi2_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI2_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI2_RX, + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD | TMIO_MMC_WRPROTECT_DISABLE, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD, .cd_gpio = 13, }; diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c index 598f704..51db288 100644 --- a/arch/arm/mach-shmobile/board-marzen.c +++ b/arch/arm/mach-shmobile/board-marzen.c @@ -122,11 +122,11 @@ static struct resource sdhi0_resources[] = { }, }; -static struct sh_mobile_sdhi_info sdhi0_platform_data = { - .dma_slave_tx = HPBDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = HPBDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, - .tmio_caps = MMC_CAP_SD_HIGHSPEED, +static struct tmio_mmc_data sdhi0_platform_data = { + .chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, + .capabilities = MMC_CAP_SD_HIGHSPEED, }; static struct platform_device sdhi0_device = { diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index 669df51..324599b 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -243,10 +244,10 @@ static struct platform_device sh_mmcif_device = { }; /* SDHI0 */ -static struct sh_mobile_sdhi_info sdhi_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI_RX, - .tmio_caps = MMC_CAP_SD_HIGHSPEED, +static struct tmio_mmc_data sdhi_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI_RX, + .capabilities = MMC_CAP_SD_HIGHSPEED, }; static struct resource sdhi_resources[] = { diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index d4b01d4..cbd2a9f 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -447,8 +448,8 @@ static struct resource sdhi0_cn3_resources[] = { }, }; -static struct sh_mobile_sdhi_info sdhi0_cn3_data = { - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sdhi0_cn3_data = { + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi0_cn3_device = { @@ -474,8 +475,8 @@ static struct resource sdhi1_cn7_resources[] = { }, }; -static struct sh_mobile_sdhi_info sdhi1_cn7_data = { - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sdhi1_cn7_data = { + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi1_cn7_device = { diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 0d30492..d531791 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -601,12 +601,12 @@ static struct platform_device sdhi0_power = { }, }; -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | MMC_CAP_NEEDS_POLL, - .tmio_flags = TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_USE_GPIO_CD, .cd_gpio = GPIO_PTY7, }; @@ -635,12 +635,12 @@ static struct platform_device sdhi0_device = { #if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE) /* SDHI1 */ -static struct sh_mobile_sdhi_info sdhi1_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | +static struct tmio_mmc_data sdhi1_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD | MMC_CAP_NEEDS_POLL, - .tmio_flags = TMIO_MMC_USE_GPIO_CD, + .flags = TMIO_MMC_USE_GPIO_CD, .cd_gpio = GPIO_PTW7, }; diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index 1df4398..7d997ce 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -373,11 +373,11 @@ static struct resource kfr2r09_sh_sdhi0_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi0_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi0_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device kfr2r09_sh_sdhi0_device = { diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 8b73194..29b7c0d 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -408,10 +409,10 @@ static struct resource sdhi_cn9_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi_cn9_device = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 1162bc6..4f6635a 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -468,10 +469,10 @@ static struct resource sdhi0_cn7_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi0_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi0_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi0_cn7_device = { @@ -497,10 +498,10 @@ static struct resource sdhi1_cn8_resources[] = { }, }; -static struct sh_mobile_sdhi_info sh7724_sdhi1_data = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_caps = MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sh7724_sdhi1_data = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .capabilities = MMC_CAP_SDIO_IRQ, }; static struct platform_device sdhi1_cn8_device = { diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 3137e29..354f4f3 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -202,7 +202,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; struct tmio_mmc_data *mmd = pdev->dev.platform_data; - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct tmio_mmc_host *host; struct resource *res; int irq, ret, i = 0; @@ -246,32 +245,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) else host->bus_shift = 0; - if (mmd) { - /* - * FIXME - * - * sh_mobile_sdhi_info will be replaced to tmio_mmc_data soon. - * But, sh_mobile_sdhi_info is used under - * ${LINUX}/arch/arm/mach-shmobile/ - * ${LINUX}/arch/sh/ - * To separate large patch into "tmio_mmc_data has .chan_priv_?x" - * and "replace sh_mobile_sdhi_info in tmio_mmc_data", - * here has dummy method. - * These should be removed. - */ - struct tmio_mmc_data m; - - mmd = &m; - m.flags = p->tmio_flags; - m.ocr_mask = p->tmio_ocr_mask; - m.capabilities = p->tmio_caps; - m.capabilities2 = p->tmio_caps2; - m.cd_gpio = p->cd_gpio; - m.chan_priv_tx = (void *)p->dma_slave_tx; - m.chan_priv_rx = (void *)p->dma_slave_rx; - + if (mmd) *mmc_data = *mmd; - } + dma_priv->filter = shdma_chan_filter; dma_priv->enable = sh_mobile_sdhi_enable_dma; diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index da77e5e20..95d6f03 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -7,14 +7,4 @@ #define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard" #define SH_MOBILE_SDHI_IRQ_SDIO "sdio" -struct sh_mobile_sdhi_info { - int dma_slave_tx; - int dma_slave_rx; - unsigned long tmio_flags; - unsigned long tmio_caps; - unsigned long tmio_caps2; - u32 tmio_ocr_mask; /* available MMC voltages */ - unsigned int cd_gpio; -}; - #endif /* LINUX_MMC_SH_MOBILE_SDHI_H */ -- cgit v0.10.2 From 26f7af37eeaab359c3013c704f75749a2b5ce1b1 Mon Sep 17 00:00:00 2001 From: Tapasweni Pathak Date: Fri, 20 Feb 2015 17:58:44 +0530 Subject: dmaengine: jz4740: Remove extra check Remove double check on chan->desc. Found by Coccinelle. Signed-off-by: Tapasweni Pathak Acked-by: Julia Lawall Signed-off-by: Vinod Koul diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index 4527a3e..f6a4c42 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -343,7 +343,7 @@ static void jz4740_dma_chan_irq(struct jz4740_dmaengine_chan *chan) { spin_lock(&chan->vchan.lock); if (chan->desc) { - if (chan->desc && chan->desc->cyclic) { + if (chan->desc->cyclic) { vchan_cyclic_callback(&chan->desc->vdesc); } else { if (chan->next_sg == chan->desc->num_sgs) { -- cgit v0.10.2 From 265567fbca7136f1cf45cb49a630cf7109a2308a Mon Sep 17 00:00:00 2001 From: Torsten Fleischer Date: Mon, 23 Feb 2015 17:54:11 +0100 Subject: dmaengine: at_hdmac: Add support for memory to memory sg transfers This patch adds support for memory to memory scatter-gather transfers. Changes from V1: * Fixed coding style of the multi-line comments. Changes from V2: * Added setup of 'desc->tx_width' that is needed to calculate the residue. Signed-off-by: Torsten Fleischer Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 1e1a4c5..4f5b262 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -65,6 +65,21 @@ static void atc_issue_pending(struct dma_chan *chan); /*----------------------------------------------------------------------*/ +static inline unsigned int atc_get_xfer_width(dma_addr_t src, dma_addr_t dst, + size_t len) +{ + unsigned int width; + + if (!((src | dst | len) & 3)) + width = 2; + else if (!((src | dst | len) & 1)) + width = 1; + else + width = 0; + + return width; +} + static struct at_desc *atc_first_active(struct at_dma_chan *atchan) { return list_first_entry(&atchan->active_list, @@ -628,16 +643,10 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, * We can be a lot more clever here, but this should take care * of the most common optimization. */ - if (!((src | dest | len) & 3)) { - ctrla = ATC_SRC_WIDTH_WORD | ATC_DST_WIDTH_WORD; - src_width = dst_width = 2; - } else if (!((src | dest | len) & 1)) { - ctrla = ATC_SRC_WIDTH_HALFWORD | ATC_DST_WIDTH_HALFWORD; - src_width = dst_width = 1; - } else { - ctrla = ATC_SRC_WIDTH_BYTE | ATC_DST_WIDTH_BYTE; - src_width = dst_width = 0; - } + src_width = dst_width = atc_get_xfer_width(src, dest, len); + + ctrla = ATC_SRC_WIDTH(src_width) | + ATC_DST_WIDTH(dst_width); for (offset = 0; offset < len; offset += xfer_count << src_width) { xfer_count = min_t(size_t, (len - offset) >> src_width, @@ -822,6 +831,144 @@ err: } /** + * atc_prep_dma_sg - prepare memory to memory scather-gather operation + * @chan: the channel to prepare operation on + * @dst_sg: destination scatterlist + * @dst_nents: number of destination scatterlist entries + * @src_sg: source scatterlist + * @src_nents: number of source scatterlist entries + * @flags: tx descriptor status flags + */ +static struct dma_async_tx_descriptor * +atc_prep_dma_sg(struct dma_chan *chan, + struct scatterlist *dst_sg, unsigned int dst_nents, + struct scatterlist *src_sg, unsigned int src_nents, + unsigned long flags) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_desc *desc = NULL; + struct at_desc *first = NULL; + struct at_desc *prev = NULL; + unsigned int src_width; + unsigned int dst_width; + size_t xfer_count; + u32 ctrla; + u32 ctrlb; + size_t dst_len = 0, src_len = 0; + dma_addr_t dst = 0, src = 0; + size_t len = 0, total_len = 0; + + if (unlikely(dst_nents == 0 || src_nents == 0)) + return NULL; + + if (unlikely(dst_sg == NULL || src_sg == NULL)) + return NULL; + + ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN + | ATC_SRC_ADDR_MODE_INCR + | ATC_DST_ADDR_MODE_INCR + | ATC_FC_MEM2MEM; + + /* + * loop until there is either no more source or no more destination + * scatterlist entry + */ + while (true) { + + /* prepare the next transfer */ + if (dst_len == 0) { + + /* no more destination scatterlist entries */ + if (!dst_sg || !dst_nents) + break; + + dst = sg_dma_address(dst_sg); + dst_len = sg_dma_len(dst_sg); + + dst_sg = sg_next(dst_sg); + dst_nents--; + } + + if (src_len == 0) { + + /* no more source scatterlist entries */ + if (!src_sg || !src_nents) + break; + + src = sg_dma_address(src_sg); + src_len = sg_dma_len(src_sg); + + src_sg = sg_next(src_sg); + src_nents--; + } + + len = min_t(size_t, src_len, dst_len); + if (len == 0) + continue; + + /* take care for the alignment */ + src_width = dst_width = atc_get_xfer_width(src, dst, len); + + ctrla = ATC_SRC_WIDTH(src_width) | + ATC_DST_WIDTH(dst_width); + + /* + * The number of transfers to set up refer to the source width + * that depends on the alignment. + */ + xfer_count = len >> src_width; + if (xfer_count > ATC_BTSIZE_MAX) { + xfer_count = ATC_BTSIZE_MAX; + len = ATC_BTSIZE_MAX << src_width; + } + + /* create the transfer */ + desc = atc_desc_get(atchan); + if (!desc) + goto err_desc_get; + + desc->lli.saddr = src; + desc->lli.daddr = dst; + desc->lli.ctrla = ctrla | xfer_count; + desc->lli.ctrlb = ctrlb; + + desc->txd.cookie = 0; + desc->len = len; + + /* + * Although we only need the transfer width for the first and + * the last descriptor, its easier to set it to all descriptors. + */ + desc->tx_width = src_width; + + atc_desc_chain(&first, &prev, desc); + + /* update the lengths and addresses for the next loop cycle */ + dst_len -= len; + src_len -= len; + dst += len; + src += len; + + total_len += len; + } + + /* First descriptor of the chain embedds additional information */ + first->txd.cookie = -EBUSY; + first->total_len = total_len; + + /* set end-of-link to the last link descriptor of list*/ + set_desc_eol(desc); + + first->txd.flags = flags; /* client is in control of this ack */ + + return &first->txd; + +err_desc_get: + atc_desc_put(atchan, first); + return NULL; +} + +/** * atc_dma_cyclic_check_values * Check for too big/unaligned periods and unaligned DMA buffer */ @@ -1421,8 +1568,10 @@ static int __init at_dma_probe(struct platform_device *pdev) /* setup platform data for each SoC */ dma_cap_set(DMA_MEMCPY, at91sam9rl_config.cap_mask); + dma_cap_set(DMA_SG, at91sam9rl_config.cap_mask); dma_cap_set(DMA_MEMCPY, at91sam9g45_config.cap_mask); dma_cap_set(DMA_SLAVE, at91sam9g45_config.cap_mask); + dma_cap_set(DMA_SG, at91sam9g45_config.cap_mask); /* get DMA parameters from controller type */ plat_dat = at_dma_get_driver_data(pdev); @@ -1542,11 +1691,15 @@ static int __init at_dma_probe(struct platform_device *pdev) atdma->dma_common.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; } + if (dma_has_cap(DMA_SG, atdma->dma_common.cap_mask)) + atdma->dma_common.device_prep_dma_sg = atc_prep_dma_sg; + dma_writel(atdma, EN, AT_DMA_ENABLE); - dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n", + dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s%s), %d channels\n", dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "", dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "", + dma_has_cap(DMA_SG, atdma->dma_common.cap_mask) ? "sg-cpy " : "", plat_dat->nr_channels); dma_async_device_register(&atdma->dma_common); -- cgit v0.10.2 From 550da64bc89b597395ea6e43a6b4026491035a9d Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 10 Mar 2015 11:37:23 +0200 Subject: dmaengine: dw: Make error prints unique. Part #1 The same error message is printed from different functions. Add a function name to error message in order to make it easier to grep error from sources. Signed-off-by: Jarkko Nikula Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 455b7a4..ac4fc09 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -230,7 +230,8 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) /* ASSERT: channel is idle */ if (dma_readl(dw, CH_EN) & dwc->mask) { dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start non-idle channel\n"); + "%s: BUG: Attempted to start non-idle channel\n", + __func__); dwc_dump_chan_regs(dwc); /* The tasklet will hopefully advance the queue... */ @@ -1261,7 +1262,8 @@ int dw_dma_cyclic_start(struct dma_chan *chan) /* Assert channel is idle */ if (dma_readl(dw, CH_EN) & dwc->mask) { dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start non-idle channel\n"); + "%s: BUG: Attempted to start non-idle channel\n", + __func__); dwc_dump_chan_regs(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return -EBUSY; -- cgit v0.10.2 From b26072276f20ce93717fdad5210e4ae9f1c944fa Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 10 Mar 2015 11:37:24 +0200 Subject: dmaengine: dw: Make error prints unique. Part #2 The same error message is printed from different switch cases. Since both of these jump into same error label we can move error print there and add a DMA direction in order to make it easier to grep error from sources. Signed-off-by: Jarkko Nikula Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index ac4fc09..a1c078d 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -815,11 +815,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, slave_sg_todev_fill_desc: desc = dwc_desc_get(dwc); - if (!desc) { - dev_err(chan2dev(chan), - "not enough descriptors available\n"); + if (!desc) goto err_desc_get; - } desc->lli.sar = mem; desc->lli.dar = reg; @@ -875,11 +872,8 @@ slave_sg_todev_fill_desc: slave_sg_fromdev_fill_desc: desc = dwc_desc_get(dwc); - if (!desc) { - dev_err(chan2dev(chan), - "not enough descriptors available\n"); + if (!desc) goto err_desc_get; - } desc->lli.sar = reg; desc->lli.dar = mem; @@ -923,6 +917,8 @@ slave_sg_fromdev_fill_desc: return &first->txd; err_desc_get: + dev_err(chan2dev(chan), + "not enough descriptors available. Direction %d\n", direction); dwc_desc_put(dwc, first); return NULL; } -- cgit v0.10.2 From d51da4d4a9e6592d2e5645943a23e25dfd91939e Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 23 Feb 2015 09:40:33 +0530 Subject: dmaengine: qcom_bam_dma: Add support for BAM v1.7.0 Add register offset table entry for the newer (v1.7.0) version of the BAM IP found on MSM8916. Update the DT bindings documentation. Signed-off-by: Archit Taneja Tested-by: Ivan T. Ivanov Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt index f8c3311..1c9d48e 100644 --- a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt +++ b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt @@ -4,6 +4,7 @@ Required properties: - compatible: must be one of the following: * "qcom,bam-v1.4.0" for MSM8974, APQ8074 and APQ8084 * "qcom,bam-v1.3.0" for APQ8064, IPQ8064 and MSM8960 + * "qcom,bam-v1.7.0" for MSM8916 - reg: Address range for DMA registers - interrupts: Should contain the one interrupt shared by all channels - #dma-cells: must be <1>, the cell in the dmas property of the client device diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c index cbacee5..1576cd9 100644 --- a/drivers/dma/qcom_bam_dma.c +++ b/drivers/dma/qcom_bam_dma.c @@ -171,6 +171,35 @@ static const struct reg_offset_data bam_v1_4_reg_info[] = { [BAM_P_FIFO_SIZES] = { 0x1820, 0x00, 0x1000, 0x00 }, }; +static const struct reg_offset_data bam_v1_7_reg_info[] = { + [BAM_CTRL] = { 0x00000, 0x00, 0x00, 0x00 }, + [BAM_REVISION] = { 0x01000, 0x00, 0x00, 0x00 }, + [BAM_NUM_PIPES] = { 0x01008, 0x00, 0x00, 0x00 }, + [BAM_DESC_CNT_TRSHLD] = { 0x00008, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS] = { 0x03010, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS_MSK] = { 0x03014, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS_UNMASKED] = { 0x03018, 0x00, 0x00, 0x00 }, + [BAM_IRQ_STTS] = { 0x00014, 0x00, 0x00, 0x00 }, + [BAM_IRQ_CLR] = { 0x00018, 0x00, 0x00, 0x00 }, + [BAM_IRQ_EN] = { 0x0001C, 0x00, 0x00, 0x00 }, + [BAM_CNFG_BITS] = { 0x0007C, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS_EE] = { 0x03000, 0x00, 0x00, 0x1000 }, + [BAM_IRQ_SRCS_MSK_EE] = { 0x03004, 0x00, 0x00, 0x1000 }, + [BAM_P_CTRL] = { 0x13000, 0x1000, 0x00, 0x00 }, + [BAM_P_RST] = { 0x13004, 0x1000, 0x00, 0x00 }, + [BAM_P_HALT] = { 0x13008, 0x1000, 0x00, 0x00 }, + [BAM_P_IRQ_STTS] = { 0x13010, 0x1000, 0x00, 0x00 }, + [BAM_P_IRQ_CLR] = { 0x13014, 0x1000, 0x00, 0x00 }, + [BAM_P_IRQ_EN] = { 0x13018, 0x1000, 0x00, 0x00 }, + [BAM_P_EVNT_DEST_ADDR] = { 0x1382C, 0x00, 0x1000, 0x00 }, + [BAM_P_EVNT_REG] = { 0x13818, 0x00, 0x1000, 0x00 }, + [BAM_P_SW_OFSTS] = { 0x13800, 0x00, 0x1000, 0x00 }, + [BAM_P_DATA_FIFO_ADDR] = { 0x13824, 0x00, 0x1000, 0x00 }, + [BAM_P_DESC_FIFO_ADDR] = { 0x1381C, 0x00, 0x1000, 0x00 }, + [BAM_P_EVNT_GEN_TRSHLD] = { 0x13828, 0x00, 0x1000, 0x00 }, + [BAM_P_FIFO_SIZES] = { 0x13820, 0x00, 0x1000, 0x00 }, +}; + /* BAM CTRL */ #define BAM_SW_RST BIT(0) #define BAM_EN BIT(1) @@ -1051,6 +1080,7 @@ static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan, static const struct of_device_id bam_of_match[] = { { .compatible = "qcom,bam-v1.3.0", .data = &bam_v1_3_reg_info }, { .compatible = "qcom,bam-v1.4.0", .data = &bam_v1_4_reg_info }, + { .compatible = "qcom,bam-v1.7.0", .data = &bam_v1_7_reg_info }, {} }; -- cgit v0.10.2 From 3b62286d0ef785815994e2558e8cfb686597b0cd Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 16 Mar 2015 09:37:24 +0200 Subject: dmaengine: Remove FSF mailing addresses Free Software Foundation mailing address has been moved in the past and some of the addresses here are outdated. Remove them from file headers since the COPYING file in the kernel sources includes it. Signed-off-by: Jarkko Nikula Signed-off-by: Vinod Koul diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 4a5fd24..171e768 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -15,10 +15,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is in this distribution in the file * called COPYING. * diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index f6a4c42..248a6ee 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -7,10 +7,6 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 344b0ac..24967c8 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 5aaead9..c17e18b 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h index 17b4219..ebdd567 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * diff --git a/drivers/dma/ioat/dca.c b/drivers/dma/ioat/dca.c index 3b55bb8..ea1e107 100644 --- a/drivers/dma/ioat/dca.c +++ b/drivers/dma/ioat/dca.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 940c150..ee0aa9f 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index d63f68b..30f5c7e 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 695483e..69c7dfc 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/dma_v2.h b/drivers/dma/ioat/dma_v2.h index 4702927..bf24ebe 100644 --- a/drivers/dma/ioat/dma_v2.h +++ b/drivers/dma/ioat/dma_v2.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 77a6dcf..3d19a31 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -15,10 +15,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index 02177ec..a3e731e 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index 5501eb0..76f0dc6 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/dma/ioat/registers.h b/drivers/dma/ioat/registers.h index 2f1cfa0..909352f 100644 --- a/drivers/dma/ioat/registers.h +++ b/drivers/dma/ioat/registers.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 263d9f6..9988268 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 57d2457..c66a82e 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -21,10 +21,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index b03e813..5ef567e 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -10,10 +10,6 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #include diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index 78edc7e..91958db 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -9,10 +9,6 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef MV_XOR_H diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index fa764a3..9217f89 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -16,10 +16,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 2bff9ab..ad41975 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * * The full GNU General Public License is included in this distribution in the * file called COPYING. */ -- cgit v0.10.2 From c078c62a752872fb83f56e41794dd61d5ea570a0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 16 Mar 2015 08:53:40 +0000 Subject: mmc: sh_mobile_sdhi: remove sh_mobile_sdhi_info v2 84f11d5b1f2abc0e22895b7e12e037f0ec03caeb (mmc: sh_mobile_sdhi: remove sh_mobile_sdhi_info) replaced sh_mobile_sdhi_info to tmio_mmc_data, but it was missing to replace board-ape6evm / board-mackerel. Kernel build will be failed without this patch. >> arch/arm/mach-shmobile/board-ape6evm.c:176:21: error: \ variable 'sdhi0_pdata' has initializer but incomplete type static const struct sh_mobile_sdhi_info sdhi0_pdata __initconst = { ^ >> arch/arm/mach-shmobile/board-ape6evm.c:177:2: error: \ unknown field 'tmio_flags' specified in initializer .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, ^ ... Signed-off-by: Kuninori Morimoto Acked-by: Arnd Bergmann Acked-by: Simon Horman Signed-off-by: Vinod Koul diff --git a/arch/arm/mach-shmobile/board-ape6evm.c b/arch/arm/mach-shmobile/board-ape6evm.c index 444f22d..52c9605 100644 --- a/arch/arm/mach-shmobile/board-ape6evm.c +++ b/arch/arm/mach-shmobile/board-ape6evm.c @@ -173,9 +173,9 @@ static const struct resource mmcif0_resources[] __initconst = { }; /* SDHI0 */ -static const struct sh_mobile_sdhi_info sdhi0_pdata __initconst = { - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, +static const struct tmio_mmc_data sdhi0_pdata __initconst = { + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, }; static const struct resource sdhi0_resources[] __initconst = { @@ -184,9 +184,9 @@ static const struct resource sdhi0_resources[] __initconst = { }; /* SDHI1 */ -static const struct sh_mobile_sdhi_info sdhi1_pdata __initconst = { - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static const struct tmio_mmc_data sdhi1_pdata __initconst = { + .flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL, }; diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index a1c1dfb..88e4f45 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -972,11 +972,11 @@ static struct platform_device nand_flash_device = { }; /* SDHI0 */ -static struct sh_mobile_sdhi_info sdhi0_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX, - .tmio_flags = TMIO_MMC_USE_GPIO_CD, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, +static struct tmio_mmc_data sdhi0_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX, + .flags = TMIO_MMC_USE_GPIO_CD, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, .cd_gpio = 172, }; @@ -1011,11 +1011,11 @@ static struct platform_device sdhi0_device = { /* SDHI1 */ /* GPIO 41 can trigger IRQ8, but it is used by USBHS1, we have to poll */ -static struct sh_mobile_sdhi_info sdhi1_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX, - .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_USE_GPIO_CD, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi1_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_USE_GPIO_CD, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL, .cd_gpio = 41, }; @@ -1054,11 +1054,11 @@ static struct platform_device sdhi1_device = { * The card detect pin of the top SD/MMC slot (CN23) is active low and is * connected to GPIO SCIFB_SCK of SH7372 (GPIO 162). */ -static struct sh_mobile_sdhi_info sdhi2_info = { - .dma_slave_tx = SHDMA_SLAVE_SDHI2_TX, - .dma_slave_rx = SHDMA_SLAVE_SDHI2_RX, - .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_USE_GPIO_CD, - .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | +static struct tmio_mmc_data sdhi2_info = { + .chan_priv_tx = (void *)SHDMA_SLAVE_SDHI2_TX, + .chan_priv_rx = (void *)SHDMA_SLAVE_SDHI2_RX, + .flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_USE_GPIO_CD, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL, .cd_gpio = 162, }; -- cgit v0.10.2 From 937abe88aea3161cd3a563e577fc9cf4522c7aad Mon Sep 17 00:00:00 2001 From: Kedareswara rao Appana Date: Mon, 2 Mar 2015 23:24:24 +0530 Subject: dmaengine: xilinx-dma: move header file to common location This patch moves the xilinx_dma.h header file to the include/linux/dma. Signed-off-by: Kedareswara rao Appana Signed-off-by: Vinod Koul diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c index bdd2a5d..d8434d4 100644 --- a/drivers/dma/xilinx/xilinx_vdma.c +++ b/drivers/dma/xilinx/xilinx_vdma.c @@ -22,9 +22,9 @@ * (at your option) any later version. */ -#include #include #include +#include #include #include #include diff --git a/include/linux/amba/xilinx_dma.h b/include/linux/amba/xilinx_dma.h deleted file mode 100644 index 34b98f2..0000000 --- a/include/linux/amba/xilinx_dma.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Xilinx DMA Engine drivers support header file - * - * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __DMA_XILINX_DMA_H -#define __DMA_XILINX_DMA_H - -#include -#include - -/** - * struct xilinx_vdma_config - VDMA Configuration structure - * @frm_dly: Frame delay - * @gen_lock: Whether in gen-lock mode - * @master: Master that it syncs to - * @frm_cnt_en: Enable frame count enable - * @park: Whether wants to park - * @park_frm: Frame to park on - * @coalesc: Interrupt coalescing threshold - * @delay: Delay counter - * @reset: Reset Channel - * @ext_fsync: External Frame Sync source - */ -struct xilinx_vdma_config { - int frm_dly; - int gen_lock; - int master; - int frm_cnt_en; - int park; - int park_frm; - int coalesc; - int delay; - int reset; - int ext_fsync; -}; - -int xilinx_vdma_channel_set_config(struct dma_chan *dchan, - struct xilinx_vdma_config *cfg); - -#endif diff --git a/include/linux/dma/xilinx_dma.h b/include/linux/dma/xilinx_dma.h new file mode 100644 index 0000000..34b98f2 --- /dev/null +++ b/include/linux/dma/xilinx_dma.h @@ -0,0 +1,47 @@ +/* + * Xilinx DMA Engine drivers support header file + * + * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DMA_XILINX_DMA_H +#define __DMA_XILINX_DMA_H + +#include +#include + +/** + * struct xilinx_vdma_config - VDMA Configuration structure + * @frm_dly: Frame delay + * @gen_lock: Whether in gen-lock mode + * @master: Master that it syncs to + * @frm_cnt_en: Enable frame count enable + * @park: Whether wants to park + * @park_frm: Frame to park on + * @coalesc: Interrupt coalescing threshold + * @delay: Delay counter + * @reset: Reset Channel + * @ext_fsync: External Frame Sync source + */ +struct xilinx_vdma_config { + int frm_dly; + int gen_lock; + int master; + int frm_cnt_en; + int park; + int park_frm; + int coalesc; + int delay; + int reset; + int ext_fsync; +}; + +int xilinx_vdma_channel_set_config(struct dma_chan *dchan, + struct xilinx_vdma_config *cfg); + +#endif -- cgit v0.10.2 From a572460be9cfb423c60275943f7921003b8cd372 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 11 Mar 2015 12:30:58 -0300 Subject: dmaengine: imx-sdma: Add support for version 3 firmware Currently when version 3.1 of the mx6q SDMA firmware is used we get: [ 0.392169] imx-sdma 20ec000.sdma: unknown firmware version [ 0.399281] imx-sdma 20ec000.sdma: initialized Add support for it. Based on a patch from Shengjiu Wang from the internal FSL kernel. Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 18c0a13..eb10109 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1256,6 +1256,7 @@ static void sdma_issue_pending(struct dma_chan *chan) #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 38 +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 41 static void sdma_add_scripts(struct sdma_engine *sdma, const struct sdma_script_start_addrs *addr) @@ -1302,6 +1303,9 @@ static void sdma_load_firmware(const struct firmware *fw, void *context) case 2: sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; break; + case 3: + sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3; + break; default: dev_err(sdma->dev, "unknown firmware version\n"); goto err_firmware; diff --git a/include/linux/platform_data/dma-imx-sdma.h b/include/linux/platform_data/dma-imx-sdma.h index eabac4e..2d08816 100644 --- a/include/linux/platform_data/dma-imx-sdma.h +++ b/include/linux/platform_data/dma-imx-sdma.h @@ -48,6 +48,9 @@ struct sdma_script_start_addrs { s32 ssish_2_mcu_addr; s32 hdmi_dma_addr; /* End of v2 array */ + s32 zcanfd_2_mcu_addr; + s32 zqspi_2_mcu_addr; + /* End of v3 array */ }; /** -- cgit v0.10.2 From 3cd44dcd35a6618df88c51561290dc02fb35b8e2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Mar 2015 00:39:49 +0000 Subject: dmaengine: remove Renesas Audio DMAC peri peri Renesas R-Car sound (= rsnd) needs 2 DMAC which are called as Audio DMAC (= 1st DMAC) and Audio DMAC peri peri (2nd DMAC). And rsnd had assumed that 1st / 2nd DMACs are implemented as DMAEngine. But, in result of DMA ML discussion, 2nd DMAC was concluded that it is not a general purpose DMAC (2nd DMAC is for Device to Device inside sound system). Additionally, current DMAEngine can't support Device to Device, and we don't have correct DT bindings for it at this point. So the easiest solution for it is that move it from DMAEngine to rsnd driver. Audio DMAC peri peri on DMAEngine is no longer needed. remove it. Signed-off-by: Kuninori Morimoto Acked-by: Geert Uytterhoeven Acked-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/dma/rcar-audmapp.txt b/Documentation/devicetree/bindings/dma/rcar-audmapp.txt deleted file mode 100644 index 61bca50..0000000 --- a/Documentation/devicetree/bindings/dma/rcar-audmapp.txt +++ /dev/null @@ -1,29 +0,0 @@ -* R-Car Audio DMAC peri peri Device Tree bindings - -Required properties: -- compatible: should be "renesas,rcar-audmapp" -- #dma-cells: should be <1>, see "dmas" property below - -Example: - audmapp: audio-dma-pp@0xec740000 { - compatible = "renesas,rcar-audmapp"; - #dma-cells = <1>; - - reg = <0 0xec740000 0 0x200>; - }; - - -* DMA client - -Required properties: -- dmas: a list of <[DMA multiplexer phandle] [SRS << 8 | DRS]> pairs. - where SRS/DRS are specified in the SoC manual. - It will be written into PDMACHCR as high 16-bit parts. -- dma-names: a list of DMA channel names, one per "dmas" entry - -Example: - - dmas = <&audmapp 0x2d00 - &audmapp 0x3700>; - dma-names = "src0_ssiu0", - "dvc0_ssiu0"; diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index 8190ad2..725f6b4 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -51,12 +51,6 @@ config RCAR_HPB_DMAE help Enable support for the Renesas R-Car series DMA controllers. -config RCAR_AUDMAC_PP - tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support" - depends on SH_DMAE_BASE - help - Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers. - config RCAR_DMAC tristate "Renesas R-Car Gen2 DMA Controller" depends on ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index 2852f9d..489609b 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -15,5 +15,4 @@ obj-$(CONFIG_SH_DMAE) += shdma.o obj-$(CONFIG_SUDMAC) += sudmac.o obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o -obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o obj-$(CONFIG_RCAR_DMAC) += rcar-dmac.o diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c deleted file mode 100644 index d95bbdd..0000000 --- a/drivers/dma/sh/rcar-audmapp.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * This is for Renesas R-Car Audio-DMAC-peri-peri. - * - * Copyright (C) 2014 Renesas Electronics Corporation - * Copyright (C) 2014 Kuninori Morimoto - * - * based on the drivers/dma/sh/shdma.c - * - * Copyright (C) 2011-2012 Guennadi Liakhovetski - * Copyright (C) 2009 Nobuhiro Iwamatsu - * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. - * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * DMA register - */ -#define PDMASAR 0x00 -#define PDMADAR 0x04 -#define PDMACHCR 0x0c - -/* PDMACHCR */ -#define PDMACHCR_DE (1 << 0) - -#define AUDMAPP_MAX_CHANNELS 29 - -/* Default MEMCPY transfer size = 2^2 = 4 bytes */ -#define LOG2_DEFAULT_XFER_SIZE 2 -#define AUDMAPP_SLAVE_NUMBER 256 -#define AUDMAPP_LEN_MAX (16 * 1024 * 1024) - -struct audmapp_chan { - struct shdma_chan shdma_chan; - void __iomem *base; - dma_addr_t slave_addr; - u32 chcr; -}; - -struct audmapp_device { - struct shdma_dev shdma_dev; - struct audmapp_pdata *pdata; - struct device *dev; - void __iomem *chan_reg; -}; - -struct audmapp_desc { - struct shdma_desc shdma_desc; - dma_addr_t src; - dma_addr_t dst; -}; - -#define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan) - -#define to_chan(chan) container_of(chan, struct audmapp_chan, shdma_chan) -#define to_desc(sdesc) container_of(sdesc, struct audmapp_desc, shdma_desc) -#define to_dev(chan) container_of(chan->shdma_chan.dma_chan.device, \ - struct audmapp_device, shdma_dev.dma_dev) - -static void audmapp_write(struct audmapp_chan *auchan, u32 data, u32 reg) -{ - struct audmapp_device *audev = to_dev(auchan); - struct device *dev = audev->dev; - - dev_dbg(dev, "w %p : %08x\n", auchan->base + reg, data); - - iowrite32(data, auchan->base + reg); -} - -static u32 audmapp_read(struct audmapp_chan *auchan, u32 reg) -{ - return ioread32(auchan->base + reg); -} - -static void audmapp_halt(struct shdma_chan *schan) -{ - struct audmapp_chan *auchan = to_chan(schan); - int i; - - audmapp_write(auchan, 0, PDMACHCR); - - for (i = 0; i < 1024; i++) { - if (0 == audmapp_read(auchan, PDMACHCR)) - return; - udelay(1); - } -} - -static void audmapp_start_xfer(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - struct audmapp_chan *auchan = to_chan(schan); - struct audmapp_device *audev = to_dev(auchan); - struct audmapp_desc *desc = to_desc(sdesc); - struct device *dev = audev->dev; - u32 chcr = auchan->chcr | PDMACHCR_DE; - - dev_dbg(dev, "src/dst/chcr = %pad/%pad/%08x\n", - &desc->src, &desc->dst, chcr); - - audmapp_write(auchan, desc->src, PDMASAR); - audmapp_write(auchan, desc->dst, PDMADAR); - audmapp_write(auchan, chcr, PDMACHCR); -} - -static int audmapp_get_config(struct audmapp_chan *auchan, int slave_id, - u32 *chcr, dma_addr_t *dst) -{ - struct audmapp_device *audev = to_dev(auchan); - struct audmapp_pdata *pdata = audev->pdata; - struct audmapp_slave_config *cfg; - int i; - - *chcr = 0; - *dst = 0; - - if (!pdata) { /* DT */ - *chcr = ((u32)slave_id) << 16; - auchan->shdma_chan.slave_id = (slave_id) >> 8; - return 0; - } - - /* non-DT */ - - if (slave_id >= AUDMAPP_SLAVE_NUMBER) - return -ENXIO; - - for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) - if (cfg->slave_id == slave_id) { - *chcr = cfg->chcr; - *dst = cfg->dst; - return 0; - } - - return -ENXIO; -} - -static int audmapp_set_slave(struct shdma_chan *schan, int slave_id, - dma_addr_t slave_addr, bool try) -{ - struct audmapp_chan *auchan = to_chan(schan); - u32 chcr; - dma_addr_t dst; - int ret; - - ret = audmapp_get_config(auchan, slave_id, &chcr, &dst); - if (ret < 0) - return ret; - - if (try) - return 0; - - auchan->chcr = chcr; - auchan->slave_addr = slave_addr ? : dst; - - return 0; -} - -static int audmapp_desc_setup(struct shdma_chan *schan, - struct shdma_desc *sdesc, - dma_addr_t src, dma_addr_t dst, size_t *len) -{ - struct audmapp_desc *desc = to_desc(sdesc); - - if (*len > (size_t)AUDMAPP_LEN_MAX) - *len = (size_t)AUDMAPP_LEN_MAX; - - desc->src = src; - desc->dst = dst; - - return 0; -} - -static void audmapp_setup_xfer(struct shdma_chan *schan, - int slave_id) -{ -} - -static dma_addr_t audmapp_slave_addr(struct shdma_chan *schan) -{ - struct audmapp_chan *auchan = to_chan(schan); - - return auchan->slave_addr; -} - -static bool audmapp_channel_busy(struct shdma_chan *schan) -{ - struct audmapp_chan *auchan = to_chan(schan); - u32 chcr = audmapp_read(auchan, PDMACHCR); - - return chcr & ~PDMACHCR_DE; -} - -static bool audmapp_desc_completed(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - return true; -} - -static struct shdma_desc *audmapp_embedded_desc(void *buf, int i) -{ - return &((struct audmapp_desc *)buf)[i].shdma_desc; -} - -static const struct shdma_ops audmapp_shdma_ops = { - .halt_channel = audmapp_halt, - .desc_setup = audmapp_desc_setup, - .set_slave = audmapp_set_slave, - .start_xfer = audmapp_start_xfer, - .embedded_desc = audmapp_embedded_desc, - .setup_xfer = audmapp_setup_xfer, - .slave_addr = audmapp_slave_addr, - .channel_busy = audmapp_channel_busy, - .desc_completed = audmapp_desc_completed, -}; - -static int audmapp_chan_probe(struct platform_device *pdev, - struct audmapp_device *audev, int id) -{ - struct shdma_dev *sdev = &audev->shdma_dev; - struct audmapp_chan *auchan; - struct shdma_chan *schan; - struct device *dev = audev->dev; - - auchan = devm_kzalloc(dev, sizeof(*auchan), GFP_KERNEL); - if (!auchan) - return -ENOMEM; - - schan = &auchan->shdma_chan; - schan->max_xfer_len = AUDMAPP_LEN_MAX; - - shdma_chan_probe(sdev, schan, id); - - auchan->base = audev->chan_reg + 0x20 + (0x10 * id); - dev_dbg(dev, "%02d : %p / %p", id, auchan->base, audev->chan_reg); - - return 0; -} - -static void audmapp_chan_remove(struct audmapp_device *audev) -{ - struct shdma_chan *schan; - int i; - - shdma_for_each_chan(schan, &audev->shdma_dev, i) { - BUG_ON(!schan); - shdma_chan_remove(schan); - } -} - -static struct dma_chan *audmapp_of_xlate(struct of_phandle_args *dma_spec, - struct of_dma *ofdma) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - u32 chcr = dma_spec->args[0]; - - if (dma_spec->args_count != 1) - return NULL; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - chan = dma_request_channel(mask, shdma_chan_filter, NULL); - if (chan) - to_shdma_chan(chan)->hw_req = chcr; - - return chan; -} - -static int audmapp_probe(struct platform_device *pdev) -{ - struct audmapp_pdata *pdata = pdev->dev.platform_data; - struct device_node *np = pdev->dev.of_node; - struct audmapp_device *audev; - struct shdma_dev *sdev; - struct dma_device *dma_dev; - struct resource *res; - int err, i; - - if (np) - of_dma_controller_register(np, audmapp_of_xlate, pdev); - else if (!pdata) - return -ENODEV; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - audev = devm_kzalloc(&pdev->dev, sizeof(*audev), GFP_KERNEL); - if (!audev) - return -ENOMEM; - - audev->dev = &pdev->dev; - audev->pdata = pdata; - audev->chan_reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(audev->chan_reg)) - return PTR_ERR(audev->chan_reg); - - sdev = &audev->shdma_dev; - sdev->ops = &audmapp_shdma_ops; - sdev->desc_size = sizeof(struct audmapp_desc); - - dma_dev = &sdev->dma_dev; - dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE; - dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); - - err = shdma_init(&pdev->dev, sdev, AUDMAPP_MAX_CHANNELS); - if (err < 0) - return err; - - platform_set_drvdata(pdev, audev); - - /* Create DMA Channel */ - for (i = 0; i < AUDMAPP_MAX_CHANNELS; i++) { - err = audmapp_chan_probe(pdev, audev, i); - if (err) - goto chan_probe_err; - } - - err = dma_async_device_register(dma_dev); - if (err < 0) - goto chan_probe_err; - - return err; - -chan_probe_err: - audmapp_chan_remove(audev); - shdma_cleanup(sdev); - - return err; -} - -static int audmapp_remove(struct platform_device *pdev) -{ - struct audmapp_device *audev = platform_get_drvdata(pdev); - struct dma_device *dma_dev = &audev->shdma_dev.dma_dev; - - dma_async_device_unregister(dma_dev); - - audmapp_chan_remove(audev); - shdma_cleanup(&audev->shdma_dev); - - return 0; -} - -static const struct of_device_id audmapp_of_match[] = { - { .compatible = "renesas,rcar-audmapp", }, - {}, -}; - -static struct platform_driver audmapp_driver = { - .probe = audmapp_probe, - .remove = audmapp_remove, - .driver = { - .name = "rcar-audmapp-engine", - .of_match_table = audmapp_of_match, - }, -}; -module_platform_driver(audmapp_driver); - -MODULE_AUTHOR("Kuninori Morimoto "); -MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 57c0342239b028db54741209a051a4d5f7c857e7 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:17:14 +0100 Subject: dmaengine: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Vinod Koul diff --git a/drivers/dma/bestcomm/bestcomm.c b/drivers/dma/bestcomm/bestcomm.c index fa378d8..180fedb 100644 --- a/drivers/dma/bestcomm/bestcomm.c +++ b/drivers/dma/bestcomm/bestcomm.c @@ -30,7 +30,7 @@ #define DRIVER_NAME "bestcomm-core" /* MPC5200 device tree match tables */ -static struct of_device_id mpc52xx_sram_ids[] = { +static const struct of_device_id mpc52xx_sram_ids[] = { { .compatible = "fsl,mpc5200-sram", }, { .compatible = "mpc5200-sram", }, {} @@ -481,7 +481,7 @@ static int mpc52xx_bcom_remove(struct platform_device *op) return 0; } -static struct of_device_id mpc52xx_bcom_of_match[] = { +static const struct of_device_id mpc52xx_bcom_of_match[] = { { .compatible = "fsl,mpc5200-bestcomm", }, { .compatible = "mpc5200-bestcomm", }, {}, diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 6f7f435..3ac9256 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -654,7 +654,7 @@ static void k3_dma_free_desc(struct virt_dma_desc *vd) kfree(ds); } -static struct of_device_id k3_pdma_dt_ids[] = { +static const struct of_device_id k3_pdma_dt_ids[] = { { .compatible = "hisilicon,k3-dma-1.0", }, {} }; diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 8926f27..4c4cda9 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -970,7 +970,7 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq) return 0; } -static struct of_device_id mmp_pdma_dt_ids[] = { +static const struct of_device_id mmp_pdma_dt_ids[] = { { .compatible = "marvell,pdma-1.0", }, {} }; diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 70c2fa9..bc7bc53 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -594,7 +594,7 @@ struct dma_chan *mmp_tdma_xlate(struct of_phandle_args *dma_spec, return dma_request_channel(mask, mmp_tdma_filter_fn, ¶m); } -static struct of_device_id mmp_tdma_dt_ids[] = { +static const struct of_device_id mmp_tdma_dt_ids[] = { { .compatible = "marvell,adma-1.0", .data = (void *)MMP_AUD_TDMA}, { .compatible = "marvell,pxa910-squ", .data = (void *)PXA910_SQU}, {} diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index c66a82e..e6281e7 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -1068,7 +1068,7 @@ static int mpc_dma_remove(struct platform_device *op) return 0; } -static struct of_device_id mpc_dma_match[] = { +static const struct of_device_id mpc_dma_match[] = { { .compatible = "fsl,mpc5121-dma", }, { .compatible = "fsl,mpc8308-dma", }, {}, diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 5ef567e..1c56001 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1245,7 +1245,7 @@ static int mv_xor_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id mv_xor_dt_ids[] = { +static const struct of_device_id mv_xor_dt_ids[] = { { .compatible = "marvell,orion-xor", }, {}, }; diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index d0086e9..a1afda4 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -896,7 +896,7 @@ static const struct dev_pm_ops sirfsoc_dma_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_dma_pm_suspend, sirfsoc_dma_pm_resume) }; -static struct of_device_id sirfsoc_dma_match[] = { +static const struct of_device_id sirfsoc_dma_match[] = { { .compatible = "sirf,prima2-dmac", }, { .compatible = "sirf,marco-dmac", }, {}, diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 7ebcf9b..5358453 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -896,7 +896,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_vchans = 37, }; -static struct of_device_id sun6i_dma_match[] = { +static const struct of_device_id sun6i_dma_match[] = { { .compatible = "allwinner,sun6i-a31-dma", .data = &sun6i_a31_dma_cfg }, { .compatible = "allwinner,sun8i-a23-dma", .data = &sun8i_a23_dma_cfg }, { /* sentinel */ } -- cgit v0.10.2 From 3a2307f72e684199a27ae9d8e7cfd98e8b05c8db Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 16 Mar 2015 11:52:43 +0000 Subject: dmaengine: pl330: fix issues with big-endian armv7 When running Xilinx Zynq in big-endian mode the pl330 driver fails to pass the dmatest suite. To fix this, ensure all non byte values are written in little endian. As a note, the documentation does not mention if it will do big-endian descriptor fetches, only that it will swap the data in flight. Signed-off-by: Ben Dooks -- Vinod Koul Dan Williams DMA List Maxime Ripard Jassi Brar Liviu Dudau Linux ARM Kernel Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 0e1f567..f76c94c 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -556,7 +556,7 @@ static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[], buf[0] = CMD_DMAADDH; buf[0] |= (da << 1); - *((u16 *)&buf[1]) = val; + *((__le16 *)&buf[1]) = cpu_to_le16(val); PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n", da == 1 ? "DA" : "SA", val); @@ -710,7 +710,7 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], buf[0] = CMD_DMAMOV; buf[1] = dst; - *((u32 *)&buf[2]) = val; + *((__le32 *)&buf[2]) = cpu_to_le32(val); PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); @@ -888,7 +888,7 @@ static inline u32 _emit_GO(unsigned dry_run, u8 buf[], buf[1] = chan & 0x7; - *((u32 *)&buf[2]) = addr; + *((__le32 *)&buf[2]) = cpu_to_le32(addr); return SZ_DMAGO; } @@ -928,7 +928,7 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd, } writel(val, regs + DBGINST0); - val = *((u32 *)&insn[2]); + val = le32_to_cpu(*((__le32 *)&insn[2])); writel(val, regs + DBGINST1); /* If timed out due to halted state-machine */ -- cgit v0.10.2 From 5503aed8117881f58a2688521dfbf9fc7dbcdfe7 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 16 Mar 2015 11:52:44 +0000 Subject: dmaengine: pl330: make unexported functions static Whilst running sparse on pl330 driver it was noticed there are two functions that are not static but not exported to any other users in the kernel. Fix the following warnings by making 'pl330_pause' and the 'pl330_get_current_xferred_count' static: pl330.c:2165:5: warning: symbol 'pl330_pause' was not declared. Should it be static? pl330.c:2206:5: warning: symbol 'pl330_get_current_xferred_count' was not declared. Should it be static? Signed-off-by: Ben Dooks -- Vinod Koul Dan Williams DMA List Maxime Ripard Jassi Brar Liviu Dudau Linux ARM Kernel Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f76c94c..d6f677e 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2162,7 +2162,7 @@ static int pl330_terminate_all(struct dma_chan *chan) * DMA transfer again. This pause feature was implemented to * allow safely read residue before channel termination. */ -int pl330_pause(struct dma_chan *chan) +static int pl330_pause(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); struct pl330_dmac *pl330 = pch->dmac; @@ -2203,8 +2203,8 @@ static void pl330_free_chan_resources(struct dma_chan *chan) pm_runtime_put_autosuspend(pch->dmac->ddma.dev); } -int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, - struct dma_pl330_desc *desc) +static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, + struct dma_pl330_desc *desc) { struct pl330_thread *thrd = pch->thread; struct pl330_dmac *pl330 = pch->dmac; -- cgit v0.10.2 From 75967b788c2898601620ce91ed14b4a9a371b6fe Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 16 Mar 2015 11:52:45 +0000 Subject: dmaengine: pl330: fix return status on pending transfers The pl330_tx_status() function returns the desc->status if the dma_cookie_status() call does indicate the cookie completed, however the desc->status is not look directly compatible. Sparse throws the following warning: pl330.c:2262:35: warning: mixing different enum types pl330.c:2262:35: int enum desc_status versus pl330.c:2262:35: int enum dma_status Attempt to fix this by adding a switch statement to turn the desc->status into a dma_status. Note, this has only been tested with the dmatest suite. Signed-off-by: Ben Dooks -- Vinod Koul Dan Williams DMA List Maxime Ripard Jassi Brar Liviu Dudau Linux ARM Kernel Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index d6f677e..a7d9d30 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2259,7 +2259,17 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, transferred = 0; residual += desc->bytes_requested - transferred; if (desc->txd.cookie == cookie) { - ret = desc->status; + switch (desc->status) { + case DONE: + ret = DMA_COMPLETE; + break; + case PREP: + case BUSY: + ret = DMA_IN_PROGRESS; + break; + default: + WARN_ON(1); + } break; } if (desc->last) -- cgit v0.10.2 From 16cab61800cebca941c47c1c9d76fe9f66e1a486 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 13:56:47 +0530 Subject: dmaengine: amba-pl08x:remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Acked-by: Linus Walleij Signed-off-by: Vinod Koul diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 4a5fd24..aae652b 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1189,11 +1189,6 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, /* * The DMA ENGINE API */ -static int pl08x_alloc_chan_resources(struct dma_chan *chan) -{ - return 0; -} - static void pl08x_free_chan_resources(struct dma_chan *chan) { /* Ensure all queued descriptors are freed */ @@ -2060,7 +2055,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) /* Initialize memcpy engine */ dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask); pl08x->memcpy.dev = &adev->dev; - pl08x->memcpy.device_alloc_chan_resources = pl08x_alloc_chan_resources; pl08x->memcpy.device_free_chan_resources = pl08x_free_chan_resources; pl08x->memcpy.device_prep_dma_memcpy = pl08x_prep_dma_memcpy; pl08x->memcpy.device_prep_dma_interrupt = pl08x_prep_dma_interrupt; @@ -2075,7 +2069,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask); dma_cap_set(DMA_CYCLIC, pl08x->slave.cap_mask); pl08x->slave.dev = &adev->dev; - pl08x->slave.device_alloc_chan_resources = pl08x_alloc_chan_resources; pl08x->slave.device_free_chan_resources = pl08x_free_chan_resources; pl08x->slave.device_prep_dma_interrupt = pl08x_prep_dma_interrupt; pl08x->slave.device_tx_status = pl08x_dma_tx_status; -- cgit v0.10.2 From 3d32b2506d0c63aef42de01b268ed4d6e83bfd2f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 13:58:51 +0530 Subject: dmaengine: jz4740: remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Acked-by: Lars-Peter Clausen Signed-off-by: Vinod Koul diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index f6a4c42..93deff0 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -496,11 +496,6 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c, return status; } -static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) -{ - return 0; -} - static void jz4740_dma_free_chan_resources(struct dma_chan *c) { vchan_free_chan_resources(to_virt_chan(c)); @@ -540,7 +535,6 @@ static int jz4740_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, dd->cap_mask); dma_cap_set(DMA_CYCLIC, dd->cap_mask); - dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; dd->device_free_chan_resources = jz4740_dma_free_chan_resources; dd->device_tx_status = jz4740_dma_tx_status; dd->device_issue_pending = jz4740_dma_issue_pending; -- cgit v0.10.2 From f265958a6b6da2af3ff57d44cd661138b04ce78d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 13:59:25 +0530 Subject: dmaengine: img-mdc: remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Acked-by: Andrew Bresticker Signed-off-by: Vinod Koul diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c index ed045a9..9ca5683 100644 --- a/drivers/dma/img-mdc-dma.c +++ b/drivers/dma/img-mdc-dma.c @@ -689,11 +689,6 @@ static int mdc_slave_config(struct dma_chan *chan, return 0; } -static int mdc_alloc_chan_resources(struct dma_chan *chan) -{ - return 0; -} - static void mdc_free_chan_resources(struct dma_chan *chan) { struct mdc_chan *mchan = to_mdc_chan(chan); @@ -910,7 +905,6 @@ static int mdc_dma_probe(struct platform_device *pdev) mdma->dma_dev.device_prep_slave_sg = mdc_prep_slave_sg; mdma->dma_dev.device_prep_dma_cyclic = mdc_prep_dma_cyclic; mdma->dma_dev.device_prep_dma_memcpy = mdc_prep_dma_memcpy; - mdma->dma_dev.device_alloc_chan_resources = mdc_alloc_chan_resources; mdma->dma_dev.device_free_chan_resources = mdc_free_chan_resources; mdma->dma_dev.device_tx_status = mdc_tx_status; mdma->dma_dev.device_issue_pending = mdc_issue_pending; -- cgit v0.10.2 From c509c495c0aca237cb7e3a38cb1fa90ff3a0bdfa Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 13:59:48 +0530 Subject: dmaengine: k3dma: remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Acked-by: Zhangfei Gao Signed-off-by: Vinod Koul diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 6f7f435..c535c58 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -313,11 +313,6 @@ static void k3_dma_tasklet(unsigned long arg) } } -static int k3_dma_alloc_chan_resources(struct dma_chan *chan) -{ - return 0; -} - static void k3_dma_free_chan_resources(struct dma_chan *chan) { struct k3_dma_chan *c = to_k3_chan(chan); @@ -728,7 +723,6 @@ static int k3_dma_probe(struct platform_device *op) dma_cap_set(DMA_SLAVE, d->slave.cap_mask); dma_cap_set(DMA_MEMCPY, d->slave.cap_mask); d->slave.dev = &op->dev; - d->slave.device_alloc_chan_resources = k3_dma_alloc_chan_resources; d->slave.device_free_chan_resources = k3_dma_free_chan_resources; d->slave.device_tx_status = k3_dma_tx_status; d->slave.device_prep_dma_memcpy = k3_dma_prep_memcpy; -- cgit v0.10.2 From b07064aa080c3c769ab7698e63bed81e0f29155d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 14:00:09 +0530 Subject: dmaengine: s3c24xx: remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Signed-off-by: Vinod Koul diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 110a0cd..01dcaf2 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -749,11 +749,6 @@ unlock: return ret; } -static int s3c24xx_dma_alloc_chan_resources(struct dma_chan *chan) -{ - return 0; -} - static void s3c24xx_dma_free_chan_resources(struct dma_chan *chan) { /* Ensure all queued descriptors are freed */ @@ -1290,8 +1285,6 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, s3cdma->memcpy.cap_mask); dma_cap_set(DMA_PRIVATE, s3cdma->memcpy.cap_mask); s3cdma->memcpy.dev = &pdev->dev; - s3cdma->memcpy.device_alloc_chan_resources = - s3c24xx_dma_alloc_chan_resources; s3cdma->memcpy.device_free_chan_resources = s3c24xx_dma_free_chan_resources; s3cdma->memcpy.device_prep_dma_memcpy = s3c24xx_dma_prep_memcpy; @@ -1305,8 +1298,6 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_CYCLIC, s3cdma->slave.cap_mask); dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask); s3cdma->slave.dev = &pdev->dev; - s3cdma->slave.device_alloc_chan_resources = - s3c24xx_dma_alloc_chan_resources; s3cdma->slave.device_free_chan_resources = s3c24xx_dma_free_chan_resources; s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status; -- cgit v0.10.2 From 7595c5717f2be8708f5e6bdd9365f15354ccd024 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 14:00:27 +0530 Subject: dmaengine: sa11x0: remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Signed-off-by: Vinod Koul diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index 5adf540..dafe13f 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -389,11 +389,6 @@ static void sa11x0_dma_tasklet(unsigned long arg) } -static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan) -{ - return 0; -} - static void sa11x0_dma_free_chan_resources(struct dma_chan *chan) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); @@ -835,7 +830,6 @@ static int sa11x0_dma_init_dmadev(struct dma_device *dmadev, INIT_LIST_HEAD(&dmadev->channels); dmadev->dev = dev; - dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources; dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources; dmadev->device_config = sa11x0_dma_device_config; dmadev->device_pause = sa11x0_dma_device_pause; -- cgit v0.10.2 From 2fcb9e3c86fc312beb031832fca783498fd4bdb5 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 16 Mar 2015 14:00:46 +0530 Subject: dmaengine: sun6i: remove device_alloc_chan_resources handler Now that device_alloc_chan_resources handler in not mandatory, remove dummy implementations Acked-by: Maxime Ripard Signed-off-by: Vinod Koul diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 7ebcf9b..6506834 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -796,11 +796,6 @@ static void sun6i_dma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&vchan->vc.lock, flags); } -static int sun6i_dma_alloc_chan_resources(struct dma_chan *chan) -{ - return 0; -} - static void sun6i_dma_free_chan_resources(struct dma_chan *chan) { struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device); @@ -957,7 +952,6 @@ static int sun6i_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, sdc->slave.cap_mask); INIT_LIST_HEAD(&sdc->slave.channels); - sdc->slave.device_alloc_chan_resources = sun6i_dma_alloc_chan_resources; sdc->slave.device_free_chan_resources = sun6i_dma_free_chan_resources; sdc->slave.device_tx_status = sun6i_dma_tx_status; sdc->slave.device_issue_pending = sun6i_dma_issue_pending; -- cgit v0.10.2 From c8307106f5fa53b8fe8763b488d629e3cce9fae3 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 18 Mar 2015 16:16:35 +0000 Subject: dt-bindings: dma: Add binding for jz4780-dma Add device tree bindings for the DMA controller on JZ4780 SoCs, used by the dma-jz4780 driver. Signed-off-by: Alex Smith Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/jz4780-dma.txt b/Documentation/devicetree/bindings/dma/jz4780-dma.txt new file mode 100644 index 0000000..f25feee --- /dev/null +++ b/Documentation/devicetree/bindings/dma/jz4780-dma.txt @@ -0,0 +1,56 @@ +* Ingenic JZ4780 DMA Controller + +Required properties: + +- compatible: Should be "ingenic,jz4780-dma" +- reg: Should contain the DMA controller registers location and length. +- interrupts: Should contain the interrupt specifier of the DMA controller. +- interrupt-parent: Should be the phandle of the interrupt controller that +- clocks: Should contain a clock specifier for the JZ4780 PDMA clock. +- #dma-cells: Must be <2>. Number of integer cells in the dmas property of + DMA clients (see below). + +Optional properties: + +- ingenic,reserved-channels: Bitmask of channels to reserve for devices that + need a specific channel. These channels will only be assigned when explicitly + requested by a client. The primary use for this is channels 0 and 1, which + can be configured to have special behaviour for NAND/BCH when using + programmable firmware. + +Example: + +dma: dma@13420000 { + compatible = "ingenic,jz4780-dma"; + reg = <0x13420000 0x10000>; + + interrupt-parent = <&intc>; + interrupts = <10>; + + clocks = <&cgu JZ4780_CLK_PDMA>; + + #dma-cells = <2>; + + ingenic,reserved-channels = <0x3>; +}; + +DMA clients must use the format described in dma.txt, giving a phandle to the +DMA controller plus the following 2 integer cells: + +1. Request type: The DMA request type for transfers to/from the device on + the allocated channel, as defined in the SoC documentation. + +2. Channel: If set to 0xffffffff, any available channel will be allocated for + the client. Otherwise, the exact channel specified will be used. The channel + should be reserved on the DMA controller using the ingenic,reserved-channels + property. + +Example: + +uart0: serial@10030000 { + ... + dmas = <&dma 0x14 0xffffffff + &dma 0x15 0xffffffff>; + dma-names = "tx", "rx"; + ... +}; -- cgit v0.10.2 From d894fc6046fecd66b0d8ec35c7d2515781cc030b Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 18 Mar 2015 16:16:36 +0000 Subject: dmaengine: jz4780: add driver for the Ingenic JZ4780 DMA controller This patch adds a driver for the DMA controller found in the Ingenic JZ4780. It currently does not implement any support for the programmable firmware feature of the controller - this is not necessary for most uses. It also does not take priority into account when allocating channels, it just allocates the first available channel. This can be implemented later. Signed-off-by: Alex Smith Signed-off-by: Zubair Lutfullah Kakakhel [Updated for dmaengine api changes, Add residue support, couple of minor fixes] Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a874b6e..ce09734 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -358,6 +358,16 @@ config DMA_JZ4740 select DMA_ENGINE select DMA_VIRTUAL_CHANNELS +config DMA_JZ4780 + tristate "JZ4780 DMA support" + depends on MACH_JZ4780 + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + This selects support for the DMA controller in Ingenic JZ4780 SoCs. + If you have a board based on such a SoC and wish to use DMA for + devices which can use the DMA controller, say Y or M here. + config K3_DMA tristate "Hisilicon K3 DMA support" depends on ARCH_HI3xxx diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index f915f61..af239e7 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o +obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o obj-$(CONFIG_TI_CPPI41) += cppi41.o obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_MOXART_DMA) += moxart-dma.o diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c new file mode 100644 index 0000000..26d2f0e --- /dev/null +++ b/drivers/dma/dma-jz4780.c @@ -0,0 +1,877 @@ +/* + * Ingenic JZ4780 DMA controller + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "virt-dma.h" + +#define JZ_DMA_NR_CHANNELS 32 + +/* Global registers. */ +#define JZ_DMA_REG_DMAC 0x1000 +#define JZ_DMA_REG_DIRQP 0x1004 +#define JZ_DMA_REG_DDR 0x1008 +#define JZ_DMA_REG_DDRS 0x100c +#define JZ_DMA_REG_DMACP 0x101c +#define JZ_DMA_REG_DSIRQP 0x1020 +#define JZ_DMA_REG_DSIRQM 0x1024 +#define JZ_DMA_REG_DCIRQP 0x1028 +#define JZ_DMA_REG_DCIRQM 0x102c + +/* Per-channel registers. */ +#define JZ_DMA_REG_CHAN(n) (n * 0x20) +#define JZ_DMA_REG_DSA(n) (0x00 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DTA(n) (0x04 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DTC(n) (0x08 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DRT(n) (0x0c + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DCS(n) (0x10 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DCM(n) (0x14 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DDA(n) (0x18 + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DSD(n) (0x1c + JZ_DMA_REG_CHAN(n)) + +#define JZ_DMA_DMAC_DMAE BIT(0) +#define JZ_DMA_DMAC_AR BIT(2) +#define JZ_DMA_DMAC_HLT BIT(3) +#define JZ_DMA_DMAC_FMSC BIT(31) + +#define JZ_DMA_DRT_AUTO 0x8 + +#define JZ_DMA_DCS_CTE BIT(0) +#define JZ_DMA_DCS_HLT BIT(2) +#define JZ_DMA_DCS_TT BIT(3) +#define JZ_DMA_DCS_AR BIT(4) +#define JZ_DMA_DCS_DES8 BIT(30) + +#define JZ_DMA_DCM_LINK BIT(0) +#define JZ_DMA_DCM_TIE BIT(1) +#define JZ_DMA_DCM_STDE BIT(2) +#define JZ_DMA_DCM_TSZ_SHIFT 8 +#define JZ_DMA_DCM_TSZ_MASK (0x7 << JZ_DMA_DCM_TSZ_SHIFT) +#define JZ_DMA_DCM_DP_SHIFT 12 +#define JZ_DMA_DCM_SP_SHIFT 14 +#define JZ_DMA_DCM_DAI BIT(22) +#define JZ_DMA_DCM_SAI BIT(23) + +#define JZ_DMA_SIZE_4_BYTE 0x0 +#define JZ_DMA_SIZE_1_BYTE 0x1 +#define JZ_DMA_SIZE_2_BYTE 0x2 +#define JZ_DMA_SIZE_16_BYTE 0x3 +#define JZ_DMA_SIZE_32_BYTE 0x4 +#define JZ_DMA_SIZE_64_BYTE 0x5 +#define JZ_DMA_SIZE_128_BYTE 0x6 + +#define JZ_DMA_WIDTH_32_BIT 0x0 +#define JZ_DMA_WIDTH_8_BIT 0x1 +#define JZ_DMA_WIDTH_16_BIT 0x2 + +#define JZ_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +/** + * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller. + * @dcm: value for the DCM (channel command) register + * @dsa: source address + * @dta: target address + * @dtc: transfer count (number of blocks of the transfer size specified in DCM + * to transfer) in the low 24 bits, offset of the next descriptor from the + * descriptor base address in the upper 8 bits. + * @sd: target/source stride difference (in stride transfer mode). + * @drt: request type + */ +struct jz4780_dma_hwdesc { + uint32_t dcm; + uint32_t dsa; + uint32_t dta; + uint32_t dtc; + uint32_t sd; + uint32_t drt; + uint32_t reserved[2]; +}; + +/* Size of allocations for hardware descriptor blocks. */ +#define JZ_DMA_DESC_BLOCK_SIZE PAGE_SIZE +#define JZ_DMA_MAX_DESC \ + (JZ_DMA_DESC_BLOCK_SIZE / sizeof(struct jz4780_dma_hwdesc)) + +struct jz4780_dma_desc { + struct virt_dma_desc vdesc; + + struct jz4780_dma_hwdesc *desc; + dma_addr_t desc_phys; + unsigned int count; + enum dma_transaction_type type; + uint32_t status; +}; + +struct jz4780_dma_chan { + struct virt_dma_chan vchan; + unsigned int id; + struct dma_pool *desc_pool; + + uint32_t transfer_type; + uint32_t transfer_shift; + struct dma_slave_config config; + + struct jz4780_dma_desc *desc; + unsigned int curr_hwdesc; +}; + +struct jz4780_dma_dev { + struct dma_device dma_device; + void __iomem *base; + struct clk *clk; + unsigned int irq; + + uint32_t chan_reserved; + struct jz4780_dma_chan chan[JZ_DMA_NR_CHANNELS]; +}; + +struct jz4780_dma_data { + uint32_t transfer_type; + int channel; +}; + +static inline struct jz4780_dma_chan *to_jz4780_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct jz4780_dma_chan, vchan.chan); +} + +static inline struct jz4780_dma_desc *to_jz4780_dma_desc( + struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct jz4780_dma_desc, vdesc); +} + +static inline struct jz4780_dma_dev *jz4780_dma_chan_parent( + struct jz4780_dma_chan *jzchan) +{ + return container_of(jzchan->vchan.chan.device, struct jz4780_dma_dev, + dma_device); +} + +static inline uint32_t jz4780_dma_readl(struct jz4780_dma_dev *jzdma, + unsigned int reg) +{ + return readl(jzdma->base + reg); +} + +static inline void jz4780_dma_writel(struct jz4780_dma_dev *jzdma, + unsigned int reg, uint32_t val) +{ + writel(val, jzdma->base + reg); +} + +static struct jz4780_dma_desc *jz4780_dma_desc_alloc( + struct jz4780_dma_chan *jzchan, unsigned int count, + enum dma_transaction_type type) +{ + struct jz4780_dma_desc *desc; + + if (count > JZ_DMA_MAX_DESC) + return NULL; + + desc = kzalloc(sizeof(*desc), GFP_NOWAIT); + if (!desc) + return NULL; + + desc->desc = dma_pool_alloc(jzchan->desc_pool, GFP_NOWAIT, + &desc->desc_phys); + if (!desc->desc) { + kfree(desc); + return NULL; + } + + desc->count = count; + desc->type = type; + return desc; +} + +static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc) +{ + struct jz4780_dma_desc *desc = to_jz4780_dma_desc(vdesc); + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(vdesc->tx.chan); + + dma_pool_free(jzchan->desc_pool, desc->desc, desc->desc_phys); + kfree(desc); +} + +static uint32_t jz4780_dma_transfer_size(unsigned long val, int *ord) +{ + *ord = ffs(val) - 1; + + switch (*ord) { + case 0: + return JZ_DMA_SIZE_1_BYTE; + case 1: + return JZ_DMA_SIZE_2_BYTE; + case 2: + return JZ_DMA_SIZE_4_BYTE; + case 4: + return JZ_DMA_SIZE_16_BYTE; + case 5: + return JZ_DMA_SIZE_32_BYTE; + case 6: + return JZ_DMA_SIZE_64_BYTE; + case 7: + return JZ_DMA_SIZE_128_BYTE; + default: + return -EINVAL; + } +} + +static uint32_t jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan, + struct jz4780_dma_hwdesc *desc, dma_addr_t addr, size_t len, + enum dma_transfer_direction direction) +{ + struct dma_slave_config *config = &jzchan->config; + uint32_t width, maxburst, tsz; + int ord; + + if (direction == DMA_MEM_TO_DEV) { + desc->dcm = JZ_DMA_DCM_SAI; + desc->dsa = addr; + desc->dta = config->dst_addr; + desc->drt = jzchan->transfer_type; + + width = config->dst_addr_width; + maxburst = config->dst_maxburst; + } else { + desc->dcm = JZ_DMA_DCM_DAI; + desc->dsa = config->src_addr; + desc->dta = addr; + desc->drt = jzchan->transfer_type; + + width = config->src_addr_width; + maxburst = config->src_maxburst; + } + + /* + * This calculates the maximum transfer size that can be used with the + * given address, length, width and maximum burst size. The address + * must be aligned to the transfer size, the total length must be + * divisible by the transfer size, and we must not use more than the + * maximum burst specified by the user. + */ + tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst), &ord); + jzchan->transfer_shift = ord; + + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + case DMA_SLAVE_BUSWIDTH_2_BYTES: + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + width = JZ_DMA_WIDTH_32_BIT; + break; + default: + return -EINVAL; + } + + desc->dcm |= tsz << JZ_DMA_DCM_TSZ_SHIFT; + desc->dcm |= width << JZ_DMA_DCM_SP_SHIFT; + desc->dcm |= width << JZ_DMA_DCM_DP_SHIFT; + + desc->dtc = len >> ord; +} + +static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_desc *desc; + unsigned int i; + int err; + + desc = jz4780_dma_desc_alloc(jzchan, sg_len, DMA_SLAVE); + if (!desc) + return NULL; + + for (i = 0; i < sg_len; i++) { + err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], + sg_dma_address(&sgl[i]), + sg_dma_len(&sgl[i]), + direction); + if (err < 0) + return ERR_PTR(err); + + + desc->desc[i].dcm |= JZ_DMA_DCM_TIE; + + if (i != (sg_len - 1)) { + /* Automatically proceeed to the next descriptor. */ + desc->desc[i].dcm |= JZ_DMA_DCM_LINK; + + /* + * The upper 8 bits of the DTC field in the descriptor + * must be set to (offset from descriptor base of next + * descriptor >> 4). + */ + desc->desc[i].dtc |= + (((i + 1) * sizeof(*desc->desc)) >> 4) << 24; + } + } + + return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_desc *desc; + unsigned int periods, i; + int err; + + if (buf_len % period_len) + return NULL; + + periods = buf_len / period_len; + + desc = jz4780_dma_desc_alloc(jzchan, periods, DMA_CYCLIC); + if (!desc) + return NULL; + + for (i = 0; i < periods; i++) { + err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], buf_addr, + period_len, direction); + if (err < 0) + return ERR_PTR(err); + + buf_addr += period_len; + + /* + * Set the link bit to indicate that the controller should + * automatically proceed to the next descriptor. In + * jz4780_dma_begin(), this will be cleared if we need to issue + * an interrupt after each period. + */ + desc->desc[i].dcm |= JZ_DMA_DCM_TIE | JZ_DMA_DCM_LINK; + + /* + * The upper 8 bits of the DTC field in the descriptor must be + * set to (offset from descriptor base of next descriptor >> 4). + * If this is the last descriptor, link it back to the first, + * i.e. leave offset set to 0, otherwise point to the next one. + */ + if (i != (periods - 1)) { + desc->desc[i].dtc |= + (((i + 1) * sizeof(*desc->desc)) >> 4) << 24; + } + } + + return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); +} + +struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_desc *desc; + uint32_t tsz; + int ord; + + desc = jz4780_dma_desc_alloc(jzchan, 1, DMA_MEMCPY); + if (!desc) + return NULL; + + tsz = jz4780_dma_transfer_size(dest | src | len, &ord); + if (tsz < 0) + return ERR_PTR(tsz); + + desc->desc[0].dsa = src; + desc->desc[0].dta = dest; + desc->desc[0].drt = JZ_DMA_DRT_AUTO; + desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI | + tsz << JZ_DMA_DCM_TSZ_SHIFT | + JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT | + JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_DP_SHIFT; + desc->desc[0].dtc = len >> ord; + + return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); +} + +static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) +{ + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + struct virt_dma_desc *vdesc; + unsigned int i; + dma_addr_t desc_phys; + + if (!jzchan->desc) { + vdesc = vchan_next_desc(&jzchan->vchan); + if (!vdesc) + return; + + list_del(&vdesc->node); + + jzchan->desc = to_jz4780_dma_desc(vdesc); + jzchan->curr_hwdesc = 0; + + if (jzchan->desc->type == DMA_CYCLIC && vdesc->tx.callback) { + /* + * The DMA controller doesn't support triggering an + * interrupt after processing each descriptor, only + * after processing an entire terminated list of + * descriptors. For a cyclic DMA setup the list of + * descriptors is not terminated so we can never get an + * interrupt. + * + * If the user requested a callback for a cyclic DMA + * setup then we workaround this hardware limitation + * here by degrading to a set of unlinked descriptors + * which we will submit in sequence in response to the + * completion of processing the previous descriptor. + */ + for (i = 0; i < jzchan->desc->count; i++) + jzchan->desc->desc[i].dcm &= ~JZ_DMA_DCM_LINK; + } + } else { + /* + * There is an existing transfer, therefore this must be one + * for which we unlinked the descriptors above. Advance to the + * next one in the list. + */ + jzchan->curr_hwdesc = + (jzchan->curr_hwdesc + 1) % jzchan->desc->count; + } + + /* Use 8-word descriptors. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), JZ_DMA_DCS_DES8); + + /* Write descriptor address and initiate descriptor fetch. */ + desc_phys = jzchan->desc->desc_phys + + (jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc)); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DDA(jzchan->id), desc_phys); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id)); + + /* Enable the channel. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), + JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE); +} + +static void jz4780_dma_issue_pending(struct dma_chan *chan) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&jzchan->vchan.lock, flags); + + if (vchan_issue_pending(&jzchan->vchan) && !jzchan->desc) + jz4780_dma_begin(jzchan); + + spin_unlock_irqrestore(&jzchan->vchan.lock, flags); +} + +static int jz4780_dma_terminate_all(struct jz4780_dma_chan *jzchan) +{ + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&jzchan->vchan.lock, flags); + + /* Clear the DMA status and stop the transfer. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0); + if (jzchan->desc) { + jz4780_dma_desc_free(&jzchan->desc->vdesc); + jzchan->desc = NULL; + } + + vchan_get_all_descriptors(&jzchan->vchan, &head); + + spin_unlock_irqrestore(&jzchan->vchan.lock, flags); + + vchan_dma_desc_free_list(&jzchan->vchan, &head); + return 0; +} + +static int jz4780_dma_slave_config(struct jz4780_dma_chan *jzchan, + const struct dma_slave_config *config) +{ + if ((config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + || (config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)) + return -EINVAL; + + /* Copy the reset of the slave configuration, it is used later. */ + memcpy(&jzchan->config, config, sizeof(jzchan->config)); + + return 0; +} + +static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan, + struct jz4780_dma_desc *desc, unsigned int next_sg) +{ + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + unsigned int residue, count; + unsigned int i; + + residue = 0; + + for (i = next_sg; i < desc->count; i++) + residue += desc->desc[i].dtc << jzchan->transfer_shift; + + if (next_sg != 0) { + count = jz4780_dma_readl(jzdma, + JZ_DMA_REG_DTC(jzchan->id)); + residue += count << jzchan->transfer_shift; + } + + return residue; +} + +static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, txstate); + if ((status == DMA_COMPLETE) || (txstate == NULL)) + return status; + + spin_lock_irqsave(&jzchan->vchan.lock, flags); + + vdesc = vchan_find_desc(&jzchan->vchan, cookie); + if (vdesc) { + /* On the issued list, so hasn't been processed yet */ + txstate->residue = jz4780_dma_desc_residue(jzchan, + to_jz4780_dma_desc(vdesc), 0); + } else if (cookie == jzchan->desc->vdesc.tx.cookie) { + txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc, + (jzchan->curr_hwdesc + 1) % jzchan->desc->count); + } else + txstate->residue = 0; + + if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc + && jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) + status = DMA_ERROR; + + spin_unlock_irqrestore(&jzchan->vchan.lock, flags); + return status; +} + +static void jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma, + struct jz4780_dma_chan *jzchan) +{ + uint32_t dcs; + + spin_lock(&jzchan->vchan.lock); + + dcs = jz4780_dma_readl(jzdma, JZ_DMA_REG_DCS(jzchan->id)); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0); + + if (dcs & JZ_DMA_DCS_AR) { + dev_warn(&jzchan->vchan.chan.dev->device, + "address error (DCS=0x%x)\n", dcs); + } + + if (dcs & JZ_DMA_DCS_HLT) { + dev_warn(&jzchan->vchan.chan.dev->device, + "channel halt (DCS=0x%x)\n", dcs); + } + + if (jzchan->desc) { + jzchan->desc->status = dcs; + + if ((dcs & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) == 0) { + if (jzchan->desc->type == DMA_CYCLIC) { + vchan_cyclic_callback(&jzchan->desc->vdesc); + } else { + vchan_cookie_complete(&jzchan->desc->vdesc); + jzchan->desc = NULL; + } + + jz4780_dma_begin(jzchan); + } + } else { + dev_err(&jzchan->vchan.chan.dev->device, + "channel IRQ with no active transfer\n"); + } + + spin_unlock(&jzchan->vchan.lock); +} + +static irqreturn_t jz4780_dma_irq_handler(int irq, void *data) +{ + struct jz4780_dma_dev *jzdma = data; + uint32_t pending, dmac; + int i; + + pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP); + + for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) { + if (!(pending & (1<chan[i]); + } + + /* Clear halt and address error status of all channels. */ + dmac = jz4780_dma_readl(jzdma, JZ_DMA_REG_DMAC); + dmac &= ~(JZ_DMA_DMAC_HLT | JZ_DMA_DMAC_AR); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, dmac); + + /* Clear interrupt pending status. */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DIRQP, 0); + + return IRQ_HANDLED; +} + +static int jz4780_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + + jzchan->desc_pool = dma_pool_create(dev_name(&chan->dev->device), + chan->device->dev, + JZ_DMA_DESC_BLOCK_SIZE, + PAGE_SIZE, 0); + if (!jzchan->desc_pool) { + dev_err(&chan->dev->device, + "failed to allocate descriptor pool\n"); + return -ENOMEM; + } + + return 0; +} + +static void jz4780_dma_free_chan_resources(struct dma_chan *chan) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + + vchan_free_chan_resources(&jzchan->vchan); + dma_pool_destroy(jzchan->desc_pool); + jzchan->desc_pool = NULL; +} + +static bool jz4780_dma_filter_fn(struct dma_chan *chan, void *param) +{ + struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); + struct jz4780_dma_data *data = param; + + if (data->channel > -1) { + if (data->channel != jzchan->id) + return false; + } else if (jzdma->chan_reserved & BIT(jzchan->id)) { + return false; + } + + jzchan->transfer_type = data->transfer_type; + + return true; +} + +static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct jz4780_dma_dev *jzdma = ofdma->of_dma_data; + dma_cap_mask_t mask = jzdma->dma_device.cap_mask; + struct jz4780_dma_data data; + + if (dma_spec->args_count != 2) + return NULL; + + data.transfer_type = dma_spec->args[0]; + data.channel = dma_spec->args[1]; + + if (data.channel > -1) { + if (data.channel >= JZ_DMA_NR_CHANNELS) { + dev_err(jzdma->dma_device.dev, + "device requested non-existent channel %u\n", + data.channel); + return NULL; + } + + /* Can only select a channel marked as reserved. */ + if (!(jzdma->chan_reserved & BIT(data.channel))) { + dev_err(jzdma->dma_device.dev, + "device requested unreserved channel %u\n", + data.channel); + return NULL; + } + } + + return dma_request_channel(mask, jz4780_dma_filter_fn, &data); +} + +static int jz4780_dma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz4780_dma_dev *jzdma; + struct jz4780_dma_chan *jzchan; + struct dma_device *dd; + struct resource *res; + int i, ret; + + jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL); + if (!jzdma) + return -ENOMEM; + + platform_set_drvdata(pdev, jzdma); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get I/O memory\n"); + return -EINVAL; + } + + jzdma->base = devm_ioremap_resource(dev, res); + if (IS_ERR(jzdma->base)) + return PTR_ERR(jzdma->base); + + jzdma->irq = platform_get_irq(pdev, 0); + if (jzdma->irq < 0) { + dev_err(dev, "failed to get IRQ: %d\n", ret); + return jzdma->irq; + } + + ret = devm_request_irq(dev, jzdma->irq, jz4780_dma_irq_handler, 0, + dev_name(dev), jzdma); + if (ret) { + dev_err(dev, "failed to request IRQ %u!\n", jzdma->irq); + return -EINVAL; + } + + jzdma->clk = devm_clk_get(dev, NULL); + if (IS_ERR(jzdma->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(jzdma->clk); + } + + clk_prepare_enable(jzdma->clk); + + /* Property is optional, if it doesn't exist the value will remain 0. */ + of_property_read_u32_index(dev->of_node, "ingenic,reserved-channels", + 0, &jzdma->chan_reserved); + + dd = &jzdma->dma_device; + + dma_cap_set(DMA_MEMCPY, dd->cap_mask); + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); + + dd->dev = dev; + dd->copy_align = 2; /* 2^2 = 4 byte alignment */ + dd->device_alloc_chan_resources = jz4780_dma_alloc_chan_resources; + dd->device_free_chan_resources = jz4780_dma_free_chan_resources; + dd->device_prep_slave_sg = jz4780_dma_prep_slave_sg; + dd->device_prep_dma_cyclic = jz4780_dma_prep_dma_cyclic; + dd->device_prep_dma_memcpy = jz4780_dma_prep_dma_memcpy; + dd->device_config = jz4780_dma_slave_config; + dd->device_terminate_all = jz4780_dma_terminate_all; + dd->device_tx_status = jz4780_dma_tx_status; + dd->device_issue_pending = jz4780_dma_issue_pending; + dd->src_addr_widths = JZ_DMA_BUSWIDTHS; + dd->dst_addr_widths = JZ_DMA_BUSWIDTHS; + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + + /* + * Enable DMA controller, mark all channels as not programmable. + * Also set the FMSC bit - it increases MSC performance, so it makes + * little sense not to enable it. + */ + jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, + JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC); + jz4780_dma_writel(jzdma, JZ_DMA_REG_DMACP, 0); + + INIT_LIST_HEAD(&dd->channels); + + for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) { + jzchan = &jzdma->chan[i]; + jzchan->id = i; + + vchan_init(&jzchan->vchan, dd); + jzchan->vchan.desc_free = jz4780_dma_desc_free; + } + + ret = dma_async_device_register(dd); + if (ret) { + dev_err(dev, "failed to register device\n"); + goto err_disable_clk; + } + + /* Register with OF DMA helpers. */ + ret = of_dma_controller_register(dev->of_node, jz4780_of_dma_xlate, + jzdma); + if (ret) { + dev_err(dev, "failed to register OF DMA controller\n"); + goto err_unregister_dev; + } + + dev_info(dev, "JZ4780 DMA controller initialised\n"); + return 0; + +err_unregister_dev: + dma_async_device_unregister(dd); + +err_disable_clk: + clk_disable_unprepare(jzdma->clk); + return ret; +} + +static int jz4780_dma_remove(struct platform_device *pdev) +{ + struct jz4780_dma_dev *jzdma = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + devm_free_irq(&pdev->dev, jzdma->irq, jzdma); + dma_async_device_unregister(&jzdma->dma_device); + return 0; +} + +static const struct of_device_id jz4780_dma_dt_match[] = { + { .compatible = "ingenic,jz4780-dma", .data = NULL }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match); + +static struct platform_driver jz4780_dma_driver = { + .probe = jz4780_dma_probe, + .remove = jz4780_dma_remove, + .driver = { + .name = "jz4780-dma", + .of_match_table = of_match_ptr(jz4780_dma_dt_match), + }, +}; + +static int __init jz4780_dma_init(void) +{ + return platform_driver_register(&jz4780_dma_driver); +} +subsys_initcall(jz4780_dma_init); + +static void __exit jz4780_dma_exit(void) +{ + platform_driver_unregister(&jz4780_dma_driver); +} +module_exit(jz4780_dma_exit); + +MODULE_AUTHOR("Alex Smith "); +MODULE_DESCRIPTION("Ingenic JZ4780 DMA controller driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/dma/jz4780-dma.h b/include/dt-bindings/dma/jz4780-dma.h new file mode 100644 index 0000000..df017fd --- /dev/null +++ b/include/dt-bindings/dma/jz4780-dma.h @@ -0,0 +1,49 @@ +#ifndef __DT_BINDINGS_DMA_JZ4780_DMA_H__ +#define __DT_BINDINGS_DMA_JZ4780_DMA_H__ + +/* + * Request type numbers for the JZ4780 DMA controller (written to the DRTn + * register for the channel). + */ +#define JZ4780_DMA_I2S1_TX 0x4 +#define JZ4780_DMA_I2S1_RX 0x5 +#define JZ4780_DMA_I2S0_TX 0x6 +#define JZ4780_DMA_I2S0_RX 0x7 +#define JZ4780_DMA_AUTO 0x8 +#define JZ4780_DMA_SADC_RX 0x9 +#define JZ4780_DMA_UART4_TX 0xc +#define JZ4780_DMA_UART4_RX 0xd +#define JZ4780_DMA_UART3_TX 0xe +#define JZ4780_DMA_UART3_RX 0xf +#define JZ4780_DMA_UART2_TX 0x10 +#define JZ4780_DMA_UART2_RX 0x11 +#define JZ4780_DMA_UART1_TX 0x12 +#define JZ4780_DMA_UART1_RX 0x13 +#define JZ4780_DMA_UART0_TX 0x14 +#define JZ4780_DMA_UART0_RX 0x15 +#define JZ4780_DMA_SSI0_TX 0x16 +#define JZ4780_DMA_SSI0_RX 0x17 +#define JZ4780_DMA_SSI1_TX 0x18 +#define JZ4780_DMA_SSI1_RX 0x19 +#define JZ4780_DMA_MSC0_TX 0x1a +#define JZ4780_DMA_MSC0_RX 0x1b +#define JZ4780_DMA_MSC1_TX 0x1c +#define JZ4780_DMA_MSC1_RX 0x1d +#define JZ4780_DMA_MSC2_TX 0x1e +#define JZ4780_DMA_MSC2_RX 0x1f +#define JZ4780_DMA_PCM0_TX 0x20 +#define JZ4780_DMA_PCM0_RX 0x21 +#define JZ4780_DMA_SMB0_TX 0x24 +#define JZ4780_DMA_SMB0_RX 0x25 +#define JZ4780_DMA_SMB1_TX 0x26 +#define JZ4780_DMA_SMB1_RX 0x27 +#define JZ4780_DMA_SMB2_TX 0x28 +#define JZ4780_DMA_SMB2_RX 0x29 +#define JZ4780_DMA_SMB3_TX 0x2a +#define JZ4780_DMA_SMB3_RX 0x2b +#define JZ4780_DMA_SMB4_TX 0x2c +#define JZ4780_DMA_SMB4_RX 0x2d +#define JZ4780_DMA_DES_TX 0x2e +#define JZ4780_DMA_DES_RX 0x2f + +#endif /* __DT_BINDINGS_DMA_JZ4780_DMA_H__ */ -- cgit v0.10.2 From 8adc53fdc9721ef6f71fbaad07c486abdc4c8f60 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Wed, 18 Mar 2015 16:16:37 +0000 Subject: MAINTAINERS: Add Ingenic JZ4780 DMA driver maintainer entry Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Vinod Koul diff --git a/MAINTAINERS b/MAINTAINERS index ddc5a8c..be98874 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4962,6 +4962,11 @@ W: http://industrypack.sourceforge.net S: Maintained F: drivers/ipack/ +INGENIC JZ4780 DMA Driver +M: Zubair Lutfullah Kakakhel +S: Maintained +F: drivers/dma/dma-jz4780.c + INTEGRITY MEASUREMENT ARCHITECTURE (IMA) M: Mimi Zohar M: Dmitry Kasatkin -- cgit v0.10.2 From de6b641e2d23d116dcf6b6a67e2ee4856e3c3026 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 22 Mar 2015 00:51:08 +0100 Subject: dmaengine: ste_dma40: fix implicit conversion The function d40_prep_sg takes the type enum dma_transfer_direction as second last parameter. However, the memcpy calls pass DMA_NONE which is of type enum dma_data_direction. Fix this by passing the actual transfer direction DMA_MEM_TO_MEM. This does not change the actual code flow since only the transfer direction DMA_MEM_TO_DEV and DMA_DEV_TO_MEM are actually used in the function d40_prep_sg. Signed-off-by: Stefan Agner Signed-off-by: Vinod Koul diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 68aca33..75faaea 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2514,7 +2514,8 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan, sg_dma_len(&dst_sg) = size; sg_dma_len(&src_sg) = size; - return d40_prep_sg(chan, &src_sg, &dst_sg, 1, DMA_NONE, dma_flags); + return d40_prep_sg(chan, &src_sg, &dst_sg, 1, + DMA_MEM_TO_MEM, dma_flags); } static struct dma_async_tx_descriptor * @@ -2526,7 +2527,8 @@ d40_prep_memcpy_sg(struct dma_chan *chan, if (dst_nents != src_nents) return NULL; - return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags); + return d40_prep_sg(chan, src_sg, dst_sg, src_nents, + DMA_MEM_TO_MEM, dma_flags); } static struct dma_async_tx_descriptor * -- cgit v0.10.2 From ab7add30ce63a7147f045f00c65a44c0f14ad6b2 Mon Sep 17 00:00:00 2001 From: Petr Kulhavy Date: Mon, 23 Mar 2015 21:35:01 +0100 Subject: dmaengine: edma: fixed wrongly initialized data parameter to the edma callback The "data" parameter passed indirectly to the edma_callback() should be edma_chan and not the dma_chan. This bug was so far harmless since the offset of struct dma_chan within struct edma_chan is 0. However as soon as someone changes struct edma_chan this would cause troubles. Signed-off-by: Petr Kulhavy Acked-by: Peter Ujfalusi Signed-off-by: Vinod Koul diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 276157f..0ea813d 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -805,7 +805,7 @@ static int edma_alloc_chan_resources(struct dma_chan *chan) LIST_HEAD(descs); a_ch_num = edma_alloc_channel(echan->ch_num, edma_callback, - chan, EVENTQ_DEFAULT); + echan, EVENTQ_DEFAULT); if (a_ch_num < 0) { ret = -ENODEV; -- cgit v0.10.2 From a7267831565030ab94dab76eb1afe5d4b63a1009 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Apr 2015 15:22:44 +0900 Subject: dmaengine: renesas,usb-dmac: Add device tree bindings documentation Document the device tree bindings for the Renesas USB DMA Controller (USB-DMAC). Signed-off-by: Yoshihiro Shimoda Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt new file mode 100644 index 0000000..040f365 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt @@ -0,0 +1,37 @@ +* Renesas USB DMA Controller Device Tree bindings + +Required Properties: +- compatible: must contain "renesas,usb-dmac" +- reg: base address and length of the registers block for the DMAC +- interrupts: interrupt specifiers for the DMAC, one for each entry in + interrupt-names. +- interrupt-names: one entry per channel, named "ch%u", where %u is the + channel number ranging from zero to the number of channels minus one. +- clocks: a list of phandle + clock-specifier pairs. +- #dma-cells: must be <1>, the cell specifies the channel number of the DMAC + port connected to the DMA client. +- dma-channels: number of DMA channels + +Example: R8A7790 (R-Car H2) USB-DMACs + + usb_dmac0: dma-controller@e65a0000 { + compatible = "renesas,usb-dmac"; + reg = <0 0xe65a0000 0 0x100>; + interrupts = <0 109 IRQ_TYPE_LEVEL_HIGH + 0 109 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "ch0", "ch1"; + clocks = <&mstp3_clks R8A7790_CLK_USBDMAC0>; + #dma-cells = <1>; + dma-channels = <2>; + }; + + usb_dmac1: dma-controller@e65b0000 { + compatible = "renesas,usb-dmac"; + reg = <0 0xe65b0000 0 0x100>; + interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH + 0 110 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "ch0", "ch1"; + clocks = <&mstp3_clks R8A7790_CLK_USBDMAC1>; + #dma-cells = <1>; + dma-channels = <2>; + }; -- cgit v0.10.2 From 0c1c8ff32fa29e425b4938934c21afdb81104431 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Apr 2015 15:22:45 +0900 Subject: dmaengine: usb-dmac: Add Renesas USB DMA Controller (USB-DMAC) driver This DMAC is Renesas USB high-speed module DMA controller that supports slave transfer. This USB-DMAC has similar register sets with R-Car Gen2 DMAC, but the USB-DMAC has specific registers to control the USB transactions. If this code is added into the rcar-dmac driver, it will become unreadable. So, this driver is independent from the rcar-dmac. And, this USB-DMAC uses virt-dma infrastructure. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Vinod Koul diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index 8190ad2..f6ca002 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -64,3 +64,12 @@ config RCAR_DMAC help This driver supports the general purpose DMA controller found in the Renesas R-Car second generation SoCs. + +config RENESAS_USB_DMAC + tristate "Renesas USB-DMA Controller" + depends on ARCH_SHMOBILE || COMPILE_TEST + select RENESAS_DMA + select DMA_VIRTUAL_CHANNELS + help + This driver supports the USB-DMA controller found in the Renesas + SoCs. diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index 2852f9d..221ab19 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_SUDMAC) += sudmac.o obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o obj-$(CONFIG_RCAR_DMAC) += rcar-dmac.o +obj-$(CONFIG_RENESAS_USB_DMAC) += usb-dmac.o diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c new file mode 100644 index 0000000..d5dad98 --- /dev/null +++ b/drivers/dma/sh/usb-dmac.c @@ -0,0 +1,910 @@ +/* + * Renesas USB DMA Controller Driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * based on rcar-dmac.c + * Copyright (C) 2014 Renesas Electronics Inc. + * Author: Laurent Pinchart + * + * This is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" +#include "../virt-dma.h" + +/* + * struct usb_dmac_sg - Descriptor for a hardware transfer + * @mem_addr: memory address + * @size: transfer size in bytes + */ +struct usb_dmac_sg { + dma_addr_t mem_addr; + u32 size; +}; + +/* + * struct usb_dmac_desc - USB DMA Transfer Descriptor + * @vd: base virtual channel DMA transaction descriptor + * @direction: direction of the DMA transfer + * @sg_allocated_len: length of allocated sg + * @sg_len: length of sg + * @sg_index: index of sg + * @residue: residue after the DMAC completed a transfer + * @node: node for desc_got and desc_freed + * @done_cookie: cookie after the DMAC completed a transfer + * @sg: information for the transfer + */ +struct usb_dmac_desc { + struct virt_dma_desc vd; + enum dma_transfer_direction direction; + unsigned int sg_allocated_len; + unsigned int sg_len; + unsigned int sg_index; + u32 residue; + struct list_head node; + dma_cookie_t done_cookie; + struct usb_dmac_sg sg[0]; +}; + +#define to_usb_dmac_desc(vd) container_of(vd, struct usb_dmac_desc, vd) + +/* + * struct usb_dmac_chan - USB DMA Controller Channel + * @vc: base virtual DMA channel object + * @iomem: channel I/O memory base + * @index: index of this channel in the controller + * @irq: irq number of this channel + * @desc: the current descriptor + * @descs_allocated: number of descriptors allocated + * @desc_got: got descriptors + * @desc_freed: freed descriptors after the DMAC completed a transfer + */ +struct usb_dmac_chan { + struct virt_dma_chan vc; + void __iomem *iomem; + unsigned int index; + int irq; + struct usb_dmac_desc *desc; + int descs_allocated; + struct list_head desc_got; + struct list_head desc_freed; +}; + +#define to_usb_dmac_chan(c) container_of(c, struct usb_dmac_chan, vc.chan) + +/* + * struct usb_dmac - USB DMA Controller + * @engine: base DMA engine object + * @dev: the hardware device + * @iomem: remapped I/O memory base + * @n_channels: number of available channels + * @channels: array of DMAC channels + */ +struct usb_dmac { + struct dma_device engine; + struct device *dev; + void __iomem *iomem; + + unsigned int n_channels; + struct usb_dmac_chan *channels; +}; + +#define to_usb_dmac(d) container_of(d, struct usb_dmac, engine) + +/* ----------------------------------------------------------------------------- + * Registers + */ + +#define USB_DMAC_CHAN_OFFSET(i) (0x20 + 0x20 * (i)) + +#define USB_DMASWR 0x0008 +#define USB_DMASWR_SWR (1 << 0) +#define USB_DMAOR 0x0060 +#define USB_DMAOR_AE (1 << 2) +#define USB_DMAOR_DME (1 << 0) + +#define USB_DMASAR 0x0000 +#define USB_DMADAR 0x0004 +#define USB_DMATCR 0x0008 +#define USB_DMATCR_MASK 0x00ffffff +#define USB_DMACHCR 0x0014 +#define USB_DMACHCR_FTE (1 << 24) +#define USB_DMACHCR_NULLE (1 << 16) +#define USB_DMACHCR_NULL (1 << 12) +#define USB_DMACHCR_TS_8B ((0 << 7) | (0 << 6)) +#define USB_DMACHCR_TS_16B ((0 << 7) | (1 << 6)) +#define USB_DMACHCR_TS_32B ((1 << 7) | (0 << 6)) +#define USB_DMACHCR_IE (1 << 5) +#define USB_DMACHCR_SP (1 << 2) +#define USB_DMACHCR_TE (1 << 1) +#define USB_DMACHCR_DE (1 << 0) +#define USB_DMATEND 0x0018 + +/* Hardcode the xfer_shift to 5 (32bytes) */ +#define USB_DMAC_XFER_SHIFT 5 +#define USB_DMAC_XFER_SIZE (1 << USB_DMAC_XFER_SHIFT) +#define USB_DMAC_CHCR_TS USB_DMACHCR_TS_32B +#define USB_DMAC_SLAVE_BUSWIDTH DMA_SLAVE_BUSWIDTH_32_BYTES + +/* for descriptors */ +#define USB_DMAC_INITIAL_NR_DESC 16 +#define USB_DMAC_INITIAL_NR_SG 8 + +/* ----------------------------------------------------------------------------- + * Device access + */ + +static void usb_dmac_write(struct usb_dmac *dmac, u32 reg, u32 data) +{ + writel(data, dmac->iomem + reg); +} + +static u32 usb_dmac_read(struct usb_dmac *dmac, u32 reg) +{ + return readl(dmac->iomem + reg); +} + +static u32 usb_dmac_chan_read(struct usb_dmac_chan *chan, u32 reg) +{ + return readl(chan->iomem + reg); +} + +static void usb_dmac_chan_write(struct usb_dmac_chan *chan, u32 reg, u32 data) +{ + writel(data, chan->iomem + reg); +} + +/* ----------------------------------------------------------------------------- + * Initialization and configuration + */ + +static bool usb_dmac_chan_is_busy(struct usb_dmac_chan *chan) +{ + u32 chcr = usb_dmac_chan_read(chan, USB_DMACHCR); + + return (chcr & (USB_DMACHCR_DE | USB_DMACHCR_TE)) == USB_DMACHCR_DE; +} + +static u32 usb_dmac_calc_tend(u32 size) +{ + /* + * Please refer to the Figure "Example of Final Transaction Valid + * Data Transfer Enable (EDTEN) Setting" in the data sheet. + */ + return 0xffffffff << (32 - (size % USB_DMAC_XFER_SIZE ? : + USB_DMAC_XFER_SIZE)); +} + +/* This function is already held by vc.lock */ +static void usb_dmac_chan_start_sg(struct usb_dmac_chan *chan, + unsigned int index) +{ + struct usb_dmac_desc *desc = chan->desc; + struct usb_dmac_sg *sg = desc->sg + index; + dma_addr_t src_addr = 0, dst_addr = 0; + + WARN_ON_ONCE(usb_dmac_chan_is_busy(chan)); + + if (desc->direction == DMA_DEV_TO_MEM) + dst_addr = sg->mem_addr; + else + src_addr = sg->mem_addr; + + dev_dbg(chan->vc.chan.device->dev, + "chan%u: queue sg %p: %u@%pad -> %pad\n", + chan->index, sg, sg->size, &src_addr, &dst_addr); + + usb_dmac_chan_write(chan, USB_DMASAR, src_addr & 0xffffffff); + usb_dmac_chan_write(chan, USB_DMADAR, dst_addr & 0xffffffff); + usb_dmac_chan_write(chan, USB_DMATCR, + DIV_ROUND_UP(sg->size, USB_DMAC_XFER_SIZE)); + usb_dmac_chan_write(chan, USB_DMATEND, usb_dmac_calc_tend(sg->size)); + + usb_dmac_chan_write(chan, USB_DMACHCR, USB_DMAC_CHCR_TS | + USB_DMACHCR_NULLE | USB_DMACHCR_IE | USB_DMACHCR_DE); +} + +/* This function is already held by vc.lock */ +static void usb_dmac_chan_start_desc(struct usb_dmac_chan *chan) +{ + struct virt_dma_desc *vd; + + vd = vchan_next_desc(&chan->vc); + if (!vd) { + chan->desc = NULL; + return; + } + + /* + * Remove this request from vc->desc_issued. Otherwise, this driver + * will get the previous value from vchan_next_desc() after a transfer + * was completed. + */ + list_del(&vd->node); + + chan->desc = to_usb_dmac_desc(vd); + chan->desc->sg_index = 0; + usb_dmac_chan_start_sg(chan, 0); +} + +static int usb_dmac_init(struct usb_dmac *dmac) +{ + u16 dmaor; + + /* Clear all channels and enable the DMAC globally. */ + usb_dmac_write(dmac, USB_DMAOR, USB_DMAOR_DME); + + dmaor = usb_dmac_read(dmac, USB_DMAOR); + if ((dmaor & (USB_DMAOR_AE | USB_DMAOR_DME)) != USB_DMAOR_DME) { + dev_warn(dmac->dev, "DMAOR initialization failed.\n"); + return -EIO; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Descriptors allocation and free + */ +static int usb_dmac_desc_alloc(struct usb_dmac_chan *chan, unsigned int sg_len, + gfp_t gfp) +{ + struct usb_dmac_desc *desc; + unsigned long flags; + + desc = kzalloc(sizeof(*desc) + sg_len * sizeof(desc->sg[0]), gfp); + if (!desc) + return -ENOMEM; + + desc->sg_allocated_len = sg_len; + INIT_LIST_HEAD(&desc->node); + + spin_lock_irqsave(&chan->vc.lock, flags); + list_add_tail(&desc->node, &chan->desc_freed); + spin_unlock_irqrestore(&chan->vc.lock, flags); + + return 0; +} + +static void usb_dmac_desc_free(struct usb_dmac_chan *chan) +{ + struct usb_dmac_desc *desc; + LIST_HEAD(list); + + list_splice_init(&chan->desc_freed, &list); + list_splice_init(&chan->desc_got, &list); + + list_for_each_entry(desc, &list, node) { + list_del(&desc->node); + kfree(desc); + } + chan->descs_allocated = 0; +} + +static struct usb_dmac_desc *usb_dmac_desc_get(struct usb_dmac_chan *chan, + unsigned int sg_len, gfp_t gfp) +{ + struct usb_dmac_desc *desc = NULL; + unsigned long flags; + + /* Get a freed descritpor */ + spin_lock_irqsave(&chan->vc.lock, flags); + list_for_each_entry(desc, &chan->desc_freed, node) { + if (sg_len <= desc->sg_allocated_len) { + list_move_tail(&desc->node, &chan->desc_got); + spin_unlock_irqrestore(&chan->vc.lock, flags); + return desc; + } + } + spin_unlock_irqrestore(&chan->vc.lock, flags); + + /* Allocate a new descriptor */ + if (!usb_dmac_desc_alloc(chan, sg_len, gfp)) { + /* If allocated the desc, it was added to tail of the list */ + spin_lock_irqsave(&chan->vc.lock, flags); + desc = list_last_entry(&chan->desc_freed, struct usb_dmac_desc, + node); + list_move_tail(&desc->node, &chan->desc_got); + spin_unlock_irqrestore(&chan->vc.lock, flags); + return desc; + } + + return NULL; +} + +static void usb_dmac_desc_put(struct usb_dmac_chan *chan, + struct usb_dmac_desc *desc) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->vc.lock, flags); + list_move_tail(&desc->node, &chan->desc_freed); + spin_unlock_irqrestore(&chan->vc.lock, flags); +} + +/* ----------------------------------------------------------------------------- + * Stop and reset + */ + +static void usb_dmac_soft_reset(struct usb_dmac_chan *uchan) +{ + struct dma_chan *chan = &uchan->vc.chan; + struct usb_dmac *dmac = to_usb_dmac(chan->device); + int i; + + /* Don't issue soft reset if any one of channels is busy */ + for (i = 0; i < dmac->n_channels; ++i) { + if (usb_dmac_chan_is_busy(uchan)) + return; + } + + usb_dmac_write(dmac, USB_DMAOR, 0); + usb_dmac_write(dmac, USB_DMASWR, USB_DMASWR_SWR); + udelay(100); + usb_dmac_write(dmac, USB_DMASWR, 0); + usb_dmac_write(dmac, USB_DMAOR, 1); +} + +static void usb_dmac_chan_halt(struct usb_dmac_chan *chan) +{ + u32 chcr = usb_dmac_chan_read(chan, USB_DMACHCR); + + chcr &= ~(USB_DMACHCR_IE | USB_DMACHCR_TE | USB_DMACHCR_DE); + usb_dmac_chan_write(chan, USB_DMACHCR, chcr); + + usb_dmac_soft_reset(chan); +} + +static void usb_dmac_stop(struct usb_dmac *dmac) +{ + usb_dmac_write(dmac, USB_DMAOR, 0); +} + +/* ----------------------------------------------------------------------------- + * DMA engine operations + */ + +static int usb_dmac_alloc_chan_resources(struct dma_chan *chan) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + int ret; + + while (uchan->descs_allocated < USB_DMAC_INITIAL_NR_DESC) { + ret = usb_dmac_desc_alloc(uchan, USB_DMAC_INITIAL_NR_SG, + GFP_KERNEL); + if (ret < 0) { + usb_dmac_desc_free(uchan); + return ret; + } + uchan->descs_allocated++; + } + + return pm_runtime_get_sync(chan->device->dev); +} + +static void usb_dmac_free_chan_resources(struct dma_chan *chan) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + unsigned long flags; + + /* Protect against ISR */ + spin_lock_irqsave(&uchan->vc.lock, flags); + usb_dmac_chan_halt(uchan); + spin_unlock_irqrestore(&uchan->vc.lock, flags); + + usb_dmac_desc_free(uchan); + vchan_free_chan_resources(&uchan->vc); + + pm_runtime_put(chan->device->dev); +} + +static struct dma_async_tx_descriptor * +usb_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long dma_flags, void *context) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + struct usb_dmac_desc *desc; + struct scatterlist *sg; + int i; + + if (!sg_len) { + dev_warn(chan->device->dev, + "%s: bad parameter: len=%d\n", __func__, sg_len); + return NULL; + } + + desc = usb_dmac_desc_get(uchan, sg_len, GFP_NOWAIT); + if (!desc) + return NULL; + + desc->direction = dir; + desc->sg_len = sg_len; + for_each_sg(sgl, sg, sg_len, i) { + desc->sg[i].mem_addr = sg_dma_address(sg); + desc->sg[i].size = sg_dma_len(sg); + } + + return vchan_tx_prep(&uchan->vc, &desc->vd, dma_flags); +} + +static int usb_dmac_chan_terminate_all(struct dma_chan *chan) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + struct usb_dmac_desc *desc; + unsigned long flags; + LIST_HEAD(head); + LIST_HEAD(list); + + spin_lock_irqsave(&uchan->vc.lock, flags); + usb_dmac_chan_halt(uchan); + vchan_get_all_descriptors(&uchan->vc, &head); + if (uchan->desc) + uchan->desc = NULL; + list_splice_init(&uchan->desc_got, &list); + list_for_each_entry(desc, &list, node) + list_move_tail(&desc->node, &uchan->desc_freed); + spin_unlock_irqrestore(&uchan->vc.lock, flags); + vchan_dma_desc_free_list(&uchan->vc, &head); + + return 0; +} + +static unsigned int usb_dmac_get_current_residue(struct usb_dmac_chan *chan, + struct usb_dmac_desc *desc, + int sg_index) +{ + struct usb_dmac_sg *sg = desc->sg + sg_index; + u32 mem_addr = sg->mem_addr & 0xffffffff; + unsigned int residue = sg->size; + + /* + * We cannot use USB_DMATCR to calculate residue because USB_DMATCR + * has unsuited value to calculate. + */ + if (desc->direction == DMA_DEV_TO_MEM) + residue -= usb_dmac_chan_read(chan, USB_DMADAR) - mem_addr; + else + residue -= usb_dmac_chan_read(chan, USB_DMASAR) - mem_addr; + + return residue; +} + +static u32 usb_dmac_chan_get_residue_if_complete(struct usb_dmac_chan *chan, + dma_cookie_t cookie) +{ + struct usb_dmac_desc *desc; + u32 residue = 0; + + list_for_each_entry_reverse(desc, &chan->desc_freed, node) { + if (desc->done_cookie == cookie) { + residue = desc->residue; + break; + } + } + + return residue; +} + +static u32 usb_dmac_chan_get_residue(struct usb_dmac_chan *chan, + dma_cookie_t cookie) +{ + u32 residue = 0; + struct virt_dma_desc *vd; + struct usb_dmac_desc *desc = chan->desc; + int i; + + if (!desc) { + vd = vchan_find_desc(&chan->vc, cookie); + if (!vd) + return 0; + desc = to_usb_dmac_desc(vd); + } + + /* Compute the size of all usb_dmac_sg still to be transferred */ + for (i = desc->sg_index + 1; i < desc->sg_len; i++) + residue += desc->sg[i].size; + + /* Add the residue for the current sg */ + residue += usb_dmac_get_current_residue(chan, desc, desc->sg_index); + + return residue; +} + +static enum dma_status usb_dmac_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + enum dma_status status; + unsigned int residue = 0; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, txstate); + /* a client driver will get residue after DMA_COMPLETE */ + if (!txstate) + return status; + + spin_lock_irqsave(&uchan->vc.lock, flags); + if (status == DMA_COMPLETE) + residue = usb_dmac_chan_get_residue_if_complete(uchan, cookie); + else + residue = usb_dmac_chan_get_residue(uchan, cookie); + spin_unlock_irqrestore(&uchan->vc.lock, flags); + + dma_set_residue(txstate, residue); + + return status; +} + +static void usb_dmac_issue_pending(struct dma_chan *chan) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&uchan->vc.lock, flags); + if (vchan_issue_pending(&uchan->vc) && !uchan->desc) + usb_dmac_chan_start_desc(uchan); + spin_unlock_irqrestore(&uchan->vc.lock, flags); +} + +static void usb_dmac_virt_desc_free(struct virt_dma_desc *vd) +{ + struct usb_dmac_desc *desc = to_usb_dmac_desc(vd); + struct usb_dmac_chan *chan = to_usb_dmac_chan(vd->tx.chan); + + usb_dmac_desc_put(chan, desc); +} + +/* ----------------------------------------------------------------------------- + * IRQ handling + */ + +static void usb_dmac_isr_transfer_end(struct usb_dmac_chan *chan) +{ + struct usb_dmac_desc *desc = chan->desc; + + BUG_ON(!desc); + + if (++desc->sg_index < desc->sg_len) { + usb_dmac_chan_start_sg(chan, desc->sg_index); + } else { + desc->residue = usb_dmac_get_current_residue(chan, desc, + desc->sg_index - 1); + desc->done_cookie = desc->vd.tx.cookie; + vchan_cookie_complete(&desc->vd); + + /* Restart the next transfer if this driver has a next desc */ + usb_dmac_chan_start_desc(chan); + } +} + +static irqreturn_t usb_dmac_isr_channel(int irq, void *dev) +{ + struct usb_dmac_chan *chan = dev; + irqreturn_t ret = IRQ_NONE; + u32 mask = USB_DMACHCR_TE; + u32 check_bits = USB_DMACHCR_TE | USB_DMACHCR_SP; + u32 chcr; + + spin_lock(&chan->vc.lock); + + chcr = usb_dmac_chan_read(chan, USB_DMACHCR); + if (chcr & check_bits) + mask |= USB_DMACHCR_DE | check_bits; + if (chcr & USB_DMACHCR_NULL) { + /* An interruption of TE will happen after we set FTE */ + mask |= USB_DMACHCR_NULL; + chcr |= USB_DMACHCR_FTE; + ret |= IRQ_HANDLED; + } + usb_dmac_chan_write(chan, USB_DMACHCR, chcr & ~mask); + + if (chcr & check_bits) { + usb_dmac_isr_transfer_end(chan); + ret |= IRQ_HANDLED; + } + + spin_unlock(&chan->vc.lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * OF xlate and channel filter + */ + +static bool usb_dmac_chan_filter(struct dma_chan *chan, void *arg) +{ + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan); + struct of_phandle_args *dma_spec = arg; + + if (dma_spec->np != chan->device->dev->of_node) + return false; + + /* USB-DMAC should be used with fixed usb controller's FIFO */ + if (uchan->index != dma_spec->args[0]) + return false; + + return true; +} + +static struct dma_chan *usb_dmac_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct usb_dmac_chan *uchan; + struct dma_chan *chan; + dma_cap_mask_t mask; + + if (dma_spec->args_count != 1) + return NULL; + + /* Only slave DMA channels can be allocated via DT */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, usb_dmac_chan_filter, dma_spec); + if (!chan) + return NULL; + + uchan = to_usb_dmac_chan(chan); + + return chan; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int usb_dmac_runtime_suspend(struct device *dev) +{ + struct usb_dmac *dmac = dev_get_drvdata(dev); + int i; + + for (i = 0; i < dmac->n_channels; ++i) + usb_dmac_chan_halt(&dmac->channels[i]); + + return 0; +} + +static int usb_dmac_runtime_resume(struct device *dev) +{ + struct usb_dmac *dmac = dev_get_drvdata(dev); + + return usb_dmac_init(dmac); +} + +static const struct dev_pm_ops usb_dmac_pm = { + SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume, + NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe and remove + */ + +static int usb_dmac_chan_probe(struct usb_dmac *dmac, + struct usb_dmac_chan *uchan, + unsigned int index) +{ + struct platform_device *pdev = to_platform_device(dmac->dev); + char pdev_irqname[5]; + char *irqname; + int ret; + + uchan->index = index; + uchan->iomem = dmac->iomem + USB_DMAC_CHAN_OFFSET(index); + + /* Request the channel interrupt. */ + sprintf(pdev_irqname, "ch%u", index); + uchan->irq = platform_get_irq_byname(pdev, pdev_irqname); + if (uchan->irq < 0) { + dev_err(dmac->dev, "no IRQ specified for channel %u\n", index); + return -ENODEV; + } + + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", + dev_name(dmac->dev), index); + if (!irqname) + return -ENOMEM; + + ret = devm_request_irq(dmac->dev, uchan->irq, usb_dmac_isr_channel, + IRQF_SHARED, irqname, uchan); + if (ret) { + dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", + uchan->irq, ret); + return ret; + } + + uchan->vc.desc_free = usb_dmac_virt_desc_free; + vchan_init(&uchan->vc, &dmac->engine); + INIT_LIST_HEAD(&uchan->desc_freed); + INIT_LIST_HEAD(&uchan->desc_got); + + return 0; +} + +static int usb_dmac_parse_of(struct device *dev, struct usb_dmac *dmac) +{ + struct device_node *np = dev->of_node; + int ret; + + ret = of_property_read_u32(np, "dma-channels", &dmac->n_channels); + if (ret < 0) { + dev_err(dev, "unable to read dma-channels property\n"); + return ret; + } + + if (dmac->n_channels <= 0 || dmac->n_channels >= 100) { + dev_err(dev, "invalid number of channels %u\n", + dmac->n_channels); + return -EINVAL; + } + + return 0; +} + +static int usb_dmac_probe(struct platform_device *pdev) +{ + const enum dma_slave_buswidth widths = USB_DMAC_SLAVE_BUSWIDTH; + struct dma_device *engine; + struct usb_dmac *dmac; + struct resource *mem; + unsigned int i; + int ret; + + dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); + if (!dmac) + return -ENOMEM; + + dmac->dev = &pdev->dev; + platform_set_drvdata(pdev, dmac); + + ret = usb_dmac_parse_of(&pdev->dev, dmac); + if (ret < 0) + return ret; + + dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels, + sizeof(*dmac->channels), GFP_KERNEL); + if (!dmac->channels) + return -ENOMEM; + + /* Request resources. */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmac->iomem = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dmac->iomem)) + return PTR_ERR(dmac->iomem); + + /* Enable runtime PM and initialize the device. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "runtime PM get sync failed (%d)\n", ret); + return ret; + } + + ret = usb_dmac_init(dmac); + pm_runtime_put(&pdev->dev); + + if (ret) { + dev_err(&pdev->dev, "failed to reset device\n"); + goto error; + } + + /* Initialize the channels. */ + INIT_LIST_HEAD(&dmac->engine.channels); + + for (i = 0; i < dmac->n_channels; ++i) { + ret = usb_dmac_chan_probe(dmac, &dmac->channels[i], i); + if (ret < 0) + goto error; + } + + /* Register the DMAC as a DMA provider for DT. */ + ret = of_dma_controller_register(pdev->dev.of_node, usb_dmac_of_xlate, + NULL); + if (ret < 0) + goto error; + + /* + * Register the DMA engine device. + * + * Default transfer size of 32 bytes requires 32-byte alignment. + */ + engine = &dmac->engine; + dma_cap_set(DMA_SLAVE, engine->cap_mask); + + engine->dev = &pdev->dev; + + engine->src_addr_widths = widths; + engine->dst_addr_widths = widths; + engine->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); + engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + engine->device_alloc_chan_resources = usb_dmac_alloc_chan_resources; + engine->device_free_chan_resources = usb_dmac_free_chan_resources; + engine->device_prep_slave_sg = usb_dmac_prep_slave_sg; + engine->device_terminate_all = usb_dmac_chan_terminate_all; + engine->device_tx_status = usb_dmac_tx_status; + engine->device_issue_pending = usb_dmac_issue_pending; + + ret = dma_async_device_register(engine); + if (ret < 0) + goto error; + + return 0; + +error: + of_dma_controller_free(pdev->dev.of_node); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static void usb_dmac_chan_remove(struct usb_dmac *dmac, + struct usb_dmac_chan *uchan) +{ + usb_dmac_chan_halt(uchan); + devm_free_irq(dmac->dev, uchan->irq, uchan); +} + +static int usb_dmac_remove(struct platform_device *pdev) +{ + struct usb_dmac *dmac = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < dmac->n_channels; ++i) + usb_dmac_chan_remove(dmac, &dmac->channels[i]); + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&dmac->engine); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static void usb_dmac_shutdown(struct platform_device *pdev) +{ + struct usb_dmac *dmac = platform_get_drvdata(pdev); + + usb_dmac_stop(dmac); +} + +static const struct of_device_id usb_dmac_of_ids[] = { + { .compatible = "renesas,usb-dmac", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, usb_dmac_of_ids); + +static struct platform_driver usb_dmac_driver = { + .driver = { + .pm = &usb_dmac_pm, + .name = "usb-dmac", + .of_match_table = usb_dmac_of_ids, + }, + .probe = usb_dmac_probe, + .remove = usb_dmac_remove, + .shutdown = usb_dmac_shutdown, +}; + +module_platform_driver(usb_dmac_driver); + +MODULE_DESCRIPTION("Renesas USB DMA Controller Driver"); +MODULE_AUTHOR("Yoshihiro Shimoda "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 9f2fd0dfa594d857fbdaeda523ff7a46f16567f5 Mon Sep 17 00:00:00 2001 From: Rameshwar Prasad Sahu Date: Wed, 18 Mar 2015 19:17:34 +0530 Subject: dmaengine: Add support for APM X-Gene SoC DMA engine driver This patch implements the APM X-Gene SoC DMA engine driver. The APM X-Gene SoC DMA engine consists of 4 DMA channels for performing DMA operations. These DMA operations include memory copy, scatter-gather memory copy, raid5 xor, and raid6 p+q offloading. Signed-off-by: Rameshwar Prasad Sahu Signed-off-by: Loc Ho Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index ce09734..4be766f 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -435,6 +435,14 @@ config IMG_MDC_DMA help Enable support for the IMG multi-threaded DMA controller (MDC). +config XGENE_DMA + tristate "APM X-Gene DMA support" + select DMA_ENGINE + select DMA_ENGINE_RAID + select ASYNC_TX_ENABLE_CHANNEL_SWITCH + help + Enable support for the APM X-Gene SoC DMA engine. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index af239e7..1dab9ef 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o +obj-$(CONFIG_XGENE_DMA) += xgene-dma.o diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c new file mode 100755 index 0000000..2383528 --- /dev/null +++ b/drivers/dma/xgene-dma.c @@ -0,0 +1,2090 @@ +/* + * Applied Micro X-Gene SoC DMA engine Driver + * + * Copyright (c) 2015, Applied Micro Circuits Corporation + * Authors: Rameshwar Prasad Sahu + * Loc Ho + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * NOTE: PM support is currently not available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" + +/* X-Gene DMA ring csr registers and bit definations */ +#define XGENE_DMA_RING_CONFIG 0x04 +#define XGENE_DMA_RING_ENABLE BIT(31) +#define XGENE_DMA_RING_ID 0x08 +#define XGENE_DMA_RING_ID_SETUP(v) ((v) | BIT(31)) +#define XGENE_DMA_RING_ID_BUF 0x0C +#define XGENE_DMA_RING_ID_BUF_SETUP(v) (((v) << 9) | BIT(21)) +#define XGENE_DMA_RING_THRESLD0_SET1 0x30 +#define XGENE_DMA_RING_THRESLD0_SET1_VAL 0X64 +#define XGENE_DMA_RING_THRESLD1_SET1 0x34 +#define XGENE_DMA_RING_THRESLD1_SET1_VAL 0xC8 +#define XGENE_DMA_RING_HYSTERESIS 0x68 +#define XGENE_DMA_RING_HYSTERESIS_VAL 0xFFFFFFFF +#define XGENE_DMA_RING_STATE 0x6C +#define XGENE_DMA_RING_STATE_WR_BASE 0x70 +#define XGENE_DMA_RING_NE_INT_MODE 0x017C +#define XGENE_DMA_RING_NE_INT_MODE_SET(m, v) \ + ((m) = ((m) & ~BIT(31 - (v))) | BIT(31 - (v))) +#define XGENE_DMA_RING_NE_INT_MODE_RESET(m, v) \ + ((m) &= (~BIT(31 - (v)))) +#define XGENE_DMA_RING_CLKEN 0xC208 +#define XGENE_DMA_RING_SRST 0xC200 +#define XGENE_DMA_RING_MEM_RAM_SHUTDOWN 0xD070 +#define XGENE_DMA_RING_BLK_MEM_RDY 0xD074 +#define XGENE_DMA_RING_BLK_MEM_RDY_VAL 0xFFFFFFFF +#define XGENE_DMA_RING_DESC_CNT(v) (((v) & 0x0001FFFE) >> 1) +#define XGENE_DMA_RING_ID_GET(owner, num) (((owner) << 6) | (num)) +#define XGENE_DMA_RING_DST_ID(v) ((1 << 10) | (v)) +#define XGENE_DMA_RING_CMD_OFFSET 0x2C +#define XGENE_DMA_RING_CMD_BASE_OFFSET(v) ((v) << 6) +#define XGENE_DMA_RING_COHERENT_SET(m) \ + (((u32 *)(m))[2] |= BIT(4)) +#define XGENE_DMA_RING_ADDRL_SET(m, v) \ + (((u32 *)(m))[2] |= (((v) >> 8) << 5)) +#define XGENE_DMA_RING_ADDRH_SET(m, v) \ + (((u32 *)(m))[3] |= ((v) >> 35)) +#define XGENE_DMA_RING_ACCEPTLERR_SET(m) \ + (((u32 *)(m))[3] |= BIT(19)) +#define XGENE_DMA_RING_SIZE_SET(m, v) \ + (((u32 *)(m))[3] |= ((v) << 23)) +#define XGENE_DMA_RING_RECOMBBUF_SET(m) \ + (((u32 *)(m))[3] |= BIT(27)) +#define XGENE_DMA_RING_RECOMTIMEOUTL_SET(m) \ + (((u32 *)(m))[3] |= (0x7 << 28)) +#define XGENE_DMA_RING_RECOMTIMEOUTH_SET(m) \ + (((u32 *)(m))[4] |= 0x3) +#define XGENE_DMA_RING_SELTHRSH_SET(m) \ + (((u32 *)(m))[4] |= BIT(3)) +#define XGENE_DMA_RING_TYPE_SET(m, v) \ + (((u32 *)(m))[4] |= ((v) << 19)) + +/* X-Gene DMA device csr registers and bit definitions */ +#define XGENE_DMA_IPBRR 0x0 +#define XGENE_DMA_DEV_ID_RD(v) ((v) & 0x00000FFF) +#define XGENE_DMA_BUS_ID_RD(v) (((v) >> 12) & 3) +#define XGENE_DMA_REV_NO_RD(v) (((v) >> 14) & 3) +#define XGENE_DMA_GCR 0x10 +#define XGENE_DMA_CH_SETUP(v) \ + ((v) = ((v) & ~0x000FFFFF) | 0x000AAFFF) +#define XGENE_DMA_ENABLE(v) ((v) |= BIT(31)) +#define XGENE_DMA_DISABLE(v) ((v) &= ~BIT(31)) +#define XGENE_DMA_RAID6_CONT 0x14 +#define XGENE_DMA_RAID6_MULTI_CTRL(v) ((v) << 24) +#define XGENE_DMA_INT 0x70 +#define XGENE_DMA_INT_MASK 0x74 +#define XGENE_DMA_INT_ALL_MASK 0xFFFFFFFF +#define XGENE_DMA_INT_ALL_UNMASK 0x0 +#define XGENE_DMA_INT_MASK_SHIFT 0x14 +#define XGENE_DMA_RING_INT0_MASK 0x90A0 +#define XGENE_DMA_RING_INT1_MASK 0x90A8 +#define XGENE_DMA_RING_INT2_MASK 0x90B0 +#define XGENE_DMA_RING_INT3_MASK 0x90B8 +#define XGENE_DMA_RING_INT4_MASK 0x90C0 +#define XGENE_DMA_CFG_RING_WQ_ASSOC 0x90E0 +#define XGENE_DMA_ASSOC_RING_MNGR1 0xFFFFFFFF +#define XGENE_DMA_MEM_RAM_SHUTDOWN 0xD070 +#define XGENE_DMA_BLK_MEM_RDY 0xD074 +#define XGENE_DMA_BLK_MEM_RDY_VAL 0xFFFFFFFF + +/* X-Gene SoC EFUSE csr register and bit defination */ +#define XGENE_SOC_JTAG1_SHADOW 0x18 +#define XGENE_DMA_PQ_DISABLE_MASK BIT(13) + +/* X-Gene DMA Descriptor format */ +#define XGENE_DMA_DESC_NV_BIT BIT_ULL(50) +#define XGENE_DMA_DESC_IN_BIT BIT_ULL(55) +#define XGENE_DMA_DESC_C_BIT BIT_ULL(63) +#define XGENE_DMA_DESC_DR_BIT BIT_ULL(61) +#define XGENE_DMA_DESC_ELERR_POS 46 +#define XGENE_DMA_DESC_RTYPE_POS 56 +#define XGENE_DMA_DESC_LERR_POS 60 +#define XGENE_DMA_DESC_FLYBY_POS 4 +#define XGENE_DMA_DESC_BUFLEN_POS 48 +#define XGENE_DMA_DESC_HOENQ_NUM_POS 48 + +#define XGENE_DMA_DESC_NV_SET(m) \ + (((u64 *)(m))[0] |= XGENE_DMA_DESC_NV_BIT) +#define XGENE_DMA_DESC_IN_SET(m) \ + (((u64 *)(m))[0] |= XGENE_DMA_DESC_IN_BIT) +#define XGENE_DMA_DESC_RTYPE_SET(m, v) \ + (((u64 *)(m))[0] |= ((u64)(v) << XGENE_DMA_DESC_RTYPE_POS)) +#define XGENE_DMA_DESC_BUFADDR_SET(m, v) \ + (((u64 *)(m))[0] |= (v)) +#define XGENE_DMA_DESC_BUFLEN_SET(m, v) \ + (((u64 *)(m))[0] |= ((u64)(v) << XGENE_DMA_DESC_BUFLEN_POS)) +#define XGENE_DMA_DESC_C_SET(m) \ + (((u64 *)(m))[1] |= XGENE_DMA_DESC_C_BIT) +#define XGENE_DMA_DESC_FLYBY_SET(m, v) \ + (((u64 *)(m))[2] |= ((v) << XGENE_DMA_DESC_FLYBY_POS)) +#define XGENE_DMA_DESC_MULTI_SET(m, v, i) \ + (((u64 *)(m))[2] |= ((u64)(v) << (((i) + 1) * 8))) +#define XGENE_DMA_DESC_DR_SET(m) \ + (((u64 *)(m))[2] |= XGENE_DMA_DESC_DR_BIT) +#define XGENE_DMA_DESC_DST_ADDR_SET(m, v) \ + (((u64 *)(m))[3] |= (v)) +#define XGENE_DMA_DESC_H0ENQ_NUM_SET(m, v) \ + (((u64 *)(m))[3] |= ((u64)(v) << XGENE_DMA_DESC_HOENQ_NUM_POS)) +#define XGENE_DMA_DESC_ELERR_RD(m) \ + (((m) >> XGENE_DMA_DESC_ELERR_POS) & 0x3) +#define XGENE_DMA_DESC_LERR_RD(m) \ + (((m) >> XGENE_DMA_DESC_LERR_POS) & 0x7) +#define XGENE_DMA_DESC_STATUS(elerr, lerr) \ + (((elerr) << 4) | (lerr)) + +/* X-Gene DMA descriptor empty s/w signature */ +#define XGENE_DMA_DESC_EMPTY_INDEX 0 +#define XGENE_DMA_DESC_EMPTY_SIGNATURE ~0ULL +#define XGENE_DMA_DESC_SET_EMPTY(m) \ + (((u64 *)(m))[XGENE_DMA_DESC_EMPTY_INDEX] = \ + XGENE_DMA_DESC_EMPTY_SIGNATURE) +#define XGENE_DMA_DESC_IS_EMPTY(m) \ + (((u64 *)(m))[XGENE_DMA_DESC_EMPTY_INDEX] == \ + XGENE_DMA_DESC_EMPTY_SIGNATURE) + +/* X-Gene DMA configurable parameters defines */ +#define XGENE_DMA_RING_NUM 512 +#define XGENE_DMA_BUFNUM 0x0 +#define XGENE_DMA_CPU_BUFNUM 0x18 +#define XGENE_DMA_RING_OWNER_DMA 0x03 +#define XGENE_DMA_RING_OWNER_CPU 0x0F +#define XGENE_DMA_RING_TYPE_REGULAR 0x01 +#define XGENE_DMA_RING_WQ_DESC_SIZE 32 /* 32 Bytes */ +#define XGENE_DMA_RING_NUM_CONFIG 5 +#define XGENE_DMA_MAX_CHANNEL 4 +#define XGENE_DMA_XOR_CHANNEL 0 +#define XGENE_DMA_PQ_CHANNEL 1 +#define XGENE_DMA_MAX_BYTE_CNT 0x4000 /* 16 KB */ +#define XGENE_DMA_MAX_64B_DESC_BYTE_CNT 0x14000 /* 80 KB */ +#define XGENE_DMA_XOR_ALIGNMENT 6 /* 64 Bytes */ +#define XGENE_DMA_MAX_XOR_SRC 5 +#define XGENE_DMA_16K_BUFFER_LEN_CODE 0x0 +#define XGENE_DMA_INVALID_LEN_CODE 0x7800 + +/* X-Gene DMA descriptor error codes */ +#define ERR_DESC_AXI 0x01 +#define ERR_BAD_DESC 0x02 +#define ERR_READ_DATA_AXI 0x03 +#define ERR_WRITE_DATA_AXI 0x04 +#define ERR_FBP_TIMEOUT 0x05 +#define ERR_ECC 0x06 +#define ERR_DIFF_SIZE 0x08 +#define ERR_SCT_GAT_LEN 0x09 +#define ERR_CRC_ERR 0x11 +#define ERR_CHKSUM 0x12 +#define ERR_DIF 0x13 + +/* X-Gene DMA error interrupt codes */ +#define ERR_DIF_SIZE_INT 0x0 +#define ERR_GS_ERR_INT 0x1 +#define ERR_FPB_TIMEO_INT 0x2 +#define ERR_WFIFO_OVF_INT 0x3 +#define ERR_RFIFO_OVF_INT 0x4 +#define ERR_WR_TIMEO_INT 0x5 +#define ERR_RD_TIMEO_INT 0x6 +#define ERR_WR_ERR_INT 0x7 +#define ERR_RD_ERR_INT 0x8 +#define ERR_BAD_DESC_INT 0x9 +#define ERR_DESC_DST_INT 0xA +#define ERR_DESC_SRC_INT 0xB + +/* X-Gene DMA flyby operation code */ +#define FLYBY_2SRC_XOR 0x8 +#define FLYBY_3SRC_XOR 0x9 +#define FLYBY_4SRC_XOR 0xA +#define FLYBY_5SRC_XOR 0xB + +/* X-Gene DMA SW descriptor flags */ +#define XGENE_DMA_FLAG_64B_DESC BIT(0) + +/* Define to dump X-Gene DMA descriptor */ +#define XGENE_DMA_DESC_DUMP(desc, m) \ + print_hex_dump(KERN_ERR, (m), \ + DUMP_PREFIX_ADDRESS, 16, 8, (desc), 32, 0) + +#define to_dma_desc_sw(tx) \ + container_of(tx, struct xgene_dma_desc_sw, tx) +#define to_dma_chan(dchan) \ + container_of(dchan, struct xgene_dma_chan, dma_chan) + +#define chan_dbg(chan, fmt, arg...) \ + dev_dbg(chan->dev, "%s: " fmt, chan->name, ##arg) +#define chan_err(chan, fmt, arg...) \ + dev_err(chan->dev, "%s: " fmt, chan->name, ##arg) + +struct xgene_dma_desc_hw { + u64 m0; + u64 m1; + u64 m2; + u64 m3; +}; + +enum xgene_dma_ring_cfgsize { + XGENE_DMA_RING_CFG_SIZE_512B, + XGENE_DMA_RING_CFG_SIZE_2KB, + XGENE_DMA_RING_CFG_SIZE_16KB, + XGENE_DMA_RING_CFG_SIZE_64KB, + XGENE_DMA_RING_CFG_SIZE_512KB, + XGENE_DMA_RING_CFG_SIZE_INVALID +}; + +struct xgene_dma_ring { + struct xgene_dma *pdma; + u8 buf_num; + u16 id; + u16 num; + u16 head; + u16 owner; + u16 slots; + u16 dst_ring_num; + u32 size; + void __iomem *cmd; + void __iomem *cmd_base; + dma_addr_t desc_paddr; + u32 state[XGENE_DMA_RING_NUM_CONFIG]; + enum xgene_dma_ring_cfgsize cfgsize; + union { + void *desc_vaddr; + struct xgene_dma_desc_hw *desc_hw; + }; +}; + +struct xgene_dma_desc_sw { + struct xgene_dma_desc_hw desc1; + struct xgene_dma_desc_hw desc2; + u32 flags; + struct list_head node; + struct list_head tx_list; + struct dma_async_tx_descriptor tx; +}; + +/** + * struct xgene_dma_chan - internal representation of an X-Gene DMA channel + * @dma_chan: dmaengine channel object member + * @pdma: X-Gene DMA device structure reference + * @dev: struct device reference for dma mapping api + * @id: raw id of this channel + * @rx_irq: channel IRQ + * @name: name of X-Gene DMA channel + * @lock: serializes enqueue/dequeue operations to the descriptor pool + * @pending: number of transaction request pushed to DMA controller for + * execution, but still waiting for completion, + * @max_outstanding: max number of outstanding request we can push to channel + * @ld_pending: descriptors which are queued to run, but have not yet been + * submitted to the hardware for execution + * @ld_running: descriptors which are currently being executing by the hardware + * @ld_completed: descriptors which have finished execution by the hardware. + * These descriptors have already had their cleanup actions run. They + * are waiting for the ACK bit to be set by the async tx API. + * @desc_pool: descriptor pool for DMA operations + * @tasklet: bottom half where all completed descriptors cleans + * @tx_ring: transmit ring descriptor that we use to prepare actual + * descriptors for further executions + * @rx_ring: receive ring descriptor that we use to get completed DMA + * descriptors during cleanup time + */ +struct xgene_dma_chan { + struct dma_chan dma_chan; + struct xgene_dma *pdma; + struct device *dev; + int id; + int rx_irq; + char name[8]; + spinlock_t lock; + int pending; + int max_outstanding; + struct list_head ld_pending; + struct list_head ld_running; + struct list_head ld_completed; + struct dma_pool *desc_pool; + struct tasklet_struct tasklet; + struct xgene_dma_ring tx_ring; + struct xgene_dma_ring rx_ring; +}; + +/** + * struct xgene_dma - internal representation of an X-Gene DMA device + * @err_irq: DMA error irq number + * @ring_num: start id number for DMA ring + * @csr_dma: base for DMA register access + * @csr_ring: base for DMA ring register access + * @csr_ring_cmd: base for DMA ring command register access + * @csr_efuse: base for efuse register access + * @dma_dev: embedded struct dma_device + * @chan: reference to X-Gene DMA channels + */ +struct xgene_dma { + struct device *dev; + struct clk *clk; + int err_irq; + int ring_num; + void __iomem *csr_dma; + void __iomem *csr_ring; + void __iomem *csr_ring_cmd; + void __iomem *csr_efuse; + struct dma_device dma_dev[XGENE_DMA_MAX_CHANNEL]; + struct xgene_dma_chan chan[XGENE_DMA_MAX_CHANNEL]; +}; + +static const char * const xgene_dma_desc_err[] = { + [ERR_DESC_AXI] = "AXI error when reading src/dst link list", + [ERR_BAD_DESC] = "ERR or El_ERR fields not set to zero in desc", + [ERR_READ_DATA_AXI] = "AXI error when reading data", + [ERR_WRITE_DATA_AXI] = "AXI error when writing data", + [ERR_FBP_TIMEOUT] = "Timeout on bufpool fetch", + [ERR_ECC] = "ECC double bit error", + [ERR_DIFF_SIZE] = "Bufpool too small to hold all the DIF result", + [ERR_SCT_GAT_LEN] = "Gather and scatter data length not same", + [ERR_CRC_ERR] = "CRC error", + [ERR_CHKSUM] = "Checksum error", + [ERR_DIF] = "DIF error", +}; + +static const char * const xgene_dma_err[] = { + [ERR_DIF_SIZE_INT] = "DIF size error", + [ERR_GS_ERR_INT] = "Gather scatter not same size error", + [ERR_FPB_TIMEO_INT] = "Free pool time out error", + [ERR_WFIFO_OVF_INT] = "Write FIFO over flow error", + [ERR_RFIFO_OVF_INT] = "Read FIFO over flow error", + [ERR_WR_TIMEO_INT] = "Write time out error", + [ERR_RD_TIMEO_INT] = "Read time out error", + [ERR_WR_ERR_INT] = "HBF bus write error", + [ERR_RD_ERR_INT] = "HBF bus read error", + [ERR_BAD_DESC_INT] = "Ring descriptor HE0 not set error", + [ERR_DESC_DST_INT] = "HFB reading dst link address error", + [ERR_DESC_SRC_INT] = "HFB reading src link address error", +}; + +static bool is_pq_enabled(struct xgene_dma *pdma) +{ + u32 val; + + val = ioread32(pdma->csr_efuse + XGENE_SOC_JTAG1_SHADOW); + return !(val & XGENE_DMA_PQ_DISABLE_MASK); +} + +static void xgene_dma_cpu_to_le64(u64 *desc, int count) +{ + int i; + + for (i = 0; i < count; i++) + desc[i] = cpu_to_le64(desc[i]); +} + +static u16 xgene_dma_encode_len(u32 len) +{ + return (len < XGENE_DMA_MAX_BYTE_CNT) ? + len : XGENE_DMA_16K_BUFFER_LEN_CODE; +} + +static u8 xgene_dma_encode_xor_flyby(u32 src_cnt) +{ + static u8 flyby_type[] = { + FLYBY_2SRC_XOR, /* Dummy */ + FLYBY_2SRC_XOR, /* Dummy */ + FLYBY_2SRC_XOR, + FLYBY_3SRC_XOR, + FLYBY_4SRC_XOR, + FLYBY_5SRC_XOR + }; + + return flyby_type[src_cnt]; +} + +static u32 xgene_dma_ring_desc_cnt(struct xgene_dma_ring *ring) +{ + u32 __iomem *cmd_base = ring->cmd_base; + u32 ring_state = ioread32(&cmd_base[1]); + + return XGENE_DMA_RING_DESC_CNT(ring_state); +} + +static void xgene_dma_set_src_buffer(void *ext8, size_t *len, + dma_addr_t *paddr) +{ + size_t nbytes = (*len < XGENE_DMA_MAX_BYTE_CNT) ? + *len : XGENE_DMA_MAX_BYTE_CNT; + + XGENE_DMA_DESC_BUFADDR_SET(ext8, *paddr); + XGENE_DMA_DESC_BUFLEN_SET(ext8, xgene_dma_encode_len(nbytes)); + *len -= nbytes; + *paddr += nbytes; +} + +static void xgene_dma_invalidate_buffer(void *ext8) +{ + XGENE_DMA_DESC_BUFLEN_SET(ext8, XGENE_DMA_INVALID_LEN_CODE); +} + +static void *xgene_dma_lookup_ext8(u64 *desc, int idx) +{ + return (idx % 2) ? (desc + idx - 1) : (desc + idx + 1); +} + +static void xgene_dma_init_desc(void *desc, u16 dst_ring_num) +{ + XGENE_DMA_DESC_C_SET(desc); /* Coherent IO */ + XGENE_DMA_DESC_IN_SET(desc); + XGENE_DMA_DESC_H0ENQ_NUM_SET(desc, dst_ring_num); + XGENE_DMA_DESC_RTYPE_SET(desc, XGENE_DMA_RING_OWNER_DMA); +} + +static void xgene_dma_prep_cpy_desc(struct xgene_dma_chan *chan, + struct xgene_dma_desc_sw *desc_sw, + dma_addr_t dst, dma_addr_t src, + size_t len) +{ + void *desc1, *desc2; + int i; + + /* Get 1st descriptor */ + desc1 = &desc_sw->desc1; + xgene_dma_init_desc(desc1, chan->tx_ring.dst_ring_num); + + /* Set destination address */ + XGENE_DMA_DESC_DR_SET(desc1); + XGENE_DMA_DESC_DST_ADDR_SET(desc1, dst); + + /* Set 1st source address */ + xgene_dma_set_src_buffer(desc1 + 8, &len, &src); + + if (len <= 0) { + desc2 = NULL; + goto skip_additional_src; + } + + /* + * We need to split this source buffer, + * and need to use 2nd descriptor + */ + desc2 = &desc_sw->desc2; + XGENE_DMA_DESC_NV_SET(desc1); + + /* Set 2nd to 5th source address */ + for (i = 0; i < 4 && len; i++) + xgene_dma_set_src_buffer(xgene_dma_lookup_ext8(desc2, i), + &len, &src); + + /* Invalidate unused source address field */ + for (; i < 4; i++) + xgene_dma_invalidate_buffer(xgene_dma_lookup_ext8(desc2, i)); + + /* Updated flag that we have prepared 64B descriptor */ + desc_sw->flags |= XGENE_DMA_FLAG_64B_DESC; + +skip_additional_src: + /* Hardware stores descriptor in little endian format */ + xgene_dma_cpu_to_le64(desc1, 4); + if (desc2) + xgene_dma_cpu_to_le64(desc2, 4); +} + +static void xgene_dma_prep_xor_desc(struct xgene_dma_chan *chan, + struct xgene_dma_desc_sw *desc_sw, + dma_addr_t *dst, dma_addr_t *src, + u32 src_cnt, size_t *nbytes, + const u8 *scf) +{ + void *desc1, *desc2; + size_t len = *nbytes; + int i; + + desc1 = &desc_sw->desc1; + desc2 = &desc_sw->desc2; + + /* Initialize DMA descriptor */ + xgene_dma_init_desc(desc1, chan->tx_ring.dst_ring_num); + + /* Set destination address */ + XGENE_DMA_DESC_DR_SET(desc1); + XGENE_DMA_DESC_DST_ADDR_SET(desc1, *dst); + + /* We have multiple source addresses, so need to set NV bit*/ + XGENE_DMA_DESC_NV_SET(desc1); + + /* Set flyby opcode */ + XGENE_DMA_DESC_FLYBY_SET(desc1, xgene_dma_encode_xor_flyby(src_cnt)); + + /* Set 1st to 5th source addresses */ + for (i = 0; i < src_cnt; i++) { + len = *nbytes; + xgene_dma_set_src_buffer((i == 0) ? (desc1 + 8) : + xgene_dma_lookup_ext8(desc2, i - 1), + &len, &src[i]); + XGENE_DMA_DESC_MULTI_SET(desc1, scf[i], i); + } + + /* Hardware stores descriptor in little endian format */ + xgene_dma_cpu_to_le64(desc1, 4); + xgene_dma_cpu_to_le64(desc2, 4); + + /* Update meta data */ + *nbytes = len; + *dst += XGENE_DMA_MAX_BYTE_CNT; + + /* We need always 64B descriptor to perform xor or pq operations */ + desc_sw->flags |= XGENE_DMA_FLAG_64B_DESC; +} + +static dma_cookie_t xgene_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct xgene_dma_desc_sw *desc; + struct xgene_dma_chan *chan; + dma_cookie_t cookie; + + if (unlikely(!tx)) + return -EINVAL; + + chan = to_dma_chan(tx->chan); + desc = to_dma_desc_sw(tx); + + spin_lock_bh(&chan->lock); + + cookie = dma_cookie_assign(tx); + + /* Add this transaction list onto the tail of the pending queue */ + list_splice_tail_init(&desc->tx_list, &chan->ld_pending); + + spin_unlock_bh(&chan->lock); + + return cookie; +} + +static void xgene_dma_clean_descriptor(struct xgene_dma_chan *chan, + struct xgene_dma_desc_sw *desc) +{ + list_del(&desc->node); + chan_dbg(chan, "LD %p free\n", desc); + dma_pool_free(chan->desc_pool, desc, desc->tx.phys); +} + +static struct xgene_dma_desc_sw *xgene_dma_alloc_descriptor( + struct xgene_dma_chan *chan) +{ + struct xgene_dma_desc_sw *desc; + dma_addr_t phys; + + desc = dma_pool_alloc(chan->desc_pool, GFP_NOWAIT, &phys); + if (!desc) { + chan_err(chan, "Failed to allocate LDs\n"); + return NULL; + } + + memset(desc, 0, sizeof(*desc)); + + INIT_LIST_HEAD(&desc->tx_list); + desc->tx.phys = phys; + desc->tx.tx_submit = xgene_dma_tx_submit; + dma_async_tx_descriptor_init(&desc->tx, &chan->dma_chan); + + chan_dbg(chan, "LD %p allocated\n", desc); + + return desc; +} + +/** + * xgene_dma_clean_completed_descriptor - free all descriptors which + * has been completed and acked + * @chan: X-Gene DMA channel + * + * This function is used on all completed and acked descriptors. + */ +static void xgene_dma_clean_completed_descriptor(struct xgene_dma_chan *chan) +{ + struct xgene_dma_desc_sw *desc, *_desc; + + /* Run the callback for each descriptor, in order */ + list_for_each_entry_safe(desc, _desc, &chan->ld_completed, node) { + if (async_tx_test_ack(&desc->tx)) + xgene_dma_clean_descriptor(chan, desc); + } +} + +/** + * xgene_dma_run_tx_complete_actions - cleanup a single link descriptor + * @chan: X-Gene DMA channel + * @desc: descriptor to cleanup and free + * + * This function is used on a descriptor which has been executed by the DMA + * controller. It will run any callbacks, submit any dependencies. + */ +static void xgene_dma_run_tx_complete_actions(struct xgene_dma_chan *chan, + struct xgene_dma_desc_sw *desc) +{ + struct dma_async_tx_descriptor *tx = &desc->tx; + + /* + * If this is not the last transaction in the group, + * then no need to complete cookie and run any callback as + * this is not the tx_descriptor which had been sent to caller + * of this DMA request + */ + + if (tx->cookie == 0) + return; + + dma_cookie_complete(tx); + + /* Run the link descriptor callback function */ + if (tx->callback) + tx->callback(tx->callback_param); + + dma_descriptor_unmap(tx); + + /* Run any dependencies */ + dma_run_dependencies(tx); +} + +/** + * xgene_dma_clean_running_descriptor - move the completed descriptor from + * ld_running to ld_completed + * @chan: X-Gene DMA channel + * @desc: the descriptor which is completed + * + * Free the descriptor directly if acked by async_tx api, + * else move it to queue ld_completed. + */ +static void xgene_dma_clean_running_descriptor(struct xgene_dma_chan *chan, + struct xgene_dma_desc_sw *desc) +{ + /* Remove from the list of running transactions */ + list_del(&desc->node); + + /* + * the client is allowed to attach dependent operations + * until 'ack' is set + */ + if (!async_tx_test_ack(&desc->tx)) { + /* + * Move this descriptor to the list of descriptors which is + * completed, but still awaiting the 'ack' bit to be set. + */ + list_add_tail(&desc->node, &chan->ld_completed); + return; + } + + chan_dbg(chan, "LD %p free\n", desc); + dma_pool_free(chan->desc_pool, desc, desc->tx.phys); +} + +static int xgene_chan_xfer_request(struct xgene_dma_ring *ring, + struct xgene_dma_desc_sw *desc_sw) +{ + struct xgene_dma_desc_hw *desc_hw; + + /* Check if can push more descriptor to hw for execution */ + if (xgene_dma_ring_desc_cnt(ring) > (ring->slots - 2)) + return -EBUSY; + + /* Get hw descriptor from DMA tx ring */ + desc_hw = &ring->desc_hw[ring->head]; + + /* + * Increment the head count to point next + * descriptor for next time + */ + if (++ring->head == ring->slots) + ring->head = 0; + + /* Copy prepared sw descriptor data to hw descriptor */ + memcpy(desc_hw, &desc_sw->desc1, sizeof(*desc_hw)); + + /* + * Check if we have prepared 64B descriptor, + * in this case we need one more hw descriptor + */ + if (desc_sw->flags & XGENE_DMA_FLAG_64B_DESC) { + desc_hw = &ring->desc_hw[ring->head]; + + if (++ring->head == ring->slots) + ring->head = 0; + + memcpy(desc_hw, &desc_sw->desc2, sizeof(*desc_hw)); + } + + /* Notify the hw that we have descriptor ready for execution */ + iowrite32((desc_sw->flags & XGENE_DMA_FLAG_64B_DESC) ? + 2 : 1, ring->cmd); + + return 0; +} + +/** + * xgene_chan_xfer_ld_pending - push any pending transactions to hw + * @chan : X-Gene DMA channel + * + * LOCKING: must hold chan->desc_lock + */ +static void xgene_chan_xfer_ld_pending(struct xgene_dma_chan *chan) +{ + struct xgene_dma_desc_sw *desc_sw, *_desc_sw; + int ret; + + /* + * If the list of pending descriptors is empty, then we + * don't need to do any work at all + */ + if (list_empty(&chan->ld_pending)) { + chan_dbg(chan, "No pending LDs\n"); + return; + } + + /* + * Move elements from the queue of pending transactions onto the list + * of running transactions and push it to hw for further executions + */ + list_for_each_entry_safe(desc_sw, _desc_sw, &chan->ld_pending, node) { + /* + * Check if have pushed max number of transactions to hw + * as capable, so let's stop here and will push remaining + * elements from pening ld queue after completing some + * descriptors that we have already pushed + */ + if (chan->pending >= chan->max_outstanding) + return; + + ret = xgene_chan_xfer_request(&chan->tx_ring, desc_sw); + if (ret) + return; + + /* + * Delete this element from ld pending queue and append it to + * ld running queue + */ + list_move_tail(&desc_sw->node, &chan->ld_running); + + /* Increment the pending transaction count */ + chan->pending++; + } +} + +/** + * xgene_dma_cleanup_descriptors - cleanup link descriptors which are completed + * and move them to ld_completed to free until flag 'ack' is set + * @chan: X-Gene DMA channel + * + * This function is used on descriptors which have been executed by the DMA + * controller. It will run any callbacks, submit any dependencies, then + * free these descriptors if flag 'ack' is set. + */ +static void xgene_dma_cleanup_descriptors(struct xgene_dma_chan *chan) +{ + struct xgene_dma_ring *ring = &chan->rx_ring; + struct xgene_dma_desc_sw *desc_sw, *_desc_sw; + struct xgene_dma_desc_hw *desc_hw; + u8 status; + + /* Clean already completed and acked descriptors */ + xgene_dma_clean_completed_descriptor(chan); + + /* Run the callback for each descriptor, in order */ + list_for_each_entry_safe(desc_sw, _desc_sw, &chan->ld_running, node) { + /* Get subsequent hw descriptor from DMA rx ring */ + desc_hw = &ring->desc_hw[ring->head]; + + /* Check if this descriptor has been completed */ + if (unlikely(XGENE_DMA_DESC_IS_EMPTY(desc_hw))) + break; + + if (++ring->head == ring->slots) + ring->head = 0; + + /* Check if we have any error with DMA transactions */ + status = XGENE_DMA_DESC_STATUS( + XGENE_DMA_DESC_ELERR_RD(le64_to_cpu( + desc_hw->m0)), + XGENE_DMA_DESC_LERR_RD(le64_to_cpu( + desc_hw->m0))); + if (status) { + /* Print the DMA error type */ + chan_err(chan, "%s\n", xgene_dma_desc_err[status]); + + /* + * We have DMA transactions error here. Dump DMA Tx + * and Rx descriptors for this request */ + XGENE_DMA_DESC_DUMP(&desc_sw->desc1, + "X-Gene DMA TX DESC1: "); + + if (desc_sw->flags & XGENE_DMA_FLAG_64B_DESC) + XGENE_DMA_DESC_DUMP(&desc_sw->desc2, + "X-Gene DMA TX DESC2: "); + + XGENE_DMA_DESC_DUMP(desc_hw, + "X-Gene DMA RX ERR DESC: "); + } + + /* Notify the hw about this completed descriptor */ + iowrite32(-1, ring->cmd); + + /* Mark this hw descriptor as processed */ + XGENE_DMA_DESC_SET_EMPTY(desc_hw); + + xgene_dma_run_tx_complete_actions(chan, desc_sw); + + xgene_dma_clean_running_descriptor(chan, desc_sw); + + /* + * Decrement the pending transaction count + * as we have processed one + */ + chan->pending--; + } + + /* + * Start any pending transactions automatically + * In the ideal case, we keep the DMA controller busy while we go + * ahead and free the descriptors below. + */ + xgene_chan_xfer_ld_pending(chan); +} + +static int xgene_dma_alloc_chan_resources(struct dma_chan *dchan) +{ + struct xgene_dma_chan *chan = to_dma_chan(dchan); + + /* Has this channel already been allocated? */ + if (chan->desc_pool) + return 1; + + chan->desc_pool = dma_pool_create(chan->name, chan->dev, + sizeof(struct xgene_dma_desc_sw), + 0, 0); + if (!chan->desc_pool) { + chan_err(chan, "Failed to allocate descriptor pool\n"); + return -ENOMEM; + } + + chan_dbg(chan, "Allocate descripto pool\n"); + + return 1; +} + +/** + * xgene_dma_free_desc_list - Free all descriptors in a queue + * @chan: X-Gene DMA channel + * @list: the list to free + * + * LOCKING: must hold chan->desc_lock + */ +static void xgene_dma_free_desc_list(struct xgene_dma_chan *chan, + struct list_head *list) +{ + struct xgene_dma_desc_sw *desc, *_desc; + + list_for_each_entry_safe(desc, _desc, list, node) + xgene_dma_clean_descriptor(chan, desc); +} + +static void xgene_dma_free_tx_desc_list(struct xgene_dma_chan *chan, + struct list_head *list) +{ + struct xgene_dma_desc_sw *desc, *_desc; + + list_for_each_entry_safe(desc, _desc, list, node) + xgene_dma_clean_descriptor(chan, desc); +} + +static void xgene_dma_free_chan_resources(struct dma_chan *dchan) +{ + struct xgene_dma_chan *chan = to_dma_chan(dchan); + + chan_dbg(chan, "Free all resources\n"); + + if (!chan->desc_pool) + return; + + spin_lock_bh(&chan->lock); + + /* Process all running descriptor */ + xgene_dma_cleanup_descriptors(chan); + + /* Clean all link descriptor queues */ + xgene_dma_free_desc_list(chan, &chan->ld_pending); + xgene_dma_free_desc_list(chan, &chan->ld_running); + xgene_dma_free_desc_list(chan, &chan->ld_completed); + + spin_unlock_bh(&chan->lock); + + /* Delete this channel DMA pool */ + dma_pool_destroy(chan->desc_pool); + chan->desc_pool = NULL; +} + +static struct dma_async_tx_descriptor *xgene_dma_prep_memcpy( + struct dma_chan *dchan, dma_addr_t dst, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct xgene_dma_desc_sw *first = NULL, *new; + struct xgene_dma_chan *chan; + size_t copy; + + if (unlikely(!dchan || !len)) + return NULL; + + chan = to_dma_chan(dchan); + + do { + /* Allocate the link descriptor from DMA pool */ + new = xgene_dma_alloc_descriptor(chan); + if (!new) + goto fail; + + /* Create the largest transaction possible */ + copy = min_t(size_t, len, XGENE_DMA_MAX_64B_DESC_BYTE_CNT); + + /* Prepare DMA descriptor */ + xgene_dma_prep_cpy_desc(chan, new, dst, src, copy); + + if (!first) + first = new; + + new->tx.cookie = 0; + async_tx_ack(&new->tx); + + /* Update metadata */ + len -= copy; + dst += copy; + src += copy; + + /* Insert the link descriptor to the LD ring */ + list_add_tail(&new->node, &first->tx_list); + } while (len); + + new->tx.flags = flags; /* client is in control of this ack */ + new->tx.cookie = -EBUSY; + list_splice(&first->tx_list, &new->tx_list); + + return &new->tx; + +fail: + if (!first) + return NULL; + + xgene_dma_free_tx_desc_list(chan, &first->tx_list); + return NULL; +} + +static struct dma_async_tx_descriptor *xgene_dma_prep_sg( + struct dma_chan *dchan, struct scatterlist *dst_sg, + u32 dst_nents, struct scatterlist *src_sg, + u32 src_nents, unsigned long flags) +{ + struct xgene_dma_desc_sw *first = NULL, *new = NULL; + struct xgene_dma_chan *chan; + size_t dst_avail, src_avail; + dma_addr_t dst, src; + size_t len; + + if (unlikely(!dchan)) + return NULL; + + if (unlikely(!dst_nents || !src_nents)) + return NULL; + + if (unlikely(!dst_sg || !src_sg)) + return NULL; + + chan = to_dma_chan(dchan); + + /* Get prepared for the loop */ + dst_avail = sg_dma_len(dst_sg); + src_avail = sg_dma_len(src_sg); + dst_nents--; + src_nents--; + + /* Run until we are out of scatterlist entries */ + while (true) { + /* Create the largest transaction possible */ + len = min_t(size_t, src_avail, dst_avail); + len = min_t(size_t, len, XGENE_DMA_MAX_64B_DESC_BYTE_CNT); + if (len == 0) + goto fetch; + + dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail; + src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail; + + /* Allocate the link descriptor from DMA pool */ + new = xgene_dma_alloc_descriptor(chan); + if (!new) + goto fail; + + /* Prepare DMA descriptor */ + xgene_dma_prep_cpy_desc(chan, new, dst, src, len); + + if (!first) + first = new; + + new->tx.cookie = 0; + async_tx_ack(&new->tx); + + /* update metadata */ + dst_avail -= len; + src_avail -= len; + + /* Insert the link descriptor to the LD ring */ + list_add_tail(&new->node, &first->tx_list); + +fetch: + /* fetch the next dst scatterlist entry */ + if (dst_avail == 0) { + /* no more entries: we're done */ + if (dst_nents == 0) + break; + + /* fetch the next entry: if there are no more: done */ + dst_sg = sg_next(dst_sg); + if (!dst_sg) + break; + + dst_nents--; + dst_avail = sg_dma_len(dst_sg); + } + + /* fetch the next src scatterlist entry */ + if (src_avail == 0) { + /* no more entries: we're done */ + if (src_nents == 0) + break; + + /* fetch the next entry: if there are no more: done */ + src_sg = sg_next(src_sg); + if (!src_sg) + break; + + src_nents--; + src_avail = sg_dma_len(src_sg); + } + } + + if (!new) + return NULL; + + new->tx.flags = flags; /* client is in control of this ack */ + new->tx.cookie = -EBUSY; + list_splice(&first->tx_list, &new->tx_list); + + return &new->tx; +fail: + if (!first) + return NULL; + + xgene_dma_free_tx_desc_list(chan, &first->tx_list); + return NULL; +} + +static struct dma_async_tx_descriptor *xgene_dma_prep_xor( + struct dma_chan *dchan, dma_addr_t dst, dma_addr_t *src, + u32 src_cnt, size_t len, unsigned long flags) +{ + struct xgene_dma_desc_sw *first = NULL, *new; + struct xgene_dma_chan *chan; + static u8 multi[XGENE_DMA_MAX_XOR_SRC] = { + 0x01, 0x01, 0x01, 0x01, 0x01}; + + if (unlikely(!dchan || !len)) + return NULL; + + chan = to_dma_chan(dchan); + + do { + /* Allocate the link descriptor from DMA pool */ + new = xgene_dma_alloc_descriptor(chan); + if (!new) + goto fail; + + /* Prepare xor DMA descriptor */ + xgene_dma_prep_xor_desc(chan, new, &dst, src, + src_cnt, &len, multi); + + if (!first) + first = new; + + new->tx.cookie = 0; + async_tx_ack(&new->tx); + + /* Insert the link descriptor to the LD ring */ + list_add_tail(&new->node, &first->tx_list); + } while (len); + + new->tx.flags = flags; /* client is in control of this ack */ + new->tx.cookie = -EBUSY; + list_splice(&first->tx_list, &new->tx_list); + + return &new->tx; + +fail: + if (!first) + return NULL; + + xgene_dma_free_tx_desc_list(chan, &first->tx_list); + return NULL; +} + +static struct dma_async_tx_descriptor *xgene_dma_prep_pq( + struct dma_chan *dchan, dma_addr_t *dst, dma_addr_t *src, + u32 src_cnt, const u8 *scf, size_t len, unsigned long flags) +{ + struct xgene_dma_desc_sw *first = NULL, *new; + struct xgene_dma_chan *chan; + size_t _len = len; + dma_addr_t _src[XGENE_DMA_MAX_XOR_SRC]; + static u8 multi[XGENE_DMA_MAX_XOR_SRC] = {0x01, 0x01, 0x01, 0x01, 0x01}; + + if (unlikely(!dchan || !len)) + return NULL; + + chan = to_dma_chan(dchan); + + /* + * Save source addresses on local variable, may be we have to + * prepare two descriptor to generate P and Q if both enabled + * in the flags by client + */ + memcpy(_src, src, sizeof(*src) * src_cnt); + + if (flags & DMA_PREP_PQ_DISABLE_P) + len = 0; + + if (flags & DMA_PREP_PQ_DISABLE_Q) + _len = 0; + + do { + /* Allocate the link descriptor from DMA pool */ + new = xgene_dma_alloc_descriptor(chan); + if (!new) + goto fail; + + if (!first) + first = new; + + new->tx.cookie = 0; + async_tx_ack(&new->tx); + + /* Insert the link descriptor to the LD ring */ + list_add_tail(&new->node, &first->tx_list); + + /* + * Prepare DMA descriptor to generate P, + * if DMA_PREP_PQ_DISABLE_P flag is not set + */ + if (len) { + xgene_dma_prep_xor_desc(chan, new, &dst[0], src, + src_cnt, &len, multi); + continue; + } + + /* + * Prepare DMA descriptor to generate Q, + * if DMA_PREP_PQ_DISABLE_Q flag is not set + */ + if (_len) { + xgene_dma_prep_xor_desc(chan, new, &dst[1], _src, + src_cnt, &_len, scf); + } + } while (len || _len); + + new->tx.flags = flags; /* client is in control of this ack */ + new->tx.cookie = -EBUSY; + list_splice(&first->tx_list, &new->tx_list); + + return &new->tx; + +fail: + if (!first) + return NULL; + + xgene_dma_free_tx_desc_list(chan, &first->tx_list); + return NULL; +} + +static void xgene_dma_issue_pending(struct dma_chan *dchan) +{ + struct xgene_dma_chan *chan = to_dma_chan(dchan); + + spin_lock_bh(&chan->lock); + xgene_chan_xfer_ld_pending(chan); + spin_unlock_bh(&chan->lock); +} + +static enum dma_status xgene_dma_tx_status(struct dma_chan *dchan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + return dma_cookie_status(dchan, cookie, txstate); +} + +static void xgene_dma_tasklet_cb(unsigned long data) +{ + struct xgene_dma_chan *chan = (struct xgene_dma_chan *)data; + + spin_lock_bh(&chan->lock); + + /* Run all cleanup for descriptors which have been completed */ + xgene_dma_cleanup_descriptors(chan); + + /* Re-enable DMA channel IRQ */ + enable_irq(chan->rx_irq); + + spin_unlock_bh(&chan->lock); +} + +static irqreturn_t xgene_dma_chan_ring_isr(int irq, void *id) +{ + struct xgene_dma_chan *chan = (struct xgene_dma_chan *)id; + + BUG_ON(!chan); + + /* + * Disable DMA channel IRQ until we process completed + * descriptors + */ + disable_irq_nosync(chan->rx_irq); + + /* + * Schedule the tasklet to handle all cleanup of the current + * transaction. It will start a new transaction if there is + * one pending. + */ + tasklet_schedule(&chan->tasklet); + + return IRQ_HANDLED; +} + +static irqreturn_t xgene_dma_err_isr(int irq, void *id) +{ + struct xgene_dma *pdma = (struct xgene_dma *)id; + unsigned long int_mask; + u32 val, i; + + val = ioread32(pdma->csr_dma + XGENE_DMA_INT); + + /* Clear DMA interrupts */ + iowrite32(val, pdma->csr_dma + XGENE_DMA_INT); + + /* Print DMA error info */ + int_mask = val >> XGENE_DMA_INT_MASK_SHIFT; + for_each_set_bit(i, &int_mask, ARRAY_SIZE(xgene_dma_err)) + dev_err(pdma->dev, + "Interrupt status 0x%08X %s\n", val, xgene_dma_err[i]); + + return IRQ_HANDLED; +} + +static void xgene_dma_wr_ring_state(struct xgene_dma_ring *ring) +{ + int i; + + iowrite32(ring->num, ring->pdma->csr_ring + XGENE_DMA_RING_STATE); + + for (i = 0; i < XGENE_DMA_RING_NUM_CONFIG; i++) + iowrite32(ring->state[i], ring->pdma->csr_ring + + XGENE_DMA_RING_STATE_WR_BASE + (i * 4)); +} + +static void xgene_dma_clr_ring_state(struct xgene_dma_ring *ring) +{ + memset(ring->state, 0, sizeof(u32) * XGENE_DMA_RING_NUM_CONFIG); + xgene_dma_wr_ring_state(ring); +} + +static void xgene_dma_setup_ring(struct xgene_dma_ring *ring) +{ + void *ring_cfg = ring->state; + u64 addr = ring->desc_paddr; + void *desc; + u32 i, val; + + ring->slots = ring->size / XGENE_DMA_RING_WQ_DESC_SIZE; + + /* Clear DMA ring state */ + xgene_dma_clr_ring_state(ring); + + /* Set DMA ring type */ + XGENE_DMA_RING_TYPE_SET(ring_cfg, XGENE_DMA_RING_TYPE_REGULAR); + + if (ring->owner == XGENE_DMA_RING_OWNER_DMA) { + /* Set recombination buffer and timeout */ + XGENE_DMA_RING_RECOMBBUF_SET(ring_cfg); + XGENE_DMA_RING_RECOMTIMEOUTL_SET(ring_cfg); + XGENE_DMA_RING_RECOMTIMEOUTH_SET(ring_cfg); + } + + /* Initialize DMA ring state */ + XGENE_DMA_RING_SELTHRSH_SET(ring_cfg); + XGENE_DMA_RING_ACCEPTLERR_SET(ring_cfg); + XGENE_DMA_RING_COHERENT_SET(ring_cfg); + XGENE_DMA_RING_ADDRL_SET(ring_cfg, addr); + XGENE_DMA_RING_ADDRH_SET(ring_cfg, addr); + XGENE_DMA_RING_SIZE_SET(ring_cfg, ring->cfgsize); + + /* Write DMA ring configurations */ + xgene_dma_wr_ring_state(ring); + + /* Set DMA ring id */ + iowrite32(XGENE_DMA_RING_ID_SETUP(ring->id), + ring->pdma->csr_ring + XGENE_DMA_RING_ID); + + /* Set DMA ring buffer */ + iowrite32(XGENE_DMA_RING_ID_BUF_SETUP(ring->num), + ring->pdma->csr_ring + XGENE_DMA_RING_ID_BUF); + + if (ring->owner != XGENE_DMA_RING_OWNER_CPU) + return; + + /* Set empty signature to DMA Rx ring descriptors */ + for (i = 0; i < ring->slots; i++) { + desc = &ring->desc_hw[i]; + XGENE_DMA_DESC_SET_EMPTY(desc); + } + + /* Enable DMA Rx ring interrupt */ + val = ioread32(ring->pdma->csr_ring + XGENE_DMA_RING_NE_INT_MODE); + XGENE_DMA_RING_NE_INT_MODE_SET(val, ring->buf_num); + iowrite32(val, ring->pdma->csr_ring + XGENE_DMA_RING_NE_INT_MODE); +} + +static void xgene_dma_clear_ring(struct xgene_dma_ring *ring) +{ + u32 ring_id, val; + + if (ring->owner == XGENE_DMA_RING_OWNER_CPU) { + /* Disable DMA Rx ring interrupt */ + val = ioread32(ring->pdma->csr_ring + + XGENE_DMA_RING_NE_INT_MODE); + XGENE_DMA_RING_NE_INT_MODE_RESET(val, ring->buf_num); + iowrite32(val, ring->pdma->csr_ring + + XGENE_DMA_RING_NE_INT_MODE); + } + + /* Clear DMA ring state */ + ring_id = XGENE_DMA_RING_ID_SETUP(ring->id); + iowrite32(ring_id, ring->pdma->csr_ring + XGENE_DMA_RING_ID); + + iowrite32(0, ring->pdma->csr_ring + XGENE_DMA_RING_ID_BUF); + xgene_dma_clr_ring_state(ring); +} + +static void xgene_dma_set_ring_cmd(struct xgene_dma_ring *ring) +{ + ring->cmd_base = ring->pdma->csr_ring_cmd + + XGENE_DMA_RING_CMD_BASE_OFFSET((ring->num - + XGENE_DMA_RING_NUM)); + + ring->cmd = ring->cmd_base + XGENE_DMA_RING_CMD_OFFSET; +} + +static int xgene_dma_get_ring_size(struct xgene_dma_chan *chan, + enum xgene_dma_ring_cfgsize cfgsize) +{ + int size; + + switch (cfgsize) { + case XGENE_DMA_RING_CFG_SIZE_512B: + size = 0x200; + break; + case XGENE_DMA_RING_CFG_SIZE_2KB: + size = 0x800; + break; + case XGENE_DMA_RING_CFG_SIZE_16KB: + size = 0x4000; + break; + case XGENE_DMA_RING_CFG_SIZE_64KB: + size = 0x10000; + break; + case XGENE_DMA_RING_CFG_SIZE_512KB: + size = 0x80000; + break; + default: + chan_err(chan, "Unsupported cfg ring size %d\n", cfgsize); + return -EINVAL; + } + + return size; +} + +static void xgene_dma_delete_ring_one(struct xgene_dma_ring *ring) +{ + /* Clear DMA ring configurations */ + xgene_dma_clear_ring(ring); + + /* De-allocate DMA ring descriptor */ + if (ring->desc_vaddr) { + dma_free_coherent(ring->pdma->dev, ring->size, + ring->desc_vaddr, ring->desc_paddr); + ring->desc_vaddr = NULL; + } +} + +static void xgene_dma_delete_chan_rings(struct xgene_dma_chan *chan) +{ + xgene_dma_delete_ring_one(&chan->rx_ring); + xgene_dma_delete_ring_one(&chan->tx_ring); +} + +static int xgene_dma_create_ring_one(struct xgene_dma_chan *chan, + struct xgene_dma_ring *ring, + enum xgene_dma_ring_cfgsize cfgsize) +{ + /* Setup DMA ring descriptor variables */ + ring->pdma = chan->pdma; + ring->cfgsize = cfgsize; + ring->num = chan->pdma->ring_num++; + ring->id = XGENE_DMA_RING_ID_GET(ring->owner, ring->buf_num); + + ring->size = xgene_dma_get_ring_size(chan, cfgsize); + if (ring->size <= 0) + return ring->size; + + /* Allocate memory for DMA ring descriptor */ + ring->desc_vaddr = dma_zalloc_coherent(chan->dev, ring->size, + &ring->desc_paddr, GFP_KERNEL); + if (!ring->desc_vaddr) { + chan_err(chan, "Failed to allocate ring desc\n"); + return -ENOMEM; + } + + /* Configure and enable DMA ring */ + xgene_dma_set_ring_cmd(ring); + xgene_dma_setup_ring(ring); + + return 0; +} + +static int xgene_dma_create_chan_rings(struct xgene_dma_chan *chan) +{ + struct xgene_dma_ring *rx_ring = &chan->rx_ring; + struct xgene_dma_ring *tx_ring = &chan->tx_ring; + int ret; + + /* Create DMA Rx ring descriptor */ + rx_ring->owner = XGENE_DMA_RING_OWNER_CPU; + rx_ring->buf_num = XGENE_DMA_CPU_BUFNUM + chan->id; + + ret = xgene_dma_create_ring_one(chan, rx_ring, + XGENE_DMA_RING_CFG_SIZE_64KB); + if (ret) + return ret; + + chan_dbg(chan, "Rx ring id 0x%X num %d desc 0x%p\n", + rx_ring->id, rx_ring->num, rx_ring->desc_vaddr); + + /* Create DMA Tx ring descriptor */ + tx_ring->owner = XGENE_DMA_RING_OWNER_DMA; + tx_ring->buf_num = XGENE_DMA_BUFNUM + chan->id; + + ret = xgene_dma_create_ring_one(chan, tx_ring, + XGENE_DMA_RING_CFG_SIZE_64KB); + if (ret) { + xgene_dma_delete_ring_one(rx_ring); + return ret; + } + + tx_ring->dst_ring_num = XGENE_DMA_RING_DST_ID(rx_ring->num); + + chan_dbg(chan, + "Tx ring id 0x%X num %d desc 0x%p\n", + tx_ring->id, tx_ring->num, tx_ring->desc_vaddr); + + /* Set the max outstanding request possible to this channel */ + chan->max_outstanding = rx_ring->slots; + + return ret; +} + +static int xgene_dma_init_rings(struct xgene_dma *pdma) +{ + int ret, i, j; + + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) { + ret = xgene_dma_create_chan_rings(&pdma->chan[i]); + if (ret) { + for (j = 0; j < i; j++) + xgene_dma_delete_chan_rings(&pdma->chan[j]); + return ret; + } + } + + return ret; +} + +static void xgene_dma_enable(struct xgene_dma *pdma) +{ + u32 val; + + /* Configure and enable DMA engine */ + val = ioread32(pdma->csr_dma + XGENE_DMA_GCR); + XGENE_DMA_CH_SETUP(val); + XGENE_DMA_ENABLE(val); + iowrite32(val, pdma->csr_dma + XGENE_DMA_GCR); +} + +static void xgene_dma_disable(struct xgene_dma *pdma) +{ + u32 val; + + val = ioread32(pdma->csr_dma + XGENE_DMA_GCR); + XGENE_DMA_DISABLE(val); + iowrite32(val, pdma->csr_dma + XGENE_DMA_GCR); +} + +static void xgene_dma_mask_interrupts(struct xgene_dma *pdma) +{ + /* + * Mask DMA ring overflow, underflow and + * AXI write/read error interrupts + */ + iowrite32(XGENE_DMA_INT_ALL_MASK, + pdma->csr_dma + XGENE_DMA_RING_INT0_MASK); + iowrite32(XGENE_DMA_INT_ALL_MASK, + pdma->csr_dma + XGENE_DMA_RING_INT1_MASK); + iowrite32(XGENE_DMA_INT_ALL_MASK, + pdma->csr_dma + XGENE_DMA_RING_INT2_MASK); + iowrite32(XGENE_DMA_INT_ALL_MASK, + pdma->csr_dma + XGENE_DMA_RING_INT3_MASK); + iowrite32(XGENE_DMA_INT_ALL_MASK, + pdma->csr_dma + XGENE_DMA_RING_INT4_MASK); + + /* Mask DMA error interrupts */ + iowrite32(XGENE_DMA_INT_ALL_MASK, pdma->csr_dma + XGENE_DMA_INT_MASK); +} + +static void xgene_dma_unmask_interrupts(struct xgene_dma *pdma) +{ + /* + * Unmask DMA ring overflow, underflow and + * AXI write/read error interrupts + */ + iowrite32(XGENE_DMA_INT_ALL_UNMASK, + pdma->csr_dma + XGENE_DMA_RING_INT0_MASK); + iowrite32(XGENE_DMA_INT_ALL_UNMASK, + pdma->csr_dma + XGENE_DMA_RING_INT1_MASK); + iowrite32(XGENE_DMA_INT_ALL_UNMASK, + pdma->csr_dma + XGENE_DMA_RING_INT2_MASK); + iowrite32(XGENE_DMA_INT_ALL_UNMASK, + pdma->csr_dma + XGENE_DMA_RING_INT3_MASK); + iowrite32(XGENE_DMA_INT_ALL_UNMASK, + pdma->csr_dma + XGENE_DMA_RING_INT4_MASK); + + /* Unmask DMA error interrupts */ + iowrite32(XGENE_DMA_INT_ALL_UNMASK, + pdma->csr_dma + XGENE_DMA_INT_MASK); +} + +static void xgene_dma_init_hw(struct xgene_dma *pdma) +{ + u32 val; + + /* Associate DMA ring to corresponding ring HW */ + iowrite32(XGENE_DMA_ASSOC_RING_MNGR1, + pdma->csr_dma + XGENE_DMA_CFG_RING_WQ_ASSOC); + + /* Configure RAID6 polynomial control setting */ + if (is_pq_enabled(pdma)) + iowrite32(XGENE_DMA_RAID6_MULTI_CTRL(0x1D), + pdma->csr_dma + XGENE_DMA_RAID6_CONT); + else + dev_info(pdma->dev, "PQ is disabled in HW\n"); + + xgene_dma_enable(pdma); + xgene_dma_unmask_interrupts(pdma); + + /* Get DMA id and version info */ + val = ioread32(pdma->csr_dma + XGENE_DMA_IPBRR); + + /* DMA device info */ + dev_info(pdma->dev, + "X-Gene DMA v%d.%02d.%02d driver registered %d channels", + XGENE_DMA_REV_NO_RD(val), XGENE_DMA_BUS_ID_RD(val), + XGENE_DMA_DEV_ID_RD(val), XGENE_DMA_MAX_CHANNEL); +} + +int xgene_dma_init_ring_mngr(struct xgene_dma *pdma) +{ + if (ioread32(pdma->csr_ring + XGENE_DMA_RING_CLKEN) && + (!ioread32(pdma->csr_ring + XGENE_DMA_RING_SRST))) + return 0; + + iowrite32(0x3, pdma->csr_ring + XGENE_DMA_RING_CLKEN); + iowrite32(0x0, pdma->csr_ring + XGENE_DMA_RING_SRST); + + /* Bring up memory */ + iowrite32(0x0, pdma->csr_ring + XGENE_DMA_RING_MEM_RAM_SHUTDOWN); + + /* Force a barrier */ + ioread32(pdma->csr_ring + XGENE_DMA_RING_MEM_RAM_SHUTDOWN); + + /* reset may take up to 1ms */ + usleep_range(1000, 1100); + + if (ioread32(pdma->csr_ring + XGENE_DMA_RING_BLK_MEM_RDY) + != XGENE_DMA_RING_BLK_MEM_RDY_VAL) { + dev_err(pdma->dev, + "Failed to release ring mngr memory from shutdown\n"); + return -ENODEV; + } + + /* program threshold set 1 and all hysteresis */ + iowrite32(XGENE_DMA_RING_THRESLD0_SET1_VAL, + pdma->csr_ring + XGENE_DMA_RING_THRESLD0_SET1); + iowrite32(XGENE_DMA_RING_THRESLD1_SET1_VAL, + pdma->csr_ring + XGENE_DMA_RING_THRESLD1_SET1); + iowrite32(XGENE_DMA_RING_HYSTERESIS_VAL, + pdma->csr_ring + XGENE_DMA_RING_HYSTERESIS); + + /* Enable QPcore and assign error queue */ + iowrite32(XGENE_DMA_RING_ENABLE, + pdma->csr_ring + XGENE_DMA_RING_CONFIG); + + return 0; +} + +static int xgene_dma_init_mem(struct xgene_dma *pdma) +{ + int ret; + + ret = xgene_dma_init_ring_mngr(pdma); + if (ret) + return ret; + + /* Bring up memory */ + iowrite32(0x0, pdma->csr_dma + XGENE_DMA_MEM_RAM_SHUTDOWN); + + /* Force a barrier */ + ioread32(pdma->csr_dma + XGENE_DMA_MEM_RAM_SHUTDOWN); + + /* reset may take up to 1ms */ + usleep_range(1000, 1100); + + if (ioread32(pdma->csr_dma + XGENE_DMA_BLK_MEM_RDY) + != XGENE_DMA_BLK_MEM_RDY_VAL) { + dev_err(pdma->dev, + "Failed to release DMA memory from shutdown\n"); + return -ENODEV; + } + + return 0; +} + +static int xgene_dma_request_irqs(struct xgene_dma *pdma) +{ + struct xgene_dma_chan *chan; + int ret, i, j; + + /* Register DMA error irq */ + ret = devm_request_irq(pdma->dev, pdma->err_irq, xgene_dma_err_isr, + 0, "dma_error", pdma); + if (ret) { + dev_err(pdma->dev, + "Failed to register error IRQ %d\n", pdma->err_irq); + return ret; + } + + /* Register DMA channel rx irq */ + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) { + chan = &pdma->chan[i]; + ret = devm_request_irq(chan->dev, chan->rx_irq, + xgene_dma_chan_ring_isr, + 0, chan->name, chan); + if (ret) { + chan_err(chan, "Failed to register Rx IRQ %d\n", + chan->rx_irq); + devm_free_irq(pdma->dev, pdma->err_irq, pdma); + + for (j = 0; j < i; j++) { + chan = &pdma->chan[i]; + devm_free_irq(chan->dev, chan->rx_irq, chan); + } + + return ret; + } + } + + return 0; +} + +static void xgene_dma_free_irqs(struct xgene_dma *pdma) +{ + struct xgene_dma_chan *chan; + int i; + + /* Free DMA device error irq */ + devm_free_irq(pdma->dev, pdma->err_irq, pdma); + + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) { + chan = &pdma->chan[i]; + devm_free_irq(chan->dev, chan->rx_irq, chan); + } +} + +static void xgene_dma_set_caps(struct xgene_dma_chan *chan, + struct dma_device *dma_dev) +{ + /* Initialize DMA device capability mask */ + dma_cap_zero(dma_dev->cap_mask); + + /* Set DMA device capability */ + dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); + dma_cap_set(DMA_SG, dma_dev->cap_mask); + + /* Basically here, the X-Gene SoC DMA engine channel 0 supports XOR + * and channel 1 supports XOR, PQ both. First thing here is we have + * mechanism in hw to enable/disable PQ/XOR supports on channel 1, + * we can make sure this by reading SoC Efuse register. + * Second thing, we have hw errata that if we run channel 0 and + * channel 1 simultaneously with executing XOR and PQ request, + * suddenly DMA engine hangs, So here we enable XOR on channel 0 only + * if XOR and PQ supports on channel 1 is disabled. + */ + if ((chan->id == XGENE_DMA_PQ_CHANNEL) && + is_pq_enabled(chan->pdma)) { + dma_cap_set(DMA_PQ, dma_dev->cap_mask); + dma_cap_set(DMA_XOR, dma_dev->cap_mask); + } else if ((chan->id == XGENE_DMA_XOR_CHANNEL) && + !is_pq_enabled(chan->pdma)) { + dma_cap_set(DMA_XOR, dma_dev->cap_mask); + } + + /* Set base and prep routines */ + dma_dev->dev = chan->dev; + dma_dev->device_alloc_chan_resources = xgene_dma_alloc_chan_resources; + dma_dev->device_free_chan_resources = xgene_dma_free_chan_resources; + dma_dev->device_issue_pending = xgene_dma_issue_pending; + dma_dev->device_tx_status = xgene_dma_tx_status; + dma_dev->device_prep_dma_memcpy = xgene_dma_prep_memcpy; + dma_dev->device_prep_dma_sg = xgene_dma_prep_sg; + + if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { + dma_dev->device_prep_dma_xor = xgene_dma_prep_xor; + dma_dev->max_xor = XGENE_DMA_MAX_XOR_SRC; + dma_dev->xor_align = XGENE_DMA_XOR_ALIGNMENT; + } + + if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { + dma_dev->device_prep_dma_pq = xgene_dma_prep_pq; + dma_dev->max_pq = XGENE_DMA_MAX_XOR_SRC; + dma_dev->pq_align = XGENE_DMA_XOR_ALIGNMENT; + } +} + +static int xgene_dma_async_register(struct xgene_dma *pdma, int id) +{ + struct xgene_dma_chan *chan = &pdma->chan[id]; + struct dma_device *dma_dev = &pdma->dma_dev[id]; + int ret; + + chan->dma_chan.device = dma_dev; + + spin_lock_init(&chan->lock); + INIT_LIST_HEAD(&chan->ld_pending); + INIT_LIST_HEAD(&chan->ld_running); + INIT_LIST_HEAD(&chan->ld_completed); + tasklet_init(&chan->tasklet, xgene_dma_tasklet_cb, + (unsigned long)chan); + + chan->pending = 0; + chan->desc_pool = NULL; + dma_cookie_init(&chan->dma_chan); + + /* Setup dma device capabilities and prep routines */ + xgene_dma_set_caps(chan, dma_dev); + + /* Initialize DMA device list head */ + INIT_LIST_HEAD(&dma_dev->channels); + list_add_tail(&chan->dma_chan.device_node, &dma_dev->channels); + + /* Register with Linux async DMA framework*/ + ret = dma_async_device_register(dma_dev); + if (ret) { + chan_err(chan, "Failed to register async device %d", ret); + tasklet_kill(&chan->tasklet); + + return ret; + } + + /* DMA capability info */ + dev_info(pdma->dev, + "%s: CAPABILITY ( %s%s%s%s)\n", dma_chan_name(&chan->dma_chan), + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "MEMCPY " : "", + dma_has_cap(DMA_SG, dma_dev->cap_mask) ? "SGCPY " : "", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "XOR " : "", + dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "PQ " : ""); + + return 0; +} + +static int xgene_dma_init_async(struct xgene_dma *pdma) +{ + int ret, i, j; + + for (i = 0; i < XGENE_DMA_MAX_CHANNEL ; i++) { + ret = xgene_dma_async_register(pdma, i); + if (ret) { + for (j = 0; j < i; j++) { + dma_async_device_unregister(&pdma->dma_dev[j]); + tasklet_kill(&pdma->chan[j].tasklet); + } + + return ret; + } + } + + return ret; +} + +static void xgene_dma_async_unregister(struct xgene_dma *pdma) +{ + int i; + + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) + dma_async_device_unregister(&pdma->dma_dev[i]); +} + +static void xgene_dma_init_channels(struct xgene_dma *pdma) +{ + struct xgene_dma_chan *chan; + int i; + + pdma->ring_num = XGENE_DMA_RING_NUM; + + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) { + chan = &pdma->chan[i]; + chan->dev = pdma->dev; + chan->pdma = pdma; + chan->id = i; + sprintf(chan->name, "dmachan%d", chan->id); + } +} + +static int xgene_dma_get_resources(struct platform_device *pdev, + struct xgene_dma *pdma) +{ + struct resource *res; + int irq, i; + + /* Get DMA csr region */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get csr region\n"); + return -ENXIO; + } + + pdma->csr_dma = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(pdma->csr_dma)) { + dev_err(&pdev->dev, "Failed to ioremap csr region"); + return PTR_ERR(pdma->csr_dma); + } + + /* Get DMA ring csr region */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "Failed to get ring csr region\n"); + return -ENXIO; + } + + pdma->csr_ring = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(pdma->csr_ring)) { + dev_err(&pdev->dev, "Failed to ioremap ring csr region"); + return PTR_ERR(pdma->csr_ring); + } + + /* Get DMA ring cmd csr region */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) { + dev_err(&pdev->dev, "Failed to get ring cmd csr region\n"); + return -ENXIO; + } + + pdma->csr_ring_cmd = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(pdma->csr_ring_cmd)) { + dev_err(&pdev->dev, "Failed to ioremap ring cmd csr region"); + return PTR_ERR(pdma->csr_ring_cmd); + } + + /* Get efuse csr region */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) { + dev_err(&pdev->dev, "Failed to get efuse csr region\n"); + return -ENXIO; + } + + pdma->csr_efuse = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(pdma->csr_efuse)) { + dev_err(&pdev->dev, "Failed to ioremap efuse csr region"); + return PTR_ERR(pdma->csr_efuse); + } + + /* Get DMA error interrupt */ + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "Failed to get Error IRQ\n"); + return -ENXIO; + } + + pdma->err_irq = irq; + + /* Get DMA Rx ring descriptor interrupts for all DMA channels */ + for (i = 1; i <= XGENE_DMA_MAX_CHANNEL; i++) { + irq = platform_get_irq(pdev, i); + if (irq <= 0) { + dev_err(&pdev->dev, "Failed to get Rx IRQ\n"); + return -ENXIO; + } + + pdma->chan[i - 1].rx_irq = irq; + } + + return 0; +} + +static int xgene_dma_probe(struct platform_device *pdev) +{ + struct xgene_dma *pdma; + int ret, i; + + pdma = devm_kzalloc(&pdev->dev, sizeof(*pdma), GFP_KERNEL); + if (!pdma) + return -ENOMEM; + + pdma->dev = &pdev->dev; + platform_set_drvdata(pdev, pdma); + + ret = xgene_dma_get_resources(pdev, pdma); + if (ret) + return ret; + + pdma->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdma->clk)) { + dev_err(&pdev->dev, "Failed to get clk\n"); + return PTR_ERR(pdma->clk); + } + + /* Enable clk before accessing registers */ + ret = clk_prepare_enable(pdma->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable clk %d\n", ret); + return ret; + } + + /* Remove DMA RAM out of shutdown */ + ret = xgene_dma_init_mem(pdma); + if (ret) + goto err_clk_enable; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(42)); + if (ret) { + dev_err(&pdev->dev, "No usable DMA configuration\n"); + goto err_dma_mask; + } + + /* Initialize DMA channels software state */ + xgene_dma_init_channels(pdma); + + /* Configue DMA rings */ + ret = xgene_dma_init_rings(pdma); + if (ret) + goto err_clk_enable; + + ret = xgene_dma_request_irqs(pdma); + if (ret) + goto err_request_irq; + + /* Configure and enable DMA engine */ + xgene_dma_init_hw(pdma); + + /* Register DMA device with linux async framework */ + ret = xgene_dma_init_async(pdma); + if (ret) + goto err_async_init; + + return 0; + +err_async_init: + xgene_dma_free_irqs(pdma); + +err_request_irq: + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) + xgene_dma_delete_chan_rings(&pdma->chan[i]); + +err_dma_mask: +err_clk_enable: + clk_disable_unprepare(pdma->clk); + + return ret; +} + +static int xgene_dma_remove(struct platform_device *pdev) +{ + struct xgene_dma *pdma = platform_get_drvdata(pdev); + struct xgene_dma_chan *chan; + int i; + + xgene_dma_async_unregister(pdma); + + /* Mask interrupts and disable DMA engine */ + xgene_dma_mask_interrupts(pdma); + xgene_dma_disable(pdma); + xgene_dma_free_irqs(pdma); + + for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) { + chan = &pdma->chan[i]; + tasklet_kill(&chan->tasklet); + xgene_dma_delete_chan_rings(chan); + } + + clk_disable_unprepare(pdma->clk); + + return 0; +} + +static const struct of_device_id xgene_dma_of_match_ptr[] = { + {.compatible = "apm,xgene-storm-dma",}, + {}, +}; +MODULE_DEVICE_TABLE(of, xgene_dma_of_match_ptr); + +static struct platform_driver xgene_dma_driver = { + .probe = xgene_dma_probe, + .remove = xgene_dma_remove, + .driver = { + .name = "X-Gene-DMA", + .owner = THIS_MODULE, + .of_match_table = xgene_dma_of_match_ptr, + }, +}; + +module_platform_driver(xgene_dma_driver); + +MODULE_DESCRIPTION("APM X-Gene SoC DMA driver"); +MODULE_AUTHOR("Rameshwar Prasad Sahu "); +MODULE_AUTHOR("Loc Ho "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); -- cgit v0.10.2 From 74e353e1530044ac0dac82d64012850358e60248 Mon Sep 17 00:00:00 2001 From: Rameshwar Prasad Sahu Date: Wed, 18 Mar 2015 19:17:35 +0530 Subject: arm64: dts: Add APM X-Gene SoC DMA device and DMA clock DTS nodes This patch adds the device tree node for APM X-Gene SoC DMA controller and DMA clock. Signed-off-by: Rameshwar Prasad Sahu Signed-off-by: Loc Ho Signed-off-by: Vinod Koul diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi index f1ad9c2..9471770 100644 --- a/arch/arm64/boot/dts/apm/apm-storm.dtsi +++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi @@ -102,6 +102,7 @@ #address-cells = <2>; #size-cells = <2>; ranges; + dma-ranges = <0x0 0x0 0x0 0x0 0x400 0x0>; clocks { #address-cells = <2>; @@ -352,6 +353,15 @@ reg-names = "csr-reg"; clock-output-names = "pcie4clk"; }; + + dmaclk: dmaclk@1f27c000 { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f27c000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "dmaclk"; + }; }; pcie0: pcie@1f2b0000 { @@ -656,5 +666,21 @@ interrupts = <0x0 0x41 0x4>; clocks = <&rngpkaclk 0>; }; + + dma: dma@1f270000 { + compatible = "apm,xgene-storm-dma"; + device_type = "dma"; + reg = <0x0 0x1f270000 0x0 0x10000>, + <0x0 0x1f200000 0x0 0x10000>, + <0x0 0x1b008000 0x0 0x2000>, + <0x0 0x1054a000 0x0 0x100>; + interrupts = <0x0 0x82 0x4>, + <0x0 0xb8 0x4>, + <0x0 0xb9 0x4>, + <0x0 0xba 0x4>, + <0x0 0xbb 0x4>; + dma-coherent; + clocks = <&dmaclk 0>; + }; }; }; -- cgit v0.10.2 From fe90ced9e9d7bdba75ccae83100ae656807a6261 Mon Sep 17 00:00:00 2001 From: Rameshwar Prasad Sahu Date: Wed, 18 Mar 2015 19:17:36 +0530 Subject: Documentation: dma: Add documentation for the APM X-Gene SoC DMA device DTS binding This patch adds documentation for the APM X-Gene SoC DMA device DTS binding Signed-off-by: Rameshwar Prasad Sahu Signed-off-by: Loc Ho Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/apm-xgene-dma.txt b/Documentation/devicetree/bindings/dma/apm-xgene-dma.txt new file mode 100644 index 0000000..d305876 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/apm-xgene-dma.txt @@ -0,0 +1,47 @@ +Applied Micro X-Gene SoC DMA nodes + +DMA nodes are defined to describe on-chip DMA interfaces in +APM X-Gene SoC. + +Required properties for DMA interfaces: +- compatible: Should be "apm,xgene-dma". +- device_type: set to "dma". +- reg: Address and length of the register set for the device. + It contains the information of registers in the following order: + 1st - DMA control and status register address space. + 2nd - Descriptor ring control and status register address space. + 3rd - Descriptor ring command register address space. + 4th - Soc efuse register address space. +- interrupts: DMA has 5 interrupts sources. 1st interrupt is + DMA error reporting interrupt. 2nd, 3rd, 4th and 5th interrupts + are completion interrupts for each DMA channels. +- clocks: Reference to the clock entry. + +Optional properties: +- dma-coherent : Present if dma operations are coherent + +Example: + dmaclk: dmaclk@1f27c000 { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f27c000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "dmaclk"; + }; + + dma: dma@1f270000 { + compatible = "apm,xgene-storm-dma"; + device_type = "dma"; + reg = <0x0 0x1f270000 0x0 0x10000>, + <0x0 0x1f200000 0x0 0x10000>, + <0x0 0x1b008000 0x0 0x2000>, + <0x0 0x1054a000 0x0 0x100>; + interrupts = <0x0 0x82 0x4>, + <0x0 0xb8 0x4>, + <0x0 0xb9 0x4>, + <0x0 0xba 0x4>, + <0x0 0xbb 0x4>; + dma-coherent; + clocks = <&dmaclk 0>; + }; -- cgit v0.10.2 From a3f92e8ebe1547705dfda9c04bacb31417510692 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Thu, 2 Apr 2015 17:50:56 +0800 Subject: dmaengine: xgene_dma_init_ring_mngr() can be static Signed-off-by: Fengguang Wu Signed-off-by: Vinod Koul diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index 2383528..aa61935 100755 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -1623,7 +1623,7 @@ static void xgene_dma_init_hw(struct xgene_dma *pdma) XGENE_DMA_DEV_ID_RD(val), XGENE_DMA_MAX_CHANNEL); } -int xgene_dma_init_ring_mngr(struct xgene_dma *pdma) +static int xgene_dma_init_ring_mngr(struct xgene_dma *pdma) { if (ioread32(pdma->csr_ring + XGENE_DMA_RING_CLKEN) && (!ioread32(pdma->csr_ring + XGENE_DMA_RING_SRST))) -- cgit v0.10.2 From ad80da658bbcaaac1d3617ea6cb0f4d5e16da422 Mon Sep 17 00:00:00 2001 From: Xuelin Shi Date: Tue, 3 Mar 2015 14:26:22 +0800 Subject: dmaengine: Driver support for FSL RaidEngine device. The RaidEngine is a new FSL hardware used for Raid5/6 acceration. This patch enables the RaidEngine functionality and provides hardware offloading capability for memcpy, xor and pq computation. It works with async_tx. Signed-off-by: Harninder Rai Signed-off-by: Xuelin Shi Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 4be766f..b674683 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -125,6 +125,17 @@ config FSL_DMA EloPlus is on mpc85xx and mpc86xx and Pxxx parts, and the Elo3 is on some Txxx and Bxxx parts. +config FSL_RAID + tristate "Freescale RAID engine Support" + depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH + select DMA_ENGINE + select DMA_ENGINE_RAID + ---help--- + Enable support for Freescale RAID Engine. RAID Engine is + available on some QorIQ SoCs (like P5020/P5040). It has + the capability to offload memcpy, xor and pq computation + for raid5/6. + config MPC512X_DMA tristate "Freescale MPC512x built-in DMA engine support" depends on PPC_MPC512x || PPC_MPC831x diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 1dab9ef..345ec47 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o obj-$(CONFIG_TI_CPPI41) += cppi41.o obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_MOXART_DMA) += moxart-dma.o +obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o obj-y += xilinx/ diff --git a/drivers/dma/fsl_raid.c b/drivers/dma/fsl_raid.c new file mode 100644 index 0000000..12778bd --- /dev/null +++ b/drivers/dma/fsl_raid.c @@ -0,0 +1,904 @@ +/* + * drivers/dma/fsl_raid.c + * + * Freescale RAID Engine device driver + * + * Author: + * Harninder Rai + * Naveen Burmi + * + * Rewrite: + * Xuelin Shi + * + * Copyright (c) 2010-2014 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Theory of operation: + * + * General capabilities: + * RAID Engine (RE) block is capable of offloading XOR, memcpy and P/Q + * calculations required in RAID5 and RAID6 operations. RE driver + * registers with Linux's ASYNC layer as dma driver. RE hardware + * maintains strict ordering of the requests through chained + * command queueing. + * + * Data flow: + * Software RAID layer of Linux (MD layer) maintains RAID partitions, + * strips, stripes etc. It sends requests to the underlying ASYNC layer + * which further passes it to RE driver. ASYNC layer decides which request + * goes to which job ring of RE hardware. For every request processed by + * RAID Engine, driver gets an interrupt unless coalescing is set. The + * per job ring interrupt handler checks the status register for errors, + * clears the interrupt and leave the post interrupt processing to the irq + * thread. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "fsl_raid.h" + +#define FSL_RE_MAX_XOR_SRCS 16 +#define FSL_RE_MAX_PQ_SRCS 16 +#define FSL_RE_MIN_DESCS 256 +#define FSL_RE_MAX_DESCS (4 * FSL_RE_MIN_DESCS) +#define FSL_RE_FRAME_FORMAT 0x1 +#define FSL_RE_MAX_DATA_LEN (1024*1024) + +#define to_fsl_re_dma_desc(tx) container_of(tx, struct fsl_re_desc, async_tx) + +/* Add descriptors into per chan software queue - submit_q */ +static dma_cookie_t fsl_re_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct fsl_re_desc *desc; + struct fsl_re_chan *re_chan; + dma_cookie_t cookie; + unsigned long flags; + + desc = to_fsl_re_dma_desc(tx); + re_chan = container_of(tx->chan, struct fsl_re_chan, chan); + + spin_lock_irqsave(&re_chan->desc_lock, flags); + cookie = dma_cookie_assign(tx); + list_add_tail(&desc->node, &re_chan->submit_q); + spin_unlock_irqrestore(&re_chan->desc_lock, flags); + + return cookie; +} + +/* Copy descriptor from per chan software queue into hardware job ring */ +static void fsl_re_issue_pending(struct dma_chan *chan) +{ + struct fsl_re_chan *re_chan; + int avail; + struct fsl_re_desc *desc, *_desc; + unsigned long flags; + + re_chan = container_of(chan, struct fsl_re_chan, chan); + + spin_lock_irqsave(&re_chan->desc_lock, flags); + avail = FSL_RE_SLOT_AVAIL( + in_be32(&re_chan->jrregs->inbring_slot_avail)); + + list_for_each_entry_safe(desc, _desc, &re_chan->submit_q, node) { + if (!avail) + break; + + list_move_tail(&desc->node, &re_chan->active_q); + + memcpy(&re_chan->inb_ring_virt_addr[re_chan->inb_count], + &desc->hwdesc, sizeof(struct fsl_re_hw_desc)); + + re_chan->inb_count = (re_chan->inb_count + 1) & + FSL_RE_RING_SIZE_MASK; + out_be32(&re_chan->jrregs->inbring_add_job, FSL_RE_ADD_JOB(1)); + avail--; + } + spin_unlock_irqrestore(&re_chan->desc_lock, flags); +} + +static void fsl_re_desc_done(struct fsl_re_desc *desc) +{ + dma_async_tx_callback callback; + void *callback_param; + + dma_cookie_complete(&desc->async_tx); + + callback = desc->async_tx.callback; + callback_param = desc->async_tx.callback_param; + if (callback) + callback(callback_param); + + dma_descriptor_unmap(&desc->async_tx); +} + +static void fsl_re_cleanup_descs(struct fsl_re_chan *re_chan) +{ + struct fsl_re_desc *desc, *_desc; + unsigned long flags; + + spin_lock_irqsave(&re_chan->desc_lock, flags); + list_for_each_entry_safe(desc, _desc, &re_chan->ack_q, node) { + if (async_tx_test_ack(&desc->async_tx)) + list_move_tail(&desc->node, &re_chan->free_q); + } + spin_unlock_irqrestore(&re_chan->desc_lock, flags); + + fsl_re_issue_pending(&re_chan->chan); +} + +static void fsl_re_dequeue(unsigned long data) +{ + struct fsl_re_chan *re_chan; + struct fsl_re_desc *desc, *_desc; + struct fsl_re_hw_desc *hwdesc; + unsigned long flags; + unsigned int count, oub_count; + int found; + + re_chan = dev_get_drvdata((struct device *)data); + + fsl_re_cleanup_descs(re_chan); + + spin_lock_irqsave(&re_chan->desc_lock, flags); + count = FSL_RE_SLOT_FULL(in_be32(&re_chan->jrregs->oubring_slot_full)); + while (count--) { + found = 0; + hwdesc = &re_chan->oub_ring_virt_addr[re_chan->oub_count]; + list_for_each_entry_safe(desc, _desc, &re_chan->active_q, + node) { + /* compare the hw dma addr to find the completed */ + if (desc->hwdesc.lbea32 == hwdesc->lbea32 && + desc->hwdesc.addr_low == hwdesc->addr_low) { + found = 1; + break; + } + } + + if (found) { + fsl_re_desc_done(desc); + list_move_tail(&desc->node, &re_chan->ack_q); + } else { + dev_err(re_chan->dev, + "found hwdesc not in sw queue, discard it\n"); + } + + oub_count = (re_chan->oub_count + 1) & FSL_RE_RING_SIZE_MASK; + re_chan->oub_count = oub_count; + + out_be32(&re_chan->jrregs->oubring_job_rmvd, + FSL_RE_RMVD_JOB(1)); + } + spin_unlock_irqrestore(&re_chan->desc_lock, flags); +} + +/* Per Job Ring interrupt handler */ +static irqreturn_t fsl_re_isr(int irq, void *data) +{ + struct fsl_re_chan *re_chan; + u32 irqstate, status; + + re_chan = dev_get_drvdata((struct device *)data); + + irqstate = in_be32(&re_chan->jrregs->jr_interrupt_status); + if (!irqstate) + return IRQ_NONE; + + /* + * There's no way in upper layer (read MD layer) to recover from + * error conditions except restart everything. In long term we + * need to do something more than just crashing + */ + if (irqstate & FSL_RE_ERROR) { + status = in_be32(&re_chan->jrregs->jr_status); + dev_err(re_chan->dev, "chan error irqstate: %x, status: %x\n", + irqstate, status); + } + + /* Clear interrupt */ + out_be32(&re_chan->jrregs->jr_interrupt_status, FSL_RE_CLR_INTR); + + tasklet_schedule(&re_chan->irqtask); + + return IRQ_HANDLED; +} + +static enum dma_status fsl_re_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + return dma_cookie_status(chan, cookie, txstate); +} + +static void fill_cfd_frame(struct fsl_re_cmpnd_frame *cf, u8 index, + size_t length, dma_addr_t addr, bool final) +{ + u32 efrl = length & FSL_RE_CF_LENGTH_MASK; + + efrl |= final << FSL_RE_CF_FINAL_SHIFT; + cf[index].efrl32 = efrl; + cf[index].addr_high = upper_32_bits(addr); + cf[index].addr_low = lower_32_bits(addr); +} + +static struct fsl_re_desc *fsl_re_init_desc(struct fsl_re_chan *re_chan, + struct fsl_re_desc *desc, + void *cf, dma_addr_t paddr) +{ + desc->re_chan = re_chan; + desc->async_tx.tx_submit = fsl_re_tx_submit; + dma_async_tx_descriptor_init(&desc->async_tx, &re_chan->chan); + INIT_LIST_HEAD(&desc->node); + + desc->hwdesc.fmt32 = FSL_RE_FRAME_FORMAT << FSL_RE_HWDESC_FMT_SHIFT; + desc->hwdesc.lbea32 = upper_32_bits(paddr); + desc->hwdesc.addr_low = lower_32_bits(paddr); + desc->cf_addr = cf; + desc->cf_paddr = paddr; + + desc->cdb_addr = (void *)(cf + FSL_RE_CF_DESC_SIZE); + desc->cdb_paddr = paddr + FSL_RE_CF_DESC_SIZE; + + return desc; +} + +static struct fsl_re_desc *fsl_re_chan_alloc_desc(struct fsl_re_chan *re_chan, + unsigned long flags) +{ + struct fsl_re_desc *desc = NULL; + void *cf; + dma_addr_t paddr; + unsigned long lock_flag; + + fsl_re_cleanup_descs(re_chan); + + spin_lock_irqsave(&re_chan->desc_lock, lock_flag); + if (!list_empty(&re_chan->free_q)) { + /* take one desc from free_q */ + desc = list_first_entry(&re_chan->free_q, + struct fsl_re_desc, node); + list_del(&desc->node); + + desc->async_tx.flags = flags; + } + spin_unlock_irqrestore(&re_chan->desc_lock, lock_flag); + + if (!desc) { + desc = kzalloc(sizeof(*desc), GFP_NOWAIT); + if (!desc) + return NULL; + + cf = dma_pool_alloc(re_chan->re_dev->cf_desc_pool, GFP_NOWAIT, + &paddr); + if (!cf) { + kfree(desc); + return NULL; + } + + desc = fsl_re_init_desc(re_chan, desc, cf, paddr); + desc->async_tx.flags = flags; + + spin_lock_irqsave(&re_chan->desc_lock, lock_flag); + re_chan->alloc_count++; + spin_unlock_irqrestore(&re_chan->desc_lock, lock_flag); + } + + return desc; +} + +static struct dma_async_tx_descriptor *fsl_re_prep_dma_genq( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, size_t len, + unsigned long flags) +{ + struct fsl_re_chan *re_chan; + struct fsl_re_desc *desc; + struct fsl_re_xor_cdb *xor; + struct fsl_re_cmpnd_frame *cf; + u32 cdb; + unsigned int i, j; + unsigned int save_src_cnt = src_cnt; + int cont_q = 0; + + re_chan = container_of(chan, struct fsl_re_chan, chan); + if (len > FSL_RE_MAX_DATA_LEN) { + dev_err(re_chan->dev, "genq tx length %lu, max length %d\n", + len, FSL_RE_MAX_DATA_LEN); + return NULL; + } + + desc = fsl_re_chan_alloc_desc(re_chan, flags); + if (desc <= 0) + return NULL; + + if (scf && (flags & DMA_PREP_CONTINUE)) { + cont_q = 1; + src_cnt += 1; + } + + /* Filling xor CDB */ + cdb = FSL_RE_XOR_OPCODE << FSL_RE_CDB_OPCODE_SHIFT; + cdb |= (src_cnt - 1) << FSL_RE_CDB_NRCS_SHIFT; + cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT; + cdb |= FSL_RE_INTR_ON_ERROR << FSL_RE_CDB_ERROR_SHIFT; + cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT; + xor = desc->cdb_addr; + xor->cdb32 = cdb; + + if (scf) { + /* compute q = src0*coef0^src1*coef1^..., * is GF(8) mult */ + for (i = 0; i < save_src_cnt; i++) + xor->gfm[i] = scf[i]; + if (cont_q) + xor->gfm[i++] = 1; + } else { + /* compute P, that is XOR all srcs */ + for (i = 0; i < src_cnt; i++) + xor->gfm[i] = 1; + } + + /* Filling frame 0 of compound frame descriptor with CDB */ + cf = desc->cf_addr; + fill_cfd_frame(cf, 0, sizeof(*xor), desc->cdb_paddr, 0); + + /* Fill CFD's 1st frame with dest buffer */ + fill_cfd_frame(cf, 1, len, dest, 0); + + /* Fill CFD's rest of the frames with source buffers */ + for (i = 2, j = 0; j < save_src_cnt; i++, j++) + fill_cfd_frame(cf, i, len, src[j], 0); + + if (cont_q) + fill_cfd_frame(cf, i++, len, dest, 0); + + /* Setting the final bit in the last source buffer frame in CFD */ + cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT; + + return &desc->async_tx; +} + +/* + * Prep function for P parity calculation.In RAID Engine terminology, + * XOR calculation is called GenQ calculation done through GenQ command + */ +static struct dma_async_tx_descriptor *fsl_re_prep_dma_xor( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, + unsigned int src_cnt, size_t len, unsigned long flags) +{ + /* NULL let genq take all coef as 1 */ + return fsl_re_prep_dma_genq(chan, dest, src, src_cnt, NULL, len, flags); +} + +/* + * Prep function for P/Q parity calculation.In RAID Engine terminology, + * P/Q calculation is called GenQQ done through GenQQ command + */ +static struct dma_async_tx_descriptor *fsl_re_prep_dma_pq( + struct dma_chan *chan, dma_addr_t *dest, dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, size_t len, + unsigned long flags) +{ + struct fsl_re_chan *re_chan; + struct fsl_re_desc *desc; + struct fsl_re_pq_cdb *pq; + struct fsl_re_cmpnd_frame *cf; + u32 cdb; + u8 *p; + int gfmq_len, i, j; + unsigned int save_src_cnt = src_cnt; + + re_chan = container_of(chan, struct fsl_re_chan, chan); + if (len > FSL_RE_MAX_DATA_LEN) { + dev_err(re_chan->dev, "pq tx length is %lu, max length is %d\n", + len, FSL_RE_MAX_DATA_LEN); + return NULL; + } + + /* + * RE requires at least 2 sources, if given only one source, we pass the + * second source same as the first one. + * With only one source, generating P is meaningless, only generate Q. + */ + if (src_cnt == 1) { + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_src[2]; + unsigned char coef[2]; + + dma_src[0] = *src; + coef[0] = *scf; + dma_src[1] = *src; + coef[1] = 0; + tx = fsl_re_prep_dma_genq(chan, dest[1], dma_src, 2, coef, len, + flags); + if (tx) + desc = to_fsl_re_dma_desc(tx); + + return tx; + } + + /* + * During RAID6 array creation, Linux's MD layer gets P and Q + * calculated separately in two steps. But our RAID Engine has + * the capability to calculate both P and Q with a single command + * Hence to merge well with MD layer, we need to provide a hook + * here and call re_jq_prep_dma_genq() function + */ + + if (flags & DMA_PREP_PQ_DISABLE_P) + return fsl_re_prep_dma_genq(chan, dest[1], src, src_cnt, + scf, len, flags); + + if (flags & DMA_PREP_CONTINUE) + src_cnt += 3; + + desc = fsl_re_chan_alloc_desc(re_chan, flags); + if (desc <= 0) + return NULL; + + /* Filling GenQQ CDB */ + cdb = FSL_RE_PQ_OPCODE << FSL_RE_CDB_OPCODE_SHIFT; + cdb |= (src_cnt - 1) << FSL_RE_CDB_NRCS_SHIFT; + cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT; + cdb |= FSL_RE_BUFFER_OUTPUT << FSL_RE_CDB_BUFFER_SHIFT; + cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT; + + pq = desc->cdb_addr; + pq->cdb32 = cdb; + + p = pq->gfm_q1; + /* Init gfm_q1[] */ + for (i = 0; i < src_cnt; i++) + p[i] = 1; + + /* Align gfm[] to 32bit */ + gfmq_len = ALIGN(src_cnt, 4); + + /* Init gfm_q2[] */ + p += gfmq_len; + for (i = 0; i < src_cnt; i++) + p[i] = scf[i]; + + /* Filling frame 0 of compound frame descriptor with CDB */ + cf = desc->cf_addr; + fill_cfd_frame(cf, 0, sizeof(struct fsl_re_pq_cdb), desc->cdb_paddr, 0); + + /* Fill CFD's 1st & 2nd frame with dest buffers */ + for (i = 1, j = 0; i < 3; i++, j++) + fill_cfd_frame(cf, i, len, dest[j], 0); + + /* Fill CFD's rest of the frames with source buffers */ + for (i = 3, j = 0; j < save_src_cnt; i++, j++) + fill_cfd_frame(cf, i, len, src[j], 0); + + /* PQ computation continuation */ + if (flags & DMA_PREP_CONTINUE) { + if (src_cnt - save_src_cnt == 3) { + p[save_src_cnt] = 0; + p[save_src_cnt + 1] = 0; + p[save_src_cnt + 2] = 1; + fill_cfd_frame(cf, i++, len, dest[0], 0); + fill_cfd_frame(cf, i++, len, dest[1], 0); + fill_cfd_frame(cf, i++, len, dest[1], 0); + } else { + dev_err(re_chan->dev, "PQ tx continuation error!\n"); + return NULL; + } + } + + /* Setting the final bit in the last source buffer frame in CFD */ + cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT; + + return &desc->async_tx; +} + +/* + * Prep function for memcpy. In RAID Engine, memcpy is done through MOVE + * command. Logic of this function will need to be modified once multipage + * support is added in Linux's MD/ASYNC Layer + */ +static struct dma_async_tx_descriptor *fsl_re_prep_dma_memcpy( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct fsl_re_chan *re_chan; + struct fsl_re_desc *desc; + size_t length; + struct fsl_re_cmpnd_frame *cf; + struct fsl_re_move_cdb *move; + u32 cdb; + + re_chan = container_of(chan, struct fsl_re_chan, chan); + + if (len > FSL_RE_MAX_DATA_LEN) { + dev_err(re_chan->dev, "cp tx length is %lu, max length is %d\n", + len, FSL_RE_MAX_DATA_LEN); + return NULL; + } + + desc = fsl_re_chan_alloc_desc(re_chan, flags); + if (desc <= 0) + return NULL; + + /* Filling move CDB */ + cdb = FSL_RE_MOVE_OPCODE << FSL_RE_CDB_OPCODE_SHIFT; + cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT; + cdb |= FSL_RE_INTR_ON_ERROR << FSL_RE_CDB_ERROR_SHIFT; + cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT; + + move = desc->cdb_addr; + move->cdb32 = cdb; + + /* Filling frame 0 of CFD with move CDB */ + cf = desc->cf_addr; + fill_cfd_frame(cf, 0, sizeof(*move), desc->cdb_paddr, 0); + + length = min_t(size_t, len, FSL_RE_MAX_DATA_LEN); + + /* Fill CFD's 1st frame with dest buffer */ + fill_cfd_frame(cf, 1, length, dest, 0); + + /* Fill CFD's 2nd frame with src buffer */ + fill_cfd_frame(cf, 2, length, src, 1); + + return &desc->async_tx; +} + +static int fsl_re_alloc_chan_resources(struct dma_chan *chan) +{ + struct fsl_re_chan *re_chan; + struct fsl_re_desc *desc; + void *cf; + dma_addr_t paddr; + int i; + + re_chan = container_of(chan, struct fsl_re_chan, chan); + for (i = 0; i < FSL_RE_MIN_DESCS; i++) { + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + break; + + cf = dma_pool_alloc(re_chan->re_dev->cf_desc_pool, GFP_KERNEL, + &paddr); + if (!cf) { + kfree(desc); + break; + } + + INIT_LIST_HEAD(&desc->node); + fsl_re_init_desc(re_chan, desc, cf, paddr); + + list_add_tail(&desc->node, &re_chan->free_q); + re_chan->alloc_count++; + } + return re_chan->alloc_count; +} + +static void fsl_re_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_re_chan *re_chan; + struct fsl_re_desc *desc; + + re_chan = container_of(chan, struct fsl_re_chan, chan); + while (re_chan->alloc_count--) { + desc = list_first_entry(&re_chan->free_q, + struct fsl_re_desc, + node); + + list_del(&desc->node); + dma_pool_free(re_chan->re_dev->cf_desc_pool, desc->cf_addr, + desc->cf_paddr); + kfree(desc); + } + + if (!list_empty(&re_chan->free_q)) + dev_err(re_chan->dev, "chan resource cannot be cleaned!\n"); +} + +int fsl_re_chan_probe(struct platform_device *ofdev, + struct device_node *np, u8 q, u32 off) +{ + struct device *dev, *chandev; + struct fsl_re_drv_private *re_priv; + struct fsl_re_chan *chan; + struct dma_device *dma_dev; + u32 ptr; + u32 status; + int ret = 0, rc; + struct platform_device *chan_ofdev; + + dev = &ofdev->dev; + re_priv = dev_get_drvdata(dev); + dma_dev = &re_priv->dma_dev; + + chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + /* create platform device for chan node */ + chan_ofdev = of_platform_device_create(np, NULL, dev); + if (!chan_ofdev) { + dev_err(dev, "Not able to create ofdev for jr %d\n", q); + ret = -EINVAL; + goto err_free; + } + + /* read reg property from dts */ + rc = of_property_read_u32(np, "reg", &ptr); + if (rc) { + dev_err(dev, "Reg property not found in jr %d\n", q); + ret = -ENODEV; + goto err_free; + } + + chan->jrregs = (struct fsl_re_chan_cfg *)((u8 *)re_priv->re_regs + + off + ptr); + + /* read irq property from dts */ + chan->irq = irq_of_parse_and_map(np, 0); + if (chan->irq == NO_IRQ) { + dev_err(dev, "No IRQ defined for JR %d\n", q); + ret = -ENODEV; + goto err_free; + } + + snprintf(chan->name, sizeof(chan->name), "re_jr%02d", q); + + chandev = &chan_ofdev->dev; + tasklet_init(&chan->irqtask, fsl_re_dequeue, (unsigned long)chandev); + + ret = request_irq(chan->irq, fsl_re_isr, 0, chan->name, chandev); + if (ret) { + dev_err(dev, "Unable to register interrupt for JR %d\n", q); + ret = -EINVAL; + goto err_free; + } + + re_priv->re_jrs[q] = chan; + chan->chan.device = dma_dev; + chan->chan.private = chan; + chan->dev = chandev; + chan->re_dev = re_priv; + + spin_lock_init(&chan->desc_lock); + INIT_LIST_HEAD(&chan->ack_q); + INIT_LIST_HEAD(&chan->active_q); + INIT_LIST_HEAD(&chan->submit_q); + INIT_LIST_HEAD(&chan->free_q); + + chan->inb_ring_virt_addr = dma_pool_alloc(chan->re_dev->hw_desc_pool, + GFP_KERNEL, &chan->inb_phys_addr); + if (!chan->inb_ring_virt_addr) { + dev_err(dev, "No dma memory for inb_ring_virt_addr\n"); + ret = -ENOMEM; + goto err_free; + } + + chan->oub_ring_virt_addr = dma_pool_alloc(chan->re_dev->hw_desc_pool, + GFP_KERNEL, &chan->oub_phys_addr); + if (!chan->oub_ring_virt_addr) { + dev_err(dev, "No dma memory for oub_ring_virt_addr\n"); + ret = -ENOMEM; + goto err_free_1; + } + + /* Program the Inbound/Outbound ring base addresses and size */ + out_be32(&chan->jrregs->inbring_base_h, + chan->inb_phys_addr & FSL_RE_ADDR_BIT_MASK); + out_be32(&chan->jrregs->oubring_base_h, + chan->oub_phys_addr & FSL_RE_ADDR_BIT_MASK); + out_be32(&chan->jrregs->inbring_base_l, + chan->inb_phys_addr >> FSL_RE_ADDR_BIT_SHIFT); + out_be32(&chan->jrregs->oubring_base_l, + chan->oub_phys_addr >> FSL_RE_ADDR_BIT_SHIFT); + out_be32(&chan->jrregs->inbring_size, + FSL_RE_RING_SIZE << FSL_RE_RING_SIZE_SHIFT); + out_be32(&chan->jrregs->oubring_size, + FSL_RE_RING_SIZE << FSL_RE_RING_SIZE_SHIFT); + + /* Read LIODN value from u-boot */ + status = in_be32(&chan->jrregs->jr_config_1) & FSL_RE_REG_LIODN_MASK; + + /* Program the CFG reg */ + out_be32(&chan->jrregs->jr_config_1, + FSL_RE_CFG1_CBSI | FSL_RE_CFG1_CBS0 | status); + + dev_set_drvdata(chandev, chan); + + /* Enable RE/CHAN */ + out_be32(&chan->jrregs->jr_command, FSL_RE_ENABLE); + + return 0; + +err_free_1: + dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr, + chan->inb_phys_addr); +err_free: + return ret; +} + +/* Probe function for RAID Engine */ +static int fsl_re_probe(struct platform_device *ofdev) +{ + struct fsl_re_drv_private *re_priv; + struct device_node *np; + struct device_node *child; + u32 off; + u8 ridx = 0; + struct dma_device *dma_dev; + struct resource *res; + int rc; + struct device *dev = &ofdev->dev; + + re_priv = devm_kzalloc(dev, sizeof(*re_priv), GFP_KERNEL); + if (!re_priv) + return -ENOMEM; + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + /* IOMAP the entire RAID Engine region */ + re_priv->re_regs = devm_ioremap(dev, res->start, resource_size(res)); + if (!re_priv->re_regs) + return -EBUSY; + + /* Program the RE mode */ + out_be32(&re_priv->re_regs->global_config, FSL_RE_NON_DPAA_MODE); + + /* Program Galois Field polynomial */ + out_be32(&re_priv->re_regs->galois_field_config, FSL_RE_GFM_POLY); + + dev_info(dev, "version %x, mode %x, gfp %x\n", + in_be32(&re_priv->re_regs->re_version_id), + in_be32(&re_priv->re_regs->global_config), + in_be32(&re_priv->re_regs->galois_field_config)); + + dma_dev = &re_priv->dma_dev; + dma_dev->dev = dev; + INIT_LIST_HEAD(&dma_dev->channels); + dma_set_mask(dev, DMA_BIT_MASK(40)); + + dma_dev->device_alloc_chan_resources = fsl_re_alloc_chan_resources; + dma_dev->device_tx_status = fsl_re_tx_status; + dma_dev->device_issue_pending = fsl_re_issue_pending; + + dma_dev->max_xor = FSL_RE_MAX_XOR_SRCS; + dma_dev->device_prep_dma_xor = fsl_re_prep_dma_xor; + dma_cap_set(DMA_XOR, dma_dev->cap_mask); + + dma_dev->max_pq = FSL_RE_MAX_PQ_SRCS; + dma_dev->device_prep_dma_pq = fsl_re_prep_dma_pq; + dma_cap_set(DMA_PQ, dma_dev->cap_mask); + + dma_dev->device_prep_dma_memcpy = fsl_re_prep_dma_memcpy; + dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); + + dma_dev->device_free_chan_resources = fsl_re_free_chan_resources; + + re_priv->total_chans = 0; + + re_priv->cf_desc_pool = dmam_pool_create("fsl_re_cf_desc_pool", dev, + FSL_RE_CF_CDB_SIZE, + FSL_RE_CF_CDB_ALIGN, 0); + + if (!re_priv->cf_desc_pool) { + dev_err(dev, "No memory for fsl re_cf desc pool\n"); + return -ENOMEM; + } + + re_priv->hw_desc_pool = dmam_pool_create("fsl_re_hw_desc_pool", dev, + sizeof(struct fsl_re_hw_desc) * FSL_RE_RING_SIZE, + FSL_RE_FRAME_ALIGN, 0); + if (!re_priv->hw_desc_pool) { + dev_err(dev, "No memory for fsl re_hw desc pool\n"); + return -ENOMEM; + } + + dev_set_drvdata(dev, re_priv); + + /* Parse Device tree to find out the total number of JQs present */ + for_each_compatible_node(np, NULL, "fsl,raideng-v1.0-job-queue") { + rc = of_property_read_u32(np, "reg", &off); + if (rc) { + dev_err(dev, "Reg property not found in JQ node\n"); + return -ENODEV; + } + /* Find out the Job Rings present under each JQ */ + for_each_child_of_node(np, child) { + rc = of_device_is_compatible(child, + "fsl,raideng-v1.0-job-ring"); + if (rc) { + fsl_re_chan_probe(ofdev, child, ridx++, off); + re_priv->total_chans++; + } + } + } + + dma_async_device_register(dma_dev); + + return 0; +} + +static void fsl_re_remove_chan(struct fsl_re_chan *chan) +{ + dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr, + chan->inb_phys_addr); + + dma_pool_free(chan->re_dev->hw_desc_pool, chan->oub_ring_virt_addr, + chan->oub_phys_addr); +} + +static int fsl_re_remove(struct platform_device *ofdev) +{ + struct fsl_re_drv_private *re_priv; + struct device *dev; + int i; + + dev = &ofdev->dev; + re_priv = dev_get_drvdata(dev); + + /* Cleanup chan related memory areas */ + for (i = 0; i < re_priv->total_chans; i++) + fsl_re_remove_chan(re_priv->re_jrs[i]); + + /* Unregister the driver */ + dma_async_device_unregister(&re_priv->dma_dev); + + return 0; +} + +static struct of_device_id fsl_re_ids[] = { + { .compatible = "fsl,raideng-v1.0", }, + {} +}; + +static struct platform_driver fsl_re_driver = { + .driver = { + .name = "fsl-raideng", + .owner = THIS_MODULE, + .of_match_table = fsl_re_ids, + }, + .probe = fsl_re_probe, + .remove = fsl_re_remove, +}; + +module_platform_driver(fsl_re_driver); + +MODULE_AUTHOR("Harninder Rai "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Freescale RAID Engine Device Driver"); diff --git a/drivers/dma/fsl_raid.h b/drivers/dma/fsl_raid.h new file mode 100644 index 0000000..69d743c --- /dev/null +++ b/drivers/dma/fsl_raid.h @@ -0,0 +1,306 @@ +/* + * drivers/dma/fsl_raid.h + * + * Freescale RAID Engine device driver + * + * Author: + * Harninder Rai + * Naveen Burmi + * + * Rewrite: + * Xuelin Shi + + * Copyright (c) 2010-2012 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define FSL_RE_MAX_CHANS 4 +#define FSL_RE_DPAA_MODE BIT(30) +#define FSL_RE_NON_DPAA_MODE BIT(31) +#define FSL_RE_GFM_POLY 0x1d000000 +#define FSL_RE_ADD_JOB(x) ((x) << 16) +#define FSL_RE_RMVD_JOB(x) ((x) << 16) +#define FSL_RE_CFG1_CBSI 0x08000000 +#define FSL_RE_CFG1_CBS0 0x00080000 +#define FSL_RE_SLOT_FULL_SHIFT 8 +#define FSL_RE_SLOT_FULL(x) ((x) >> FSL_RE_SLOT_FULL_SHIFT) +#define FSL_RE_SLOT_AVAIL_SHIFT 8 +#define FSL_RE_SLOT_AVAIL(x) ((x) >> FSL_RE_SLOT_AVAIL_SHIFT) +#define FSL_RE_PQ_OPCODE 0x1B +#define FSL_RE_XOR_OPCODE 0x1A +#define FSL_RE_MOVE_OPCODE 0x8 +#define FSL_RE_FRAME_ALIGN 16 +#define FSL_RE_BLOCK_SIZE 0x3 /* 4096 bytes */ +#define FSL_RE_CACHEABLE_IO 0x0 +#define FSL_RE_BUFFER_OUTPUT 0x0 +#define FSL_RE_INTR_ON_ERROR 0x1 +#define FSL_RE_DATA_DEP 0x1 +#define FSL_RE_ENABLE_DPI 0x0 +#define FSL_RE_RING_SIZE 0x400 +#define FSL_RE_RING_SIZE_MASK (FSL_RE_RING_SIZE - 1) +#define FSL_RE_RING_SIZE_SHIFT 8 +#define FSL_RE_ADDR_BIT_SHIFT 4 +#define FSL_RE_ADDR_BIT_MASK (BIT(FSL_RE_ADDR_BIT_SHIFT) - 1) +#define FSL_RE_ERROR 0x40000000 +#define FSL_RE_INTR 0x80000000 +#define FSL_RE_CLR_INTR 0x80000000 +#define FSL_RE_PAUSE 0x80000000 +#define FSL_RE_ENABLE 0x80000000 +#define FSL_RE_REG_LIODN_MASK 0x00000FFF + +#define FSL_RE_CDB_OPCODE_MASK 0xF8000000 +#define FSL_RE_CDB_OPCODE_SHIFT 27 +#define FSL_RE_CDB_EXCLEN_MASK 0x03000000 +#define FSL_RE_CDB_EXCLEN_SHIFT 24 +#define FSL_RE_CDB_EXCLQ1_MASK 0x00F00000 +#define FSL_RE_CDB_EXCLQ1_SHIFT 20 +#define FSL_RE_CDB_EXCLQ2_MASK 0x000F0000 +#define FSL_RE_CDB_EXCLQ2_SHIFT 16 +#define FSL_RE_CDB_BLKSIZE_MASK 0x0000C000 +#define FSL_RE_CDB_BLKSIZE_SHIFT 14 +#define FSL_RE_CDB_CACHE_MASK 0x00003000 +#define FSL_RE_CDB_CACHE_SHIFT 12 +#define FSL_RE_CDB_BUFFER_MASK 0x00000800 +#define FSL_RE_CDB_BUFFER_SHIFT 11 +#define FSL_RE_CDB_ERROR_MASK 0x00000400 +#define FSL_RE_CDB_ERROR_SHIFT 10 +#define FSL_RE_CDB_NRCS_MASK 0x0000003C +#define FSL_RE_CDB_NRCS_SHIFT 6 +#define FSL_RE_CDB_DEPEND_MASK 0x00000008 +#define FSL_RE_CDB_DEPEND_SHIFT 3 +#define FSL_RE_CDB_DPI_MASK 0x00000004 +#define FSL_RE_CDB_DPI_SHIFT 2 + +/* + * the largest cf block is 19*sizeof(struct cmpnd_frame), which is 304 bytes. + * here 19 = 1(cdb)+2(dest)+16(src), align to 64bytes, that is 320 bytes. + * the largest cdb block: struct pq_cdb which is 180 bytes, adding to cf block + * 320+180=500, align to 64bytes, that is 512 bytes. + */ +#define FSL_RE_CF_DESC_SIZE 320 +#define FSL_RE_CF_CDB_SIZE 512 +#define FSL_RE_CF_CDB_ALIGN 64 + +struct fsl_re_ctrl { + /* General Configuration Registers */ + __be32 global_config; /* Global Configuration Register */ + u8 rsvd1[4]; + __be32 galois_field_config; /* Galois Field Configuration Register */ + u8 rsvd2[4]; + __be32 jq_wrr_config; /* WRR Configuration register */ + u8 rsvd3[4]; + __be32 crc_config; /* CRC Configuration register */ + u8 rsvd4[228]; + __be32 system_reset; /* System Reset Register */ + u8 rsvd5[252]; + __be32 global_status; /* Global Status Register */ + u8 rsvd6[832]; + __be32 re_liodn_base; /* LIODN Base Register */ + u8 rsvd7[1712]; + __be32 re_version_id; /* Version ID register of RE */ + __be32 re_version_id_2; /* Version ID 2 register of RE */ + u8 rsvd8[512]; + __be32 host_config; /* Host I/F Configuration Register */ +}; + +struct fsl_re_chan_cfg { + /* Registers for JR interface */ + __be32 jr_config_0; /* Job Queue Configuration 0 Register */ + __be32 jr_config_1; /* Job Queue Configuration 1 Register */ + __be32 jr_interrupt_status; /* Job Queue Interrupt Status Register */ + u8 rsvd1[4]; + __be32 jr_command; /* Job Queue Command Register */ + u8 rsvd2[4]; + __be32 jr_status; /* Job Queue Status Register */ + u8 rsvd3[228]; + + /* Input Ring */ + __be32 inbring_base_h; /* Inbound Ring Base Address Register - High */ + __be32 inbring_base_l; /* Inbound Ring Base Address Register - Low */ + __be32 inbring_size; /* Inbound Ring Size Register */ + u8 rsvd4[4]; + __be32 inbring_slot_avail; /* Inbound Ring Slot Available Register */ + u8 rsvd5[4]; + __be32 inbring_add_job; /* Inbound Ring Add Job Register */ + u8 rsvd6[4]; + __be32 inbring_cnsmr_indx; /* Inbound Ring Consumer Index Register */ + u8 rsvd7[220]; + + /* Output Ring */ + __be32 oubring_base_h; /* Outbound Ring Base Address Register - High */ + __be32 oubring_base_l; /* Outbound Ring Base Address Register - Low */ + __be32 oubring_size; /* Outbound Ring Size Register */ + u8 rsvd8[4]; + __be32 oubring_job_rmvd; /* Outbound Ring Job Removed Register */ + u8 rsvd9[4]; + __be32 oubring_slot_full; /* Outbound Ring Slot Full Register */ + u8 rsvd10[4]; + __be32 oubring_prdcr_indx; /* Outbound Ring Producer Index */ +}; + +/* + * Command Descriptor Block (CDB) for unicast move command. + * In RAID Engine terms, memcpy is done through move command + */ +struct fsl_re_move_cdb { + __be32 cdb32; +}; + +/* Data protection/integrity related fields */ +#define FSL_RE_DPI_APPS_MASK 0xC0000000 +#define FSL_RE_DPI_APPS_SHIFT 30 +#define FSL_RE_DPI_REF_MASK 0x30000000 +#define FSL_RE_DPI_REF_SHIFT 28 +#define FSL_RE_DPI_GUARD_MASK 0x0C000000 +#define FSL_RE_DPI_GUARD_SHIFT 26 +#define FSL_RE_DPI_ATTR_MASK 0x03000000 +#define FSL_RE_DPI_ATTR_SHIFT 24 +#define FSL_RE_DPI_META_MASK 0x0000FFFF + +struct fsl_re_dpi { + __be32 dpi32; + __be32 ref; +}; + +/* + * CDB for GenQ command. In RAID Engine terminology, XOR is + * done through this command + */ +struct fsl_re_xor_cdb { + __be32 cdb32; + u8 gfm[16]; + struct fsl_re_dpi dpi_dest_spec; + struct fsl_re_dpi dpi_src_spec[16]; +}; + +/* CDB for no-op command */ +struct fsl_re_noop_cdb { + __be32 cdb32; +}; + +/* + * CDB for GenQQ command. In RAID Engine terminology, P/Q is + * done through this command + */ +struct fsl_re_pq_cdb { + __be32 cdb32; + u8 gfm_q1[16]; + u8 gfm_q2[16]; + struct fsl_re_dpi dpi_dest_spec[2]; + struct fsl_re_dpi dpi_src_spec[16]; +}; + +/* Compound frame */ +#define FSL_RE_CF_ADDR_HIGH_MASK 0x000000FF +#define FSL_RE_CF_EXT_MASK 0x80000000 +#define FSL_RE_CF_EXT_SHIFT 31 +#define FSL_RE_CF_FINAL_MASK 0x40000000 +#define FSL_RE_CF_FINAL_SHIFT 30 +#define FSL_RE_CF_LENGTH_MASK 0x000FFFFF +#define FSL_RE_CF_BPID_MASK 0x00FF0000 +#define FSL_RE_CF_BPID_SHIFT 16 +#define FSL_RE_CF_OFFSET_MASK 0x00001FFF + +struct fsl_re_cmpnd_frame { + __be32 addr_high; + __be32 addr_low; + __be32 efrl32; + __be32 rbro32; +}; + +/* Frame descriptor */ +#define FSL_RE_HWDESC_LIODN_MASK 0x3F000000 +#define FSL_RE_HWDESC_LIODN_SHIFT 24 +#define FSL_RE_HWDESC_BPID_MASK 0x00FF0000 +#define FSL_RE_HWDESC_BPID_SHIFT 16 +#define FSL_RE_HWDESC_ELIODN_MASK 0x0000F000 +#define FSL_RE_HWDESC_ELIODN_SHIFT 12 +#define FSL_RE_HWDESC_FMT_SHIFT 29 +#define FSL_RE_HWDESC_FMT_MASK (0x3 << FSL_RE_HWDESC_FMT_SHIFT) + +struct fsl_re_hw_desc { + __be32 lbea32; + __be32 addr_low; + __be32 fmt32; + __be32 status; +}; + +/* Raid Engine device private data */ +struct fsl_re_drv_private { + u8 total_chans; + struct dma_device dma_dev; + struct fsl_re_ctrl *re_regs; + struct fsl_re_chan *re_jrs[FSL_RE_MAX_CHANS]; + struct dma_pool *cf_desc_pool; + struct dma_pool *hw_desc_pool; +}; + +/* Per job ring data structure */ +struct fsl_re_chan { + char name[16]; + spinlock_t desc_lock; /* queue lock */ + struct list_head ack_q; /* wait to acked queue */ + struct list_head active_q; /* already issued on hw, not completed */ + struct list_head submit_q; + struct list_head free_q; /* alloc available queue */ + struct device *dev; + struct fsl_re_drv_private *re_dev; + struct dma_chan chan; + struct fsl_re_chan_cfg *jrregs; + int irq; + struct tasklet_struct irqtask; + u32 alloc_count; + + /* hw descriptor ring for inbound queue*/ + dma_addr_t inb_phys_addr; + struct fsl_re_hw_desc *inb_ring_virt_addr; + u32 inb_count; + + /* hw descriptor ring for outbound queue */ + dma_addr_t oub_phys_addr; + struct fsl_re_hw_desc *oub_ring_virt_addr; + u32 oub_count; +}; + +/* Async transaction descriptor */ +struct fsl_re_desc { + struct dma_async_tx_descriptor async_tx; + struct list_head node; + struct fsl_re_hw_desc hwdesc; + struct fsl_re_chan *re_chan; + + /* hwdesc will point to cf_addr */ + void *cf_addr; + dma_addr_t cf_paddr; + + void *cdb_addr; + dma_addr_t cdb_paddr; + int status; +}; -- cgit v0.10.2 From 453dcdb5ba048011d823cd31710f78ce2fd57da3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 2 Apr 2015 16:11:35 +0530 Subject: dmaengine: fsl_raid: statify fsl_re_chan_probe Function fsl_re_chan_probe should be declared static, so do it Signed-off-by: Vinod Koul diff --git a/drivers/dma/fsl_raid.c b/drivers/dma/fsl_raid.c index 12778bd..4d9470f 100644 --- a/drivers/dma/fsl_raid.c +++ b/drivers/dma/fsl_raid.c @@ -629,7 +629,7 @@ static void fsl_re_free_chan_resources(struct dma_chan *chan) dev_err(re_chan->dev, "chan resource cannot be cleaned!\n"); } -int fsl_re_chan_probe(struct platform_device *ofdev, +static int fsl_re_chan_probe(struct platform_device *ofdev, struct device_node *np, u8 q, u32 off) { struct device *dev, *chandev; -- cgit v0.10.2 From e60841b441e1b3d176f235f36c79158dd2ed3ed8 Mon Sep 17 00:00:00 2001 From: Kedareswara rao Appana Date: Mon, 30 Mar 2015 18:48:29 +0530 Subject: dmaengine: vdma: Fix compilation warnings This patch fixes the following compilation warnings. In file included from drivers/dma/xilinx/xilinx_vdma.c:26:0: include/linux/dmapool.h:18:4: warning: 'struct device' declared inside parameter list size_t size, size_t align, size_t allocation); ^ include/linux/dmapool.h:18:4: warning: its scope is only this definition or declaration, which is probably not what you want include/linux/dmapool.h:31:7: warning: 'struct device' declared inside parameter list size_t size, size_t align, size_t allocation); ^ drivers/dma/xilinx/xilinx_vdma.c: In function 'xilinx_vdma_alloc_chan_resources': drivers/dma/xilinx/xilinx_vdma.c:501:20: warning: passing argument 2 of 'dma_pool_create' from incompatible pointer type chan->desc_pool = dma_pool_create("xilinx_vdma_desc_pool", ^ In file included from drivers/dma/xilinx/xilinx_vdma.c:26:0: include/linux/dmapool.h:17:18: note: expected 'struct device *' but argument is of type 'struct device *' struct dma_pool *dma_pool_create(const char *name, struct device *dev, . Signed-off-by: Kedareswara rao Appana Signed-off-by: Vinod Koul diff --git a/include/linux/dmapool.h b/include/linux/dmapool.h index 022e34f..52456aa 100644 --- a/include/linux/dmapool.h +++ b/include/linux/dmapool.h @@ -14,6 +14,8 @@ #include #include +struct device; + struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation); -- cgit v0.10.2 From 28591dfdd96c6289aebbf12d8eb4e5dc5ddbde97 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 2 Apr 2015 18:39:31 +0300 Subject: dmaengine: sa11x0: report slave capabilities to upper layers Fix the following warning by initializing necessary fields in the dma_device structure. ------------[ cut here ]------------ WARNING: CPU: 0 PID: 1 at drivers/dma/dmaengine.c:863 dma_async_device_register+0x2b4/0x4f0() this driver doesn't support generic slave capabilities reporting Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 4.0.0-rc5+ #10 Hardware name: Sharp-Collie [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x74/0xac) [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) [] (warn_slowpath_fmt) from [] (dma_async_device_register+0x2b4/0x4f0) [] (dma_async_device_register) from [] (sa11x0_dma_probe+0x21c/0x358) [] (sa11x0_dma_probe) from [] (platform_drv_probe+0x30/0x94) [] (platform_drv_probe) from [] (driver_probe_device+0x84/0x234) [] (driver_probe_device) from [] (__driver_attach+0x98/0x9c) [] (__driver_attach) from [] (bus_for_each_dev+0x74/0xa4) [] (bus_for_each_dev) from [] (bus_add_driver+0x13c/0x1e8) [] (bus_add_driver) from [] (driver_register+0x78/0xf8) [] (driver_register) from [] (do_one_initcall+0x84/0x1f4) [] (do_one_initcall) from [] (kernel_init_freeable+0xf8/0x1b4) [] (kernel_init_freeable) from [] (kernel_init+0x8/0xf0) [] (kernel_init) from [] (ret_from_fork+0x14/0x2c) ---[ end trace e188b8fe0e782e75 ]--- Signed-off-by: Dmitry Eremin-Solenikov Acked-by: Russell King Signed-off-by: Vinod Koul diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index dafe13f..43db255 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -942,6 +942,12 @@ static int sa11x0_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_CYCLIC, d->slave.cap_mask); d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic; + d->slave.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + d->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + d->slave.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES); + d->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES); ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); if (ret) { dev_warn(d->slave.dev, "failed to register slave async device: %d\n", -- cgit v0.10.2 From d7d8e892aa6fe280a2e2974d9fd1ec9253ee1a05 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 3 Apr 2015 20:20:15 +0900 Subject: dmaengine: usb-dmac: Fix dereferencing freed memory 'desc' This patch fixes an issue that the usb_dmac_desc_free() is dereferencing freed memory 'desc' because it uses list_for_each_entry(). This function should use list_for_each_entry_safe(). Reported-by: Dan Carpenter Signed-off-by: Yoshihiro Shimoda Signed-off-by: Vinod Koul diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index d5dad98..f705798 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -285,13 +285,13 @@ static int usb_dmac_desc_alloc(struct usb_dmac_chan *chan, unsigned int sg_len, static void usb_dmac_desc_free(struct usb_dmac_chan *chan) { - struct usb_dmac_desc *desc; + struct usb_dmac_desc *desc, *_desc; LIST_HEAD(list); list_splice_init(&chan->desc_freed, &list); list_splice_init(&chan->desc_got, &list); - list_for_each_entry(desc, &list, node) { + list_for_each_entry_safe(desc, _desc, &list, node) { list_del(&desc->node); kfree(desc); } -- cgit v0.10.2 From ed1f041842f0c4226a78fcd96ccb8e6a5433ae9d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 9 Apr 2015 12:05:04 +0300 Subject: dmaengine: xgene: buffer overflow in xgene_dma_init_channels() We put 9 characters into the 8 character name[] array. Let's make the array bigger and change the sprintf() to snprintf(). Signed-off-by: Dan Carpenter Signed-off-by: Vinod Koul diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index aa61935..06d4042 100755 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -314,7 +314,7 @@ struct xgene_dma_chan { struct device *dev; int id; int rx_irq; - char name[8]; + char name[10]; spinlock_t lock; int pending; int max_outstanding; @@ -1876,7 +1876,7 @@ static void xgene_dma_init_channels(struct xgene_dma *pdma) chan->dev = pdma->dev; chan->pdma = pdma; chan->id = i; - sprintf(chan->name, "dmachan%d", chan->id); + snprintf(chan->name, sizeof(chan->name), "dmachan%d", chan->id); } } -- cgit v0.10.2 From 9c361b1afd9b4b2bba7fa1ffb080c9f5de3ff108 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 9 Apr 2015 12:03:31 +0300 Subject: dmaengine: xgene: devm_ioremap() returns NULL on error The code here is checking for IS_ERR() but devm_ioremap() returns NULL on error and not an error pointer. Signed-off-by: Dan Carpenter Signed-off-by: Vinod Koul diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index 06d4042..8a36946 100755 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -1895,9 +1895,9 @@ static int xgene_dma_get_resources(struct platform_device *pdev, pdma->csr_dma = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(pdma->csr_dma)) { + if (!pdma->csr_dma) { dev_err(&pdev->dev, "Failed to ioremap csr region"); - return PTR_ERR(pdma->csr_dma); + return -ENOMEM; } /* Get DMA ring csr region */ @@ -1909,9 +1909,9 @@ static int xgene_dma_get_resources(struct platform_device *pdev, pdma->csr_ring = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(pdma->csr_ring)) { + if (!pdma->csr_ring) { dev_err(&pdev->dev, "Failed to ioremap ring csr region"); - return PTR_ERR(pdma->csr_ring); + return -ENOMEM; } /* Get DMA ring cmd csr region */ @@ -1923,9 +1923,9 @@ static int xgene_dma_get_resources(struct platform_device *pdev, pdma->csr_ring_cmd = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(pdma->csr_ring_cmd)) { + if (!pdma->csr_ring_cmd) { dev_err(&pdev->dev, "Failed to ioremap ring cmd csr region"); - return PTR_ERR(pdma->csr_ring_cmd); + return -ENOMEM; } /* Get efuse csr region */ @@ -1937,9 +1937,9 @@ static int xgene_dma_get_resources(struct platform_device *pdev, pdma->csr_efuse = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(pdma->csr_efuse)) { + if (!pdma->csr_efuse) { dev_err(&pdev->dev, "Failed to ioremap efuse csr region"); - return PTR_ERR(pdma->csr_efuse); + return -ENOMEM; } /* Get DMA error interrupt */ -- cgit v0.10.2 From 0434a23198d6a271b70b75221c7568e60482c04e Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 7 Apr 2015 16:42:45 +0200 Subject: dmaengine: at_xdmac: unlock spin lock before return Signed-off-by: Niklas Cassel Acked-by: Ludovic Desroches Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 09e2825..6d18abf 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1155,8 +1155,10 @@ static int at_xdmac_device_resume(struct dma_chan *chan) dev_dbg(chan2dev(chan), "%s\n", __func__); spin_lock_bh(&atchan->lock); - if (!at_xdmac_chan_is_paused(atchan)) + if (!at_xdmac_chan_is_paused(atchan)) { + spin_unlock_bh(&atchan->lock); return 0; + } at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask); clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); -- cgit v0.10.2 From 12d7b7a23694b4d3dba58d7ae971ee9120a617e5 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 11 Apr 2015 01:28:41 +0300 Subject: dmaengine: pch_dma: fix memory leak on failure path in pch_dma_probe() Memory allocated for pch_dma is not deallocated in case of failure in pch_dma_probe(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Vinod Koul diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 35c143c..b859792d 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -949,6 +949,7 @@ err_free_res: err_disable_pdev: pci_disable_device(pdev); err_free_mem: + kfree(pd); return err; } -- cgit v0.10.2 From 11ebe4c067c7f95adff73594cb5c23f7a5c6d69e Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 12 Apr 2015 02:18:34 +0800 Subject: dmaengine: fix platform_no_drv_owner.cocci warnings drivers/dma/xgene-dma.c:2079:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Rameshwar Prasad Sahu Signed-off-by: Fengguang Wu Signed-off-by: Vinod Koul diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index 8a36946..f52e3750 100755 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -2076,7 +2076,6 @@ static struct platform_driver xgene_dma_driver = { .remove = xgene_dma_remove, .driver = { .name = "X-Gene-DMA", - .owner = THIS_MODULE, .of_match_table = xgene_dma_of_match_ptr, }, }; -- cgit v0.10.2 From 7d3beab16dd9eee86bb1a4dd05b51159fc7772f0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 11 Apr 2015 00:27:58 +0200 Subject: dmaengine: shdmac: avoid unused variable warnings This driver uses '#ifdef CONFIG_ARCH_SHMOBILE' and '#ifdef CONFIG_ARM' interchangeably in its sh_dmae_probe function, which causes a build warning when building for ARM without also enabling shmobile: dma/sh/shdmac.c: In function sh_dmae_probe: dma/sh/shdmac.c:696:6: warning: unused variable errirq [-Wunused-variable] dma/sh/shdmac.c:695:16: warning: unused variable irqflags [-Wunused-variable] dma/sh/shdmac.c: At top level: dma/sh/shdmac.c:447:20: warning: sh_dmae_err defined but not used [-Wunused-function] This changes all the #ifdef to test for CONFIG_ARCH_SHMOBILE to avoid that warning. An earlier patch from Laurent had fixed the warning for non-ARM case, but it still remained present in ARM randconfig builds. Signed-off-by: Arnd Bergmann Fixes: 52d6a5ee101bf ("DMA: shdma: Fix warnings due to declared but unused symbols") Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index b2431aa..1858259 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -443,7 +443,7 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev) return ret; } -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM) +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static irqreturn_t sh_dmae_err(int irq, void *data) { struct sh_dmae_device *shdev = data; @@ -689,7 +689,7 @@ static int sh_dmae_probe(struct platform_device *pdev) const struct sh_dmae_pdata *pdata; unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {}; int chan_irq[SH_DMAE_MAX_CHANNELS]; -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM) +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) unsigned long irqflags = 0; int errirq; #endif -- cgit v0.10.2 From cdde0e61cf2d41123604c6c9d8f09ef5c23464ee Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 22 Apr 2015 12:24:13 +0530 Subject: dmaengine: dw: don't prompt for DW_DMAC_CORE DW_DMAC_CORE is slected by PCI or Platform driver, so this symbol shouldn't be user selectable, so remove the prompt Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index dcfe964..36e02f0 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -3,7 +3,7 @@ # config DW_DMAC_CORE - tristate "Synopsys DesignWare AHB DMA support" + tristate select DMA_ENGINE config DW_DMAC -- cgit v0.10.2