[PATCH v2 6/6] dmaengine: axi-dmac: add support for reading bus attributes from registers

From: Alexandru Ardelean
Date: Tue Aug 25 2020 - 08:48:38 EST


Starting with core version 4.3.a the DMA bus attributes can (and should) be
read from the INTERFACE_DESCRIPTION (0x10) register.

For older core versions, this will still need to be provided from the
device-tree.

The bus-type values are identical to the ones stored in the device-trees,
so we just need to read them. Bus-width values are stored in log2 values,
so we just need to use them as shift values to make them equivalent to the
current format.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@xxxxxxxxxx>
---
drivers/dma/dma-axi-dmac.c | 66 ++++++++++++++++++++++++++++++++++++--
1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 7ee56ae60093..25442a437879 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -6,6 +6,7 @@
* Author: Lars-Peter Clausen <lars@xxxxxxxxxx>
*/

+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
@@ -45,6 +46,16 @@
* there is no address than can or needs to be configured for the device side.
*/

+#define AXI_DMAC_REG_INTERFACE_DESC 0x10
+#define AXI_DMAC_DMA_SRC_TYPE_MSK GENMASK(13, 12)
+#define AXI_DMAC_DMA_SRC_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_SRC_TYPE_MSK, x)
+#define AXI_DMAC_DMA_SRC_WIDTH_MSK GENMASK(11, 8)
+#define AXI_DMAC_DMA_SRC_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_SRC_WIDTH_MSK, x)
+#define AXI_DMAC_DMA_DST_TYPE_MSK GENMASK(5, 4)
+#define AXI_DMAC_DMA_DST_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_TYPE_MSK, x)
+#define AXI_DMAC_DMA_DST_WIDTH_MSK GENMASK(3, 0)
+#define AXI_DMAC_DMA_DST_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_WIDTH_MSK, x)
+
#define AXI_DMAC_REG_IRQ_MASK 0x80
#define AXI_DMAC_REG_IRQ_PENDING 0x84
#define AXI_DMAC_REG_IRQ_SOURCE 0x88
@@ -801,6 +812,51 @@ static int axi_dmac_parse_dt(struct device *dev, struct axi_dmac *dmac)
return 0;
}

+static int axi_dmac_read_chan_config(struct device *dev, struct axi_dmac *dmac)
+{
+ struct axi_dmac_chan *chan = &dmac->chan;
+ unsigned int val, desc;
+
+ desc = axi_dmac_read(dmac, AXI_DMAC_REG_INTERFACE_DESC);
+ if (desc == 0) {
+ dev_err(dev, "DMA interface register reads zero\n");
+ return -EFAULT;
+ }
+
+ val = AXI_DMAC_DMA_SRC_TYPE_GET(desc);
+ if (val > AXI_DMAC_BUS_TYPE_FIFO) {
+ dev_err(dev, "Invalid source bus type read: %d\n", val);
+ return -EINVAL;
+ }
+ chan->src_type = val;
+
+ val = AXI_DMAC_DMA_DST_TYPE_GET(desc);
+ if (val > AXI_DMAC_BUS_TYPE_FIFO) {
+ dev_err(dev, "Invalid destination bus type read: %d\n", val);
+ return -EINVAL;
+ }
+ chan->dest_type = val;
+
+ val = AXI_DMAC_DMA_SRC_WIDTH_GET(desc);
+ if (val == 0) {
+ dev_err(dev, "Source bus width is zero\n");
+ return -EINVAL;
+ }
+ /* widths are stored in log2 */
+ chan->src_width = 1 << val;
+
+ val = AXI_DMAC_DMA_DST_WIDTH_GET(desc);
+ if (val == 0) {
+ dev_err(dev, "Destination bus width is zero\n");
+ return -EINVAL;
+ }
+ chan->dest_width = 1 << val;
+
+ axi_dmac_adjust_chan_params(chan);
+
+ return 0;
+}
+
static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
{
struct axi_dmac_chan *chan = &dmac->chan;
@@ -880,7 +936,13 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (ret < 0)
return ret;

- ret = axi_dmac_parse_dt(&pdev->dev, dmac);
+ version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
+
+ if (version >= ADI_AXI_PCORE_VER(4, 3, 'a'))
+ ret = axi_dmac_read_chan_config(&pdev->dev, dmac);
+ else
+ ret = axi_dmac_parse_dt(&pdev->dev, dmac);
+
if (ret < 0)
goto err_clk_disable;

@@ -912,8 +974,6 @@ static int axi_dmac_probe(struct platform_device *pdev)
dmac->chan.vchan.desc_free = axi_dmac_desc_free;
vchan_init(&dmac->chan.vchan, dma_dev);

- version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
-
ret = axi_dmac_detect_caps(dmac, version);
if (ret)
goto err_clk_disable;
--
2.17.1