summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/Kconfig7
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/adi_dma.c253
-rw-r--r--drivers/dma/ti/Makefile1
-rw-r--r--drivers/dma/ti/k3-psil.c2
-rw-r--r--drivers/dma/ti/k3-udma.c20
6 files changed, 276 insertions, 8 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 3c64e894646..4b47be6b016 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -76,6 +76,13 @@ config XILINX_DPDMA
this file is used as placeholder for driver. The main reason is
to record compatible string and calling power domain driver.
+config ADI_DMA
+ bool "ADI DMA driver"
+ depends on DMA && DMA_CHANNELS
+ help
+ Enable DMA support for Analog Devices SOCs, such as the SC5xx.
+ Currently this is a minimalistic driver tested against OSPI use only.
+
if APBH_DMA
config APBH_DMA_BURST
bool "Enable DMA BURST"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 48811eaaeb3..00d765864cd 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -13,5 +13,6 @@ obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o
obj-$(CONFIG_XILINX_DPDMA) += xilinx_dpdma.o
+obj-$(CONFIG_ADI_DMA) += adi_dma.o
obj-y += ti/
diff --git a/drivers/dma/adi_dma.c b/drivers/dma/adi_dma.c
new file mode 100644
index 00000000000..28afe488db0
--- /dev/null
+++ b/drivers/dma/adi_dma.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Analog Devices DMA controller driver
+ *
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ * Contact: Ian Roberts <ian.roberts@timesys.com>
+ *
+ */
+#include <dm.h>
+#include <dma.h>
+#include <dma-uclass.h>
+#include <dm/device_compat.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#define HAS_MDMA BIT(0)
+
+#define REG_ADDRSTART 0x04
+#define REG_CFG 0x08
+#define REG_XCNT 0x0C
+#define REG_XMOD 0x10
+#define REG_STAT 0x30
+
+#define BITP_DMA_CFG_MSIZE 8
+#define BITP_DMA_CFG_PSIZE 4
+#define BITM_DMA_CFG_WNR 0x00000002
+#define BITM_DMA_CFG_EN 0x00000001
+#define ENUM_DMA_CFG_XCNT_INT 0x00100000
+
+#define BITP_DMA_STAT_PBWID 12
+#define BITP_DMA_STAT_ERRC 4
+#define BITM_DMA_STAT_PBWID 0x00003000
+#define BITM_DMA_STAT_ERRC 0x00000070
+#define BITM_DMA_STAT_PIRQ 0x00000004
+#define BITM_DMA_STAT_IRQERR 0x00000002
+#define BITM_DMA_STAT_IRQDONE 0x00000001
+
+#define DMA_MDMA_SRC_DEFAULT_CONFIG(psize, msize) \
+ (BITM_DMA_CFG_EN | ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE))
+#define DMA_MDMA_DST_DEFAULT_CONFIG(psize, msize) \
+ (BITM_DMA_CFG_EN | BITM_DMA_CFG_WNR | ENUM_DMA_CFG_XCNT_INT | \
+ ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE))
+
+struct adi_dma_channel {
+ int id;
+ struct adi_dma *dma;
+ void __iomem *iosrc;
+ void __iomem *iodest;
+};
+
+struct adi_dma {
+ struct udevice *dev;
+ struct adi_dma_channel channels[1];
+ void __iomem *ioaddr;
+ unsigned long hw_cfg;
+};
+
+static const struct udevice_id dma_dt_ids[] = {
+ { .compatible = "adi,mdma-controller", .data = HAS_MDMA },
+ { }
+};
+
+static u8 adi_dma_get_msize(u32 n_bytecount, u32 n_address)
+{
+ /* Calculate MSIZE, PSIZE, XCNT and XMOD */
+ u8 n_msize = 0;
+ u32 n_value = n_bytecount | n_address;
+ u32 n_mask = 0x1;
+
+ for (n_msize = 0; n_msize < 5; n_msize++, n_mask <<= 1) {
+ if ((n_value & n_mask) == n_mask)
+ break;
+ }
+
+ return n_msize;
+}
+
+static int adi_dma_get_ch_error(void __iomem *ch)
+{
+ u32 cause = (ioread32(ch + REG_STAT) & BITM_DMA_STAT_ERRC) >>
+ BITP_DMA_STAT_ERRC;
+ switch (cause) {
+ case 0:
+ return -EINVAL;
+ case 1:
+ return -EBUSY;
+ case 2:
+ return -EFAULT;
+ case 3:
+ fallthrough;
+ case 5:
+ fallthrough;
+ case 6:
+ fallthrough;
+ default:
+ return -EIO;
+ }
+}
+
+static int adi_mdma_transfer(struct udevice *dev, int direction,
+ dma_addr_t dst, dma_addr_t src, size_t len)
+{
+ struct adi_dma *priv = dev_get_priv(dev);
+ void __iomem *chsrc = priv->channels[0].iosrc;
+ void __iomem *chdst = priv->channels[0].iodest;
+
+ int result = 0;
+ u32 reg;
+ u32 bytecount = len;
+
+ u8 n_srcmsize;
+ u8 n_dstmsize;
+ u8 n_srcpsize;
+ u8 n_dstpsize;
+ u8 n_psize;
+ u32 srcconfig;
+ u32 dstconfig;
+ u8 srcpsizemax = (ioread32(chsrc + REG_STAT) & BITM_DMA_STAT_PBWID) >>
+ BITP_DMA_STAT_PBWID;
+ u8 dstpsizemax = (ioread32(chdst + REG_STAT) & BITM_DMA_STAT_PBWID) >>
+ BITP_DMA_STAT_PBWID;
+
+ const u32 CLRSTAT = (BITM_DMA_STAT_IRQDONE | BITM_DMA_STAT_IRQERR |
+ BITM_DMA_STAT_PIRQ);
+
+ if (len == 0)
+ return -EINVAL;
+
+ /* Clear DMA status */
+ iowrite32(CLRSTAT, chsrc + REG_STAT);
+ iowrite32(CLRSTAT, chdst + REG_STAT);
+
+ /* Calculate MSIZE, PSIZE, XCNT and XMOD */
+ n_srcmsize = adi_dma_get_msize(bytecount, src);
+ n_dstmsize = adi_dma_get_msize(bytecount, dst);
+ n_srcpsize = min(n_srcmsize, srcpsizemax);
+ n_dstpsize = min(n_dstmsize, dstpsizemax);
+ n_psize = min(n_srcpsize, n_dstpsize);
+
+ srcconfig = DMA_MDMA_SRC_DEFAULT_CONFIG(n_psize, n_srcmsize);
+ dstconfig = DMA_MDMA_DST_DEFAULT_CONFIG(n_psize, n_dstmsize);
+
+ /* Load the DMA descriptors */
+ iowrite32(src, chsrc + REG_ADDRSTART);
+ iowrite32(bytecount >> n_srcmsize, chsrc + REG_XCNT);
+ iowrite32(1 << n_srcmsize, chsrc + REG_XMOD);
+ iowrite32(dst, chdst + REG_ADDRSTART);
+ iowrite32(bytecount >> n_dstmsize, chdst + REG_XCNT);
+ iowrite32(1 << n_dstmsize, chdst + REG_XMOD);
+
+ iowrite32(dstconfig, chdst + REG_CFG);
+ iowrite32(srcconfig, chsrc + REG_CFG);
+
+ /* Wait for DMA to complete while checking for a DMA error */
+ do {
+ reg = ioread32(chsrc + REG_STAT);
+ if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) {
+ result = adi_dma_get_ch_error(chsrc);
+ break;
+ }
+ reg = ioread32(chdst + REG_STAT);
+ if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) {
+ result = adi_dma_get_ch_error(chdst);
+ break;
+ }
+ } while ((reg & BITM_DMA_STAT_IRQDONE) == 0);
+
+ clrbits_32(chsrc + REG_CFG, 1);
+ clrbits_32(chdst + REG_CFG, 1);
+
+ return result;
+}
+
+static int adi_dma_init_channel(struct adi_dma *dma,
+ struct adi_dma_channel *channel, ofnode node)
+{
+ u32 offset;
+
+ if (ofnode_read_u32(node, "adi,id", &channel->id)) {
+ dev_err(dma->dev, "Missing adi,id for channel %s\n",
+ ofnode_get_name(node));
+ return -ENOENT;
+ }
+
+ if (ofnode_read_u32(node, "adi,src-offset", &offset)) {
+ dev_err(dma->dev, "Missing adi,src-offset for channel %s\n",
+ ofnode_get_name(node));
+ return -ENOENT;
+ }
+
+ channel->iosrc = dma->ioaddr + offset;
+ channel->dma = dma;
+
+ if (dma->hw_cfg & HAS_MDMA) {
+ if (ofnode_read_u32(node, "adi,dest-offset", &offset)) {
+ dev_err(dma->dev,
+ "Missing adi,dest-offset for channel %s\n",
+ ofnode_get_name(node));
+ return -ENOENT;
+ }
+ channel->iodest = dma->ioaddr + offset;
+ }
+
+ return 0;
+}
+
+static int adi_dma_probe(struct udevice *dev)
+{
+ struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct adi_dma *priv = dev_get_priv(dev);
+ ofnode node, child;
+
+ priv->hw_cfg = dev_get_driver_data(dev);
+ if (priv->hw_cfg & HAS_MDMA)
+ uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM;
+
+ priv->ioaddr = dev_remap_addr(dev);
+ if (!priv->ioaddr)
+ return -EINVAL;
+
+ node = dev_read_first_subnode(dev);
+ if (!ofnode_valid(node)) {
+ dev_err(dev,
+ "Error: device tree DMA channel config missing!\n");
+ return -ENODEV;
+ }
+
+ node = dev_ofnode(dev);
+ ofnode_for_each_subnode(child, node) {
+ adi_dma_init_channel(priv, priv->channels, child);
+ break; //Only 1 channel supported for now
+ }
+
+ return 0;
+}
+
+static const struct dma_ops adi_dma_ops = {
+ .transfer = adi_mdma_transfer,
+};
+
+U_BOOT_DRIVER(adi_dma) = {
+ .name = "adi_dma",
+ .id = UCLASS_DMA,
+ .of_match = dma_dt_ids,
+ .ops = &adi_dma_ops,
+ .probe = adi_dma_probe,
+ .priv_auto = sizeof(struct adi_dma),
+};
diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
index 94ec13ba7ca..90c20a6a3fa 100644
--- a/drivers/dma/ti/Makefile
+++ b/drivers/dma/ti/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_TI_K3_PSIL) += k3-psil-data.o
k3-psil-data-y += k3-psil.o
k3-psil-data-$(CONFIG_SOC_K3_AM654) += k3-psil-am654.o
k3-psil-data-$(CONFIG_SOC_K3_J721E) += k3-psil-j721e.o
+k3-psil-data-$(CONFIG_SOC_K3_J7200) += k3-psil-j721e.o
k3-psil-data-$(CONFIG_SOC_K3_J721S2) += k3-psil-j721s2.o
k3-psil-data-$(CONFIG_SOC_K3_AM642) += k3-psil-am64.o
k3-psil-data-$(CONFIG_SOC_K3_AM625) += k3-psil-am62.o
diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c
index 369e679886f..39798844a8a 100644
--- a/drivers/dma/ti/k3-psil.c
+++ b/drivers/dma/ti/k3-psil.c
@@ -20,6 +20,8 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id)
soc_ep_map = &am654_ep_map;
else if (IS_ENABLED(CONFIG_SOC_K3_J721E))
soc_ep_map = &j721e_ep_map;
+ else if (IS_ENABLED(CONFIG_SOC_K3_J7200))
+ soc_ep_map = &j721e_ep_map;
else if (IS_ENABLED(CONFIG_SOC_K3_J721S2))
soc_ep_map = &j721s2_ep_map;
else if (IS_ENABLED(CONFIG_SOC_K3_AM642))
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 3013c4741d0..723265ab2e5 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -36,6 +36,7 @@
#include "k3-psil-priv.h"
#define K3_UDMA_MAX_RFLOWS 1024
+#define K3_UDMA_MAX_TR 2
struct udma_chan;
@@ -74,7 +75,6 @@ struct udma_tchan {
struct k3_nav_ring *t_ring; /* Transmit ring */
struct k3_nav_ring *tc_ring; /* Transmit Completion ring */
int tflow_id; /* applicable only for PKTDMA */
-
};
#define udma_bchan udma_tchan
@@ -175,6 +175,7 @@ struct udma_dev {
struct udma_rflow *rflows;
struct udma_match_data *match_data;
+ void *bc_desc;
struct udma_chan *channels;
u32 psil_base;
@@ -1349,6 +1350,7 @@ static int udma_setup_resources(struct udma_dev *ud)
struct ti_sci_resource_desc *rm_desc;
struct ti_sci_resource *rm_res;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+ size_t desc_size;
ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
@@ -1366,9 +1368,11 @@ static int udma_setup_resources(struct udma_dev *ud)
ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows),
GFP_KERNEL);
+ desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t));
+ ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL);
if (!ud->tchan_map || !ud->rchan_map || !ud->rflow_map ||
!ud->rflow_map_reserved || !ud->tchans || !ud->rchans ||
- !ud->rflows)
+ !ud->rflows || !ud->bc_desc)
return -ENOMEM;
/*
@@ -1444,6 +1448,7 @@ static int bcdma_setup_resources(struct udma_dev *ud)
struct ti_sci_resource_desc *rm_desc;
struct ti_sci_resource *rm_res;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+ size_t desc_size;
ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
@@ -1460,9 +1465,12 @@ static int bcdma_setup_resources(struct udma_dev *ud)
ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows),
GFP_KERNEL);
+ desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t));
+ ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL);
+
if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map ||
!ud->bchans || !ud->tchans || !ud->rchans ||
- !ud->rflows)
+ !ud->rflows || !ud->bc_desc)
return -ENOMEM;
/* Get resource ranges from tisci */
@@ -1718,8 +1726,7 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest,
int num_tr;
size_t tr_size = sizeof(struct cppi5_tr_type15_t);
u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
- unsigned long dummy;
- void *tr_desc;
+ void *tr_desc = uc->ud->bc_desc;
size_t desc_size;
if (len < SZ_64K) {
@@ -1748,9 +1755,6 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest,
}
desc_size = cppi5_trdesc_calc_size(num_tr, tr_size);
- tr_desc = dma_alloc_coherent(desc_size, &dummy);
- if (!tr_desc)
- return NULL;
memset(tr_desc, 0, desc_size);
cppi5_trdesc_init(tr_desc, num_tr, tr_size, 0, 0);