[PATCH 4/7] dma: add dma-devices support
Sascha Hauer
s.hauer at pengutronix.de
Fri Nov 8 05:15:02 PST 2024
This adds support for dma-devices (on Linux aka dmaengines) to barebox.
The code is based on U-Boot-2025.01-rc1.
The code consists of a shim wrapper layer around a struct dma_ops and
helper functions to retrieve the suitable dma_ops instance from the
devices tree based on standard dma properties.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
drivers/dma/Kconfig | 10 +++
drivers/dma/Makefile | 1 +
drivers/dma/dma-devices.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++
include/dma-devices.h | 172 +++++++++++++++++++++++++++++++++++++
4 files changed, 393 insertions(+)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index e7516466d9..0f55b0a895 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -1,6 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "DMA support"
+config DMADEVICES
+ bool "DMA device support"
+ help
+ Add support for DMA devices. DMA devices can do asynchronous data
+ transfers without involving the host CPU.
+
+if DMADEVICES
+comment "DMA Devices"
+endif
+
config MXS_APBH_DMA
tristate "MXS APBH DMA ENGINE"
depends on ARCH_IMX23 || ARCH_IMX28 || ARCH_IMX6 || ARCH_IMX7
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 77bd8abba5..28dcf98b4f 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_DMADEVICES) += dma-devices.o
obj-$(CONFIG_HAS_DMA) += map.o
obj-$(CONFIG_DMA_API_DEBUG) += debug.o
obj-$(CONFIG_MXS_APBH_DMA) += apbh_dma.o
diff --git a/drivers/dma/dma-devices.c b/drivers/dma/dma-devices.c
new file mode 100644
index 0000000000..a8b8086d5f
--- /dev/null
+++ b/drivers/dma/dma-devices.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Direct Memory Access U-Class driver
+ *
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari at gmail.com>
+ * Copyright (C) 2015 - 2018 Texas Instruments Incorporated <www.ti.com>
+ * Written by Mugunthan V N <mugunthanvnm at ti.com>
+ *
+ * Author: Mugunthan V N <mugunthanvnm at ti.com>
+ */
+
+#include <driver.h>
+#include <dma.h>
+#include <dma-devices.h>
+
+static LIST_HEAD(dma_devices);
+
+static int dma_of_xlate_default(struct dma *dma,
+ struct of_phandle_args *args)
+{
+ dev_dbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (args->args_count > 1) {
+ pr_err("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ dma->id = args->args[0];
+ else
+ dma->id = 0;
+
+ return 0;
+}
+
+static int dma_request(struct device *dev, struct dma *dma)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_dbg(dma->dev, "%s(dev=%p, dma=%p)\n", __func__, dev, dma);
+
+ if (!ops->request)
+ return 0;
+
+ return ops->request(dma);
+}
+
+static struct dma_device *dma_find_by_node(struct device_node *np)
+{
+ struct dma_device *dmad;
+
+ list_for_each_entry(dmad, &dma_devices, list) {
+ if (dmad->dev->of_node == np)
+ return dmad;
+ }
+
+ return NULL;
+}
+
+struct dma *dma_get_by_index(struct device *dev, int index)
+{
+ int ret;
+ struct of_phandle_args args;
+ const struct dma_ops *ops;
+ struct dma *dma;
+ struct dma_device *dmad;
+
+ dev_dbg(dev, "%s index=%d)\n", __func__, index);
+
+ ret = of_parse_phandle_with_args(dev->of_node, "dmas", "#dma-cells", index,
+ &args);
+ if (ret) {
+ dev_err(dev, "%s: dev_read_phandle_with_args failed: %pe\n",
+ __func__, ERR_PTR(ret));
+ return ERR_PTR(ret);
+ }
+
+ dmad = dma_find_by_node(args.np);
+ if (!dmad)
+ return ERR_PTR(-ENODEV);
+
+ dma = xzalloc(sizeof(*dma));
+
+ dma->dmad = dmad;
+ dma->dev = dmad->dev;
+
+ ops = dmad->ops;
+
+ if (ops->of_xlate)
+ ret = ops->of_xlate(dma, &args);
+ else
+ ret = dma_of_xlate_default(dma, &args);
+ if (ret) {
+ dev_err(dma->dev, "of_xlate() failed: %pe\n", ERR_PTR(ret));
+ return ERR_PTR(ret);
+ }
+
+ ret = dma_request(dmad->dev, dma);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dma;
+}
+
+struct dma *dma_get_by_name(struct device *dev, const char *name)
+{
+ int index;
+
+ dev_dbg(dev, "%s(dev=%p, name=%s)\n", __func__, dev, name);
+
+ index = of_property_match_string(dev->of_node, "dma-names", name);
+ if (index < 0) {
+ dev_err(dev, "dev_read_stringlist_search() failed: %pe\n",
+ ERR_PTR(index));
+ return ERR_PTR(index);
+ }
+
+ return dma_get_by_index(dev, index);
+}
+
+int dma_release(struct dma *dma)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_dbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->rfree)
+ return 0;
+
+ return ops->rfree(dma);
+}
+
+int dma_enable(struct dma *dma)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_dbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->enable)
+ return -ENOSYS;
+
+ return ops->enable(dma);
+}
+
+int dma_disable(struct dma *dma)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_dbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->disable)
+ return -ENOSYS;
+
+ return ops->disable(dma);
+}
+
+int dma_prepare_rcv_buf(struct dma *dma, dma_addr_t dst, size_t size)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_vdbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->prepare_rcv_buf)
+ return -1;
+
+ return ops->prepare_rcv_buf(dma, dst, size);
+}
+
+int dma_receive(struct dma *dma, dma_addr_t *dst, void *metadata)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_vdbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->receive)
+ return -ENOSYS;
+
+ return ops->receive(dma, dst, metadata);
+}
+
+int dma_send(struct dma *dma, dma_addr_t src, size_t len, void *metadata)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_vdbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->send)
+ return -ENOSYS;
+
+ return ops->send(dma, src, len, metadata);
+}
+
+int dma_get_cfg(struct dma *dma, u32 cfg_id, void **cfg_data)
+{
+ const struct dma_ops *ops = dma->dmad->ops;
+
+ dev_dbg(dma->dev, "%s(dma=%p)\n", __func__, dma);
+
+ if (!ops->get_cfg)
+ return -ENOSYS;
+
+ return ops->get_cfg(dma, cfg_id, cfg_data);
+}
+
+int dma_device_register(struct dma_device *dmad)
+{
+ list_add_tail(&dmad->list, &dma_devices);
+
+ return 0;
+}
diff --git a/include/dma-devices.h b/include/dma-devices.h
new file mode 100644
index 0000000000..dbc6759496
--- /dev/null
+++ b/include/dma-devices.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __DMA_DEVICES_H
+#define __DMA_DEVICES_H
+
+/**
+ * enum dma_transfer_direction - dma transfer mode and direction indicator
+ * @DMA_MEM_TO_MEM: Async/Memcpy mode
+ * @DMA_MEM_TO_DEV: Slave mode & From Memory to Device
+ * @DMA_DEV_TO_MEM: Slave mode & From Device to Memory
+ * @DMA_DEV_TO_DEV: Slave mode & From Device to Device
+ */
+enum dma_transfer_direction {
+ DMA_MEM_TO_MEM,
+ DMA_MEM_TO_DEV,
+ DMA_DEV_TO_MEM,
+ DMA_DEV_TO_DEV,
+ DMA_TRANS_NONE,
+};
+
+struct dma_device;
+
+struct dma {
+ struct device *dev;
+ /*
+ * Written by of_xlate. We assume a single id is enough for now. In the
+ * future, we might add more fields here.
+ */
+ unsigned long id;
+
+ struct dma_device *dmad;
+};
+
+struct of_phandle_args;
+
+/*
+ * struct dma_ops - Driver model DMA operations
+ *
+ * The uclass interface is implemented by all DMA devices which use
+ * driver model.
+ */
+struct dma_ops {
+ /**
+ * of_xlate - Translate a client's device-tree (OF) DMA specifier.
+ *
+ * The DMA core calls this function as the first step in implementing
+ * a client's dma_get_by_*() call.
+ *
+ * If this function pointer is set to NULL, the DMA core will use a
+ * default implementation, which assumes #dma-cells = <1>, and that
+ * the DT cell contains a simple integer DMA Channel.
+ *
+ * At present, the DMA API solely supports device-tree. If this
+ * changes, other xxx_xlate() functions may be added to support those
+ * other mechanisms.
+ *
+ * @dma: The dma struct to hold the translation result.
+ * @args: The dma specifier values from device tree.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*of_xlate)(struct dma *dma,
+ struct of_phandle_args *args);
+ /**
+ * request - Request a translated DMA.
+ *
+ * The DMA core calls this function as the second step in
+ * implementing a client's dma_get_by_*() call, following a successful
+ * xxx_xlate() call, or as the only step in implementing a client's
+ * dma_request() call.
+ *
+ * @dma: The DMA struct to request; this has been filled in by
+ * a previoux xxx_xlate() function call, or by the caller of
+ * dma_request().
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*request)(struct dma *dma);
+ /**
+ * rfree - Free a previously requested dma.
+ *
+ * This is the implementation of the client dma_free() API.
+ *
+ * @dma: The DMA to free.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*rfree)(struct dma *dma);
+ /**
+ * enable() - Enable a DMA Channel.
+ *
+ * @dma: The DMA Channel to manipulate.
+ * @return zero on success, or -ve error code.
+ */
+ int (*enable)(struct dma *dma);
+ /**
+ * disable() - Disable a DMA Channel.
+ *
+ * @dma: The DMA Channel to manipulate.
+ * @return zero on success, or -ve error code.
+ */
+ int (*disable)(struct dma *dma);
+ /**
+ * prepare_rcv_buf() - Prepare/Add receive DMA buffer.
+ *
+ * @dma: The DMA Channel to manipulate.
+ * @dst: The receive buffer pointer.
+ * @size: The receive buffer size
+ * @return zero on success, or -ve error code.
+ */
+ int (*prepare_rcv_buf)(struct dma *dma, dma_addr_t dst, size_t size);
+ /**
+ * receive() - Receive a DMA transfer.
+ *
+ * @dma: The DMA Channel to manipulate.
+ * @dst: The destination pointer.
+ * @metadata: DMA driver's specific data
+ * @return zero on success, or -ve error code.
+ */
+ int (*receive)(struct dma *dma, dma_addr_t *dst, void *metadata);
+ /**
+ * send() - Send a DMA transfer.
+ *
+ * @dma: The DMA Channel to manipulate.
+ * @src: The source pointer.
+ * @len: Length of the data to be sent (number of bytes).
+ * @metadata: DMA driver's specific data
+ * @return zero on success, or -ve error code.
+ */
+ int (*send)(struct dma *dma, dma_addr_t src, size_t len, void *metadata);
+ /**
+ * get_cfg() - Get DMA channel configuration for client's use
+ *
+ * @dma: The DMA Channel to manipulate
+ * @cfg_id: DMA provider specific ID to identify what
+ * configuration data client needs
+ * @data: Pointer to store pointer to DMA driver specific
+ * configuration data for the given cfg_id (output param)
+ * @return zero on success, or -ve error code.
+ */
+ int (*get_cfg)(struct dma *dma, u32 cfg_id, void **data);
+ /**
+ * transfer() - Issue a DMA transfer. The implementation must
+ * wait until the transfer is done.
+ *
+ * @dev: The DMA device
+ * @direction: direction of data transfer (should be one from
+ * enum dma_direction)
+ * @dst: The destination pointer.
+ * @src: The source pointer.
+ * @len: Length of the data to be copied (number of bytes).
+ * @return zero on success, or -ve error code.
+ */
+ int (*transfer)(struct device *dev, int direction, dma_addr_t dst,
+ dma_addr_t src, size_t len);
+};
+
+struct dma_device {
+ struct device *dev;
+ const struct dma_ops *ops;
+ struct list_head list;
+};
+
+int dma_device_register(struct dma_device *dmad);
+
+struct dma *dma_get_by_index(struct device *dev, int index);
+struct dma *dma_get_by_name(struct device *dev, const char *name);
+int dma_prepare_rcv_buf(struct dma *dma, dma_addr_t dst, size_t size);
+int dma_enable(struct dma *dma);
+int dma_disable(struct dma *dma);
+int dma_get_cfg(struct dma *dma, u32 cfg_id, void **cfg_data);
+int dma_send(struct dma *dma, dma_addr_t src, size_t len, void *metadata);
+int dma_receive(struct dma *dma, dma_addr_t *dst, void *metadata);
+int dma_release(struct dma *dma);
+
+#endif /* __DMA_DEVICES_H */
--
2.39.5
More information about the barebox
mailing list