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@xxxxxxxxxxxxxx> --- 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@xxxxxxxxx> + * Copyright (C) 2015 - 2018 Texas Instruments Incorporated <www.ti.com> + * Written by Mugunthan V N <mugunthanvnm@xxxxxx> + * + * Author: Mugunthan V N <mugunthanvnm@xxxxxx> + */ + +#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