[PATCH v4 01/10] dma-engine: sun4i: Add a quirk to support different chips

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,


Notes:
      Changes in v2:
      * Whitespace

   drivers/dma/sun4i-dma.c | 138 ++++++++++++++++++++++++++++++----------
   1 file changed, 106 insertions(+), 32 deletions(-)

diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c
index 2e7f9b07fdd2..d472f57a39ea 100644
--- a/drivers/dma/sun4i-dma.c
+++ b/drivers/dma/sun4i-dma.c
@@ -13,6 +13,7 @@
   #include <linux/interrupt.h>
   #include <linux/module.h>
   #include <linux/of_dma.h>
+#include <linux/of_device.h>
   #include <linux/platform_device.h>
   #include <linux/slab.h>
   #include <linux/spinlock.h>
@@ -31,6 +32,8 @@
   #define SUN4I_DMA_CFG_SRC_ADDR_MODE(mode)	((mode) << 5)
   #define SUN4I_DMA_CFG_SRC_DRQ_TYPE(type)	(type)
+#define SUN4I_MAX_BURST 8
+
   /** Normal DMA register values **/
/* Normal DMA source/destination data request type values */
@@ -132,6 +135,32 @@
   #define SUN4I_DDMA_MAX_SEG_SIZE		SZ_16M
   #define SUN4I_DMA_MAX_SEG_SIZE		SUN4I_NDMA_MAX_SEG_SIZE
+/*
+ * Hardware channels / ports representation
+ *
+ * The hardware is used in several SoCs, with differing numbers
+ * of channels and endpoints. This structure ties those numbers
+ * to a certain compatible string.
+ */
+struct sun4i_dma_config {
+	u32 ndma_nr_max_channels;
+	u32 ndma_nr_max_vchans;
+
+	u32 ddma_nr_max_channels;
+	u32 ddma_nr_max_vchans;
+
+	u32 dma_nr_max_channels;
+
+	void (*set_dst_data_width)(u32 *p_cfg, s8 data_width);
+	void (*set_src_data_width)(u32 *p_cfg, s8 data_width);
+	int (*convert_burst)(u32 maxburst);
+
+	u8 ndma_drq_sdram;
+	u8 ddma_drq_sdram;
+
+	u8 max_burst;
+};
+
   struct sun4i_dma_pchan {
   	/* Register base of channel */
   	void __iomem			*base;
@@ -170,7 +199,7 @@ struct sun4i_dma_contract {
   };
struct sun4i_dma_dev {
-	DECLARE_BITMAP(pchans_used, SUN4I_DMA_NR_MAX_CHANNELS);

Is this macro "SUN4I_DMA_NR_MAX_CHANNELS" referenced elsewhere? If it’s not in use, can we clean it up?

+	unsigned long *pchans_used;
   	struct dma_device		slave;
   	struct sun4i_dma_pchan		*pchans;
   	struct sun4i_dma_vchan		*vchans;
@@ -178,6 +207,7 @@ struct sun4i_dma_dev {
   	struct clk			*clk;
   	int				irq;
   	spinlock_t			lock;
+	const struct sun4i_dma_config *cfg;
   };
static struct sun4i_dma_dev *to_sun4i_dma_dev(struct dma_device *dev)
@@ -200,7 +230,17 @@ static struct device *chan2dev(struct dma_chan *chan)
   	return &chan->dev->device;
   }
-static int convert_burst(u32 maxburst)
+static void set_dst_data_width_a10(u32 *p_cfg, s8 data_width)
+{
+	*p_cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(data_width);
+}
+
+static void set_src_data_width_a10(u32 *p_cfg, s8 data_width)
+{
+	*p_cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(data_width);
+}
+
+static int convert_burst_a10(u32 maxburst)
   {
   	if (maxburst > 8)
   		return -EINVAL;
@@ -233,15 +273,15 @@ static struct sun4i_dma_pchan *find_and_use_pchan(struct sun4i_dma_dev *priv,
   	int i, max;
/*
-	 * pchans 0-SUN4I_NDMA_NR_MAX_CHANNELS are normal, and
-	 * SUN4I_NDMA_NR_MAX_CHANNELS+ are dedicated ones
+	 * pchans 0-priv->cfg->ndma_nr_max_channels are normal, and
+	 * priv->cfg->ndma_nr_max_channels+ are dedicated ones
   	 */
   	if (vchan->is_dedicated) {
-		i = SUN4I_NDMA_NR_MAX_CHANNELS;
-		max = SUN4I_DMA_NR_MAX_CHANNELS;
+		i = priv->cfg->ndma_nr_max_channels;
+		max = priv->cfg->dma_nr_max_channels;
   	} else {
   		i = 0;
-		max = SUN4I_NDMA_NR_MAX_CHANNELS;
+		max = priv->cfg->ndma_nr_max_channels;
   	}
spin_lock_irqsave(&priv->lock, flags);
@@ -444,6 +484,7 @@ generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
   		      size_t len, struct dma_slave_config *sconfig,
   		      enum dma_transfer_direction direction)
   {
+	struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
   	struct sun4i_dma_promise *promise;
   	int ret;
@@ -467,13 +508,13 @@ generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
   		sconfig->src_addr_width, sconfig->dst_addr_width);
/* Source burst */
-	ret = convert_burst(sconfig->src_maxburst);
+	ret = priv->cfg->convert_burst(sconfig->src_maxburst);
   	if (ret < 0)
   		goto fail;
   	promise->cfg |= SUN4I_DMA_CFG_SRC_BURST_LENGTH(ret);
/* Destination burst */
-	ret = convert_burst(sconfig->dst_maxburst);
+	ret = priv->cfg->convert_burst(sconfig->dst_maxburst);
   	if (ret < 0)
   		goto fail;
   	promise->cfg |= SUN4I_DMA_CFG_DST_BURST_LENGTH(ret);
@@ -482,13 +523,13 @@ generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
   	ret = convert_buswidth(sconfig->src_addr_width);
   	if (ret < 0)
   		goto fail;
-	promise->cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(ret);
+	priv->cfg->set_src_data_width(&promise->cfg, ret);
/* Destination bus width */
   	ret = convert_buswidth(sconfig->dst_addr_width);
   	if (ret < 0)
   		goto fail;
-	promise->cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(ret);
+	priv->cfg->set_dst_data_width(&promise->cfg, ret);
return promise; @@ -510,6 +551,7 @@ static struct sun4i_dma_promise *
   generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
   		      size_t len, struct dma_slave_config *sconfig)
   {
+	struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
   	struct sun4i_dma_promise *promise;
   	int ret;
@@ -524,13 +566,13 @@ generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
   		SUN4I_DDMA_CFG_BYTE_COUNT_MODE_REMAIN;
/* Source burst */
-	ret = convert_burst(sconfig->src_maxburst);
+	ret = priv->cfg->convert_burst(sconfig->src_maxburst);
   	if (ret < 0)
   		goto fail;
   	promise->cfg |= SUN4I_DMA_CFG_SRC_BURST_LENGTH(ret);
/* Destination burst */
-	ret = convert_burst(sconfig->dst_maxburst);
+	ret = priv->cfg->convert_burst(sconfig->dst_maxburst);
   	if (ret < 0)
   		goto fail;
   	promise->cfg |= SUN4I_DMA_CFG_DST_BURST_LENGTH(ret);
@@ -539,13 +581,13 @@ generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
   	ret = convert_buswidth(sconfig->src_addr_width);
   	if (ret < 0)
   		goto fail;
-	promise->cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(ret);
+	priv->cfg->set_src_data_width(&promise->cfg, ret);
/* Destination bus width */
   	ret = convert_buswidth(sconfig->dst_addr_width);
   	if (ret < 0)
   		goto fail;
-	promise->cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(ret);
+	priv->cfg->set_dst_data_width(&promise->cfg, ret);
return promise; @@ -622,6 +664,7 @@ static struct dma_async_tx_descriptor *
   sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
   			  dma_addr_t src, size_t len, unsigned long flags)
   {
+	struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
   	struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
   	struct dma_slave_config *sconfig = &vchan->cfg;
   	struct sun4i_dma_promise *promise;
@@ -638,8 +681,8 @@ sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
   	 */
   	sconfig->src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
   	sconfig->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-	sconfig->src_maxburst = 8;
-	sconfig->dst_maxburst = 8;
+	sconfig->src_maxburst = priv->cfg->max_burst;
+	sconfig->dst_maxburst = priv->cfg->max_burst;
if (vchan->is_dedicated)
   		promise = generate_ddma_promise(chan, src, dest, len, sconfig);
@@ -654,11 +697,13 @@ sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
/* Configure memcpy mode */
   	if (vchan->is_dedicated) {
-		promise->cfg |= SUN4I_DMA_CFG_SRC_DRQ_TYPE(SUN4I_DDMA_DRQ_TYPE_SDRAM) |
-				SUN4I_DMA_CFG_DST_DRQ_TYPE(SUN4I_DDMA_DRQ_TYPE_SDRAM);
+		promise->cfg |=
+			SUN4I_DMA_CFG_SRC_DRQ_TYPE(priv->cfg->ddma_drq_sdram) |
+			SUN4I_DMA_CFG_DST_DRQ_TYPE(priv->cfg->ddma_drq_sdram);
   	} else {
-		promise->cfg |= SUN4I_DMA_CFG_SRC_DRQ_TYPE(SUN4I_NDMA_DRQ_TYPE_SDRAM) |
-				SUN4I_DMA_CFG_DST_DRQ_TYPE(SUN4I_NDMA_DRQ_TYPE_SDRAM);
+		promise->cfg |=
+			SUN4I_DMA_CFG_SRC_DRQ_TYPE(priv->cfg->ndma_drq_sdram) |
+			SUN4I_DMA_CFG_DST_DRQ_TYPE(priv->cfg->ndma_drq_sdram);
   	}
/* Fill the contract with our only promise */
@@ -673,6 +718,7 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
   			  size_t period_len, enum dma_transfer_direction dir,
   			  unsigned long flags)
   {
+	struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
   	struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
   	struct dma_slave_config *sconfig = &vchan->cfg;
   	struct sun4i_dma_promise *promise;
@@ -696,11 +742,11 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
   	if (vchan->is_dedicated) {
   		io_mode = SUN4I_DDMA_ADDR_MODE_IO;
   		linear_mode = SUN4I_DDMA_ADDR_MODE_LINEAR;
-		ram_type = SUN4I_DDMA_DRQ_TYPE_SDRAM;
+		ram_type = priv->cfg->ddma_drq_sdram;
   	} else {
   		io_mode = SUN4I_NDMA_ADDR_MODE_IO;
   		linear_mode = SUN4I_NDMA_ADDR_MODE_LINEAR;
-		ram_type = SUN4I_NDMA_DRQ_TYPE_SDRAM;
+		ram_type = priv->cfg->ndma_drq_sdram;
   	}
if (dir == DMA_MEM_TO_DEV) {
@@ -793,6 +839,7 @@ sun4i_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
   			unsigned int sg_len, enum dma_transfer_direction dir,
   			unsigned long flags, void *context)
   {
+	struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
   	struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
   	struct dma_slave_config *sconfig = &vchan->cfg;
   	struct sun4i_dma_promise *promise;
@@ -818,11 +865,11 @@ sun4i_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
   	if (vchan->is_dedicated) {
   		io_mode = SUN4I_DDMA_ADDR_MODE_IO;
   		linear_mode = SUN4I_DDMA_ADDR_MODE_LINEAR;
-		ram_type = SUN4I_DDMA_DRQ_TYPE_SDRAM;
+		ram_type = priv->cfg->ddma_drq_sdram;
   	} else {
   		io_mode = SUN4I_NDMA_ADDR_MODE_IO;
   		linear_mode = SUN4I_NDMA_ADDR_MODE_LINEAR;
-		ram_type = SUN4I_NDMA_DRQ_TYPE_SDRAM;
+		ram_type = priv->cfg->ndma_drq_sdram;
   	}
if (dir == DMA_MEM_TO_DEV)
@@ -1150,6 +1197,10 @@ static int sun4i_dma_probe(struct platform_device *pdev)
   	if (!priv)
   		return -ENOMEM;
+ priv->cfg = of_device_get_match_data(&pdev->dev);
+	if (!priv->cfg)
+		return -ENODEV;
+
   	priv->base = devm_platform_ioremap_resource(pdev, 0);
   	if (IS_ERR(priv->base))
   		return PTR_ERR(priv->base);
@@ -1197,23 +1248,26 @@ static int sun4i_dma_probe(struct platform_device *pdev)
priv->slave.dev = &pdev->dev; - priv->pchans = devm_kcalloc(&pdev->dev, SUN4I_DMA_NR_MAX_CHANNELS,
+	priv->pchans = devm_kcalloc(&pdev->dev, priv->cfg->dma_nr_max_channels,
   				    sizeof(struct sun4i_dma_pchan), GFP_KERNEL);
   	priv->vchans = devm_kcalloc(&pdev->dev, SUN4I_DMA_NR_MAX_VCHANS,
   				    sizeof(struct sun4i_dma_vchan), GFP_KERNEL);
-	if (!priv->vchans || !priv->pchans)
+	priv->pchans_used = devm_kcalloc(&pdev->dev,
+					 BITS_TO_LONGS(priv->cfg->dma_nr_max_channels),
+					 sizeof(unsigned long), GFP_KERNEL);
+	if (!priv->vchans || !priv->pchans || !priv->pchans_used)
   		return -ENOMEM;
/*
-	 * [0..SUN4I_NDMA_NR_MAX_CHANNELS) are normal pchans, and
-	 * [SUN4I_NDMA_NR_MAX_CHANNELS..SUN4I_DMA_NR_MAX_CHANNELS) are
+	 * [0..priv->cfg->ndma_nr_max_channels) are normal pchans, and
+	 * [priv->cfg->ndma_nr_max_channels..priv->cfg->dma_nr_max_channels) are
   	 * dedicated ones
   	 */
-	for (i = 0; i < SUN4I_NDMA_NR_MAX_CHANNELS; i++)
+	for (i = 0; i < priv->cfg->ndma_nr_max_channels; i++)
   		priv->pchans[i].base = priv->base +
   			SUN4I_NDMA_CHANNEL_REG_BASE(i);
- for (j = 0; i < SUN4I_DMA_NR_MAX_CHANNELS; i++, j++) {
+	for (j = 0; i < priv->cfg->dma_nr_max_channels; i++, j++) {
   		priv->pchans[i].base = priv->base +
   			SUN4I_DDMA_CHANNEL_REG_BASE(j);
   		priv->pchans[i].is_dedicated = 1;
@@ -1284,8 +1338,28 @@ static void sun4i_dma_remove(struct platform_device *pdev)
   	clk_disable_unprepare(priv->clk);
   }
+static struct sun4i_dma_config sun4i_a10_dma_cfg = {
+	.ndma_nr_max_channels	= SUN4I_NDMA_NR_MAX_CHANNELS,
+	.ndma_nr_max_vchans	= SUN4I_NDMA_NR_MAX_VCHANS,
+
+	.ddma_nr_max_channels	= SUN4I_DDMA_NR_MAX_CHANNELS,
+	.ddma_nr_max_vchans	= SUN4I_DDMA_NR_MAX_VCHANS,
+
+	.dma_nr_max_channels	= SUN4I_NDMA_NR_MAX_CHANNELS +
+		SUN4I_DDMA_NR_MAX_CHANNELS,
+

Or else use "SUN4I_DMA_NR_MAX_CHANNELS" here.

+	.set_dst_data_width	= set_dst_data_width_a10,
+	.set_src_data_width	= set_src_data_width_a10,
+	.convert_burst		= convert_burst_a10,
+
+	.ndma_drq_sdram		= SUN4I_NDMA_DRQ_TYPE_SDRAM,
+	.ddma_drq_sdram		= SUN4I_DDMA_DRQ_TYPE_SDRAM,
+
+	.max_burst		= SUN4I_MAX_BURST,
+};
+
   static const struct of_device_id sun4i_dma_match[] = {
-	{ .compatible = "allwinner,sun4i-a10-dma" },
+	{ .compatible = "allwinner,sun4i-a10-dma", .data = &sun4i_a10_dma_cfg },
   	{ /* sentinel */ },
   };
   MODULE_DEVICE_TABLE(of, sun4i_dma_match);
--
2.34.1








[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux PCI]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux