diff mbox series

[U-Boot,RFC,01/14] dma: add dma channels support and improve uclass

Message ID 20180212163858.25601-2-noltari@gmail.com
State Superseded, archived
Delegated to: Daniel Schwierzeck
Headers show
Series bmips: add bcm6348-enet support | expand

Commit Message

Álvaro Fernández Rojas Feb. 12, 2018, 4:38 p.m. UTC
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
---
 drivers/dma/dma-uclass.c    | 212 ++++++++++++++++++++++++++++++++++-------
 drivers/mtd/spi/sf-uclass.c |  17 ++++
 drivers/mtd/spi/spi_flash.c |  11 ++-
 include/dma-uclass.h        | 110 +++++++++++++++++++++
 include/dma.h               | 226 +++++++++++++++++++++++++++++++++-----------
 include/spi_flash.h         |   3 +
 6 files changed, 485 insertions(+), 94 deletions(-)
 create mode 100644 include/dma-uclass.h

Comments

Raghavendra, Vignesh Feb. 20, 2018, 8:24 a.m. UTC | #1
Please add a commit message as this is a pretty big change providing a
overview what is being added and why?

On Monday 12 February 2018 10:08 PM, Álvaro Fernández Rojas wrote:
> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
> ---
>  drivers/dma/dma-uclass.c    | 212 ++++++++++++++++++++++++++++++++++-------


This patch definitely breaks drivers/dma/ti-edma3.c which implements
current definition of dma-uclass.

One of the assumption of current dma_memcpy() implementation is that the
client's DT node need not have "dmas" entry to specify which DMA
provider and DMA channel to use for dma memcpy. (This is true in linux
kernel as well). Most DMA HWs have general purpose channels that are not
tied to any peripherals and can be used for mem to mem transfers.
So, if the client device's DT node does not populate "dmas" property
then dma_memcpy() API should try to find a DMA provider that supports
memcpy operation, request a memcpy capable channel from the provider and
use it to transfer data. Please keep the current behavior as is.
Or, provide a API for clients to request such a channel.

>  drivers/mtd/spi/sf-uclass.c |  17 ++++
>  drivers/mtd/spi/spi_flash.c |  11 ++-
>  include/dma-uclass.h        | 110 +++++++++++++++++++++
>  include/dma.h               | 226 +++++++++++++++++++++++++++++++++-----------
>  include/spi_flash.h         |   3 +
>  6 files changed, 485 insertions(+), 94 deletions(-)
>  create mode 100644 include/dma-uclass.h
> 
> diff --git a/drivers/dma/dma-uclass.c b/drivers/dma/dma-uclass.c
> index 3d0ce22fbc..7d04d98493 100644
> --- a/drivers/dma/dma-uclass.c
> +++ b/drivers/dma/dma-uclass.c
> @@ -1,59 +1,176 @@
>  /*
> - * Direct Memory Access U-Class driver
> + * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
> + * Copyright (C) 2015 Texas Instruments Incorporated, <www.ti.com>
> + * Written by Mugunthan V N <mugunthanvnm@ti.com>
>   *
> - * (C) Copyright 2015
> - *     Texas Instruments Incorporated, <www.ti.com>
> - *
> - * Author: Mugunthan V N <mugunthanvnm@ti.com>
> - *
> - * SPDX-License-Identifier:     GPL-2.0+
> + * SPDX-License-Identifier:	GPL-2.0+
>   */
>  
>  #include <common.h>
> -#include <dma.h>
>  #include <dm.h>
> -#include <dm/uclass-internal.h>
> -#include <dm/device-internal.h>
> +#include <dma.h>
> +#include <dma-uclass.h>
> +#include <dt-structs.h>
>  #include <errno.h>
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> -int dma_get_device(u32 transfer_type, struct udevice **devp)
> +static inline struct dma_ops *dma_dev_ops(struct udevice *dev)
> +{
> +	return (struct dma_ops *)dev->driver->ops;
> +}
> +
> +#if CONFIG_IS_ENABLED(OF_CONTROL)
> +# if CONFIG_IS_ENABLED(OF_PLATDATA)
> +int dma_get_by_index_platdata(struct udevice *dev, int index,
> +			      struct phandle_2_cell *cells, struct dma *dma)
>  {
> -	struct udevice *dev;
>  	int ret;
>  
> -	for (ret = uclass_first_device(UCLASS_DMA, &dev); dev && !ret;
> -	     ret = uclass_next_device(&dev)) {
> -		struct dma_dev_priv *uc_priv;
> +	if (index != 0)
> +		return -ENOSYS;
> +	ret = uclass_get_device(UCLASS_DMA, 0, &dma->dev);
> +	if (ret)
> +		return ret;
> +	dma->id = cells[0].id;
>  
> -		uc_priv = dev_get_uclass_priv(dev);
> -		if (uc_priv->supported & transfer_type)
> -			break;
> -	}
> +	return 0;
> +}
> +# else
> +static int dma_of_xlate_default(struct dma *dma,
> +				struct fdtdec_phandle_args *args)
> +{
> +	debug("%s(dma=%p)\n", __func__, dma);
>  
> -	if (!dev) {
> -		pr_err("No DMA device found that supports %x type\n",
> -		      transfer_type);
> -		return -EPROTONOSUPPORT;
> +	if (args->args_count > 1) {
> +		pr_err("Invaild args_count: %d\n", args->args_count);
> +		return -EINVAL;
>  	}
>  
> -	*devp = dev;
> +	if (args->args_count)
> +		dma->id = args->args[0];
> +	else
> +		dma->id = 0;
>  
> -	return ret;
> +	return 0;
>  }
>  
> -int dma_memcpy(void *dst, void *src, size_t len)
> +int dma_get_by_index(struct udevice *dev, int index, struct dma *dma)
>  {
> -	struct udevice *dev;
> -	const struct dma_ops *ops;
>  	int ret;
> +	struct fdtdec_phandle_args args;
> +	struct udevice *dev_dma;
> +	struct dma_ops *ops;
> +
> +	debug("%s(dev=%p, index=%d, dma=%p)\n", __func__, dev, index, dma);
>  
> -	ret = dma_get_device(DMA_SUPPORTS_MEM_TO_MEM, &dev);
> -	if (ret < 0)
> +	assert(dma);
> +	ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
> +					     "dmas", "#dma-cells", 0, index,
> +					     &args);
> +	if (ret) {
> +		pr_err("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
> +		      __func__, ret);
>  		return ret;
> +	}
> +
> +	ret = uclass_get_device_by_of_offset(UCLASS_DMA, args.node, &dev_dma);
> +	if (ret) {
> +		pr_err("%s: uclass_get_device_by_of_offset failed: err=%d\n",
> +		      __func__, ret);
> +		return ret;
> +	}
> +
> +	dma->dev = dev_dma;
> +
> +	ops = dma_dev_ops(dev_dma);
> +
> +	if (ops->of_xlate)
> +		ret = ops->of_xlate(dma, &args);
> +	else
> +		ret = dma_of_xlate_default(dma, &args);
> +	if (ret) {
> +		pr_err("of_xlate() failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return dma_request(dev_dma, dma);
> +}
> +# endif /* OF_PLATDATA */
> +
> +int dma_get_by_name(struct udevice *dev, const char *name, struct dma *dma)
> +{
> +	int index;
> +
> +	debug("%s(dev=%p, name=%s, dma=%p)\n", __func__, dev, name, dma);
> +
> +	index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev),
> +				      "dma-names", name);
> +	if (index < 0) {
> +		pr_err("fdt_stringlist_search() failed: %d\n", index);
> +		return index;
> +	}
> +
> +	return dma_get_by_index(dev, index, dma);
> +}
> +#endif /* OF_CONTROL */
> +
> +int dma_request(struct udevice *dev, struct dma *dma)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dev);
> +
> +	debug("%s(dev=%p, dma=%p)\n", __func__, dev, dma);
> +
> +	dma->dev = dev;
> +
> +	if (!ops->request)
> +		return 0;

return 0? I guess you meant to return -ENOSYS or something.

> +
> +	return ops->request(dma);
> +}
> +
> +int dma_free(struct dma *dma)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dma->dev);
> +
> +	debug("%s(dma=%p)\n", __func__, dma);
> +
> +	if (!ops->free)
> +		return 0;
> +

Same as above

> +	return ops->free(dma);
> +}
> +
> +int dma_enable(struct dma *dma)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dma->dev);
> +
> +	debug("%s(dma=%p)\n", __func__, dma);
> +
> +	if (!ops->enable)
> +		return -ENOSYS;
> +
> +	return ops->enable(dma);
> +}
> +
> +int dma_disable(struct dma *dma)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dma->dev);
> +
> +	debug("%s(dma=%p)\n", __func__, dma);
> +
> +	if (!ops->disable)
> +		return -ENOSYS;
> +
> +	return ops->disable(dma);
> +}
> +
> +int dma_memcpy(struct dma *dma, void *dst, void *src, size_t len)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dma->dev);
> +
> +	debug("%s(dma=%p)\n", __func__, dma);
>  
> -	ops = device_get_ops(dev);
>  	if (!ops->transfer)
>  		return -ENOSYS;
>  
> @@ -61,12 +178,35 @@ int dma_memcpy(void *dst, void *src, size_t len)
>  	invalidate_dcache_range((unsigned long)dst, (unsigned long)dst +
>  				roundup(len, ARCH_DMA_MINALIGN));
>  
> -	return ops->transfer(dev, DMA_MEM_TO_MEM, dst, src, len);
> +	return ops->transfer(dma, DMA_MEM_TO_MEM, dst, src, len);
> +}
> +
> +int dma_receive(struct dma *dma, void **dst)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dma->dev);
> +
> +	debug("%s(dma=%p)\n", __func__, dma);
> +
> +	if (!ops->receive)
> +		return -1;

-ENOSYS?

> +
> +	return ops->receive(dma, dst);
> +}
> +
> +int dma_send(struct dma *dma, void *src, size_t len)
> +{
> +	struct dma_ops *ops = dma_dev_ops(dma->dev);
> +
> +	debug("%s(dma=%p)\n", __func__, dma);
> +
> +	if (!ops->send)
> +		return -1;

-ENOSYS?

> +
> +	return ops->send(dma, src, len);
>  }
>  
>  UCLASS_DRIVER(dma) = {
> -	.id		= UCLASS_DMA,
> -	.name		= "dma",
> -	.flags		= DM_UC_FLAG_SEQ_ALIAS,
> -	.per_device_auto_alloc_size = sizeof(struct dma_dev_priv),
> +	.id = UCLASS_DMA,
> +	.name = "dma",
> +	.flags = DM_UC_FLAG_SEQ_ALIAS,
>  };
> diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c
> index 83876485fe..992dead1a2 100644
> --- a/drivers/mtd/spi/sf-uclass.c
> +++ b/drivers/mtd/spi/sf-uclass.c
> @@ -6,6 +6,7 @@
>  
>  #include <common.h>
>  #include <dm.h>
> +#include <dma.h>
>  #include <spi.h>
>  #include <spi_flash.h>
>  #include <dm/device-internal.h>
> @@ -72,6 +73,22 @@ int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
>  		return ret;
>  
>  	*devp = slave->dev;
> +
> +#ifdef CONFIG_DMA
> +	if (fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(*devp),
> +				  "dmas") > 0) {
> +		struct spi_flash *priv = dev_get_uclass_priv(*devp);
> +
> +		priv->dma = calloc(1, sizeof(struct dma));
> +		if (!priv->dma)
> +			return -ENOMEM;
> +
> +		ret = dma_get_by_index(*devp, 0, priv->dma);
> +		if (ret)
> +			return -EINVAL;
> +	}
> +#endif
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index 294d9f9d79..8b1c6a27de 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -458,10 +458,6 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
>   */
>  void __weak spi_flash_copy_mmap(void *data, void *offset, size_t len)
>  {
> -#ifdef CONFIG_DMA
> -	if (!dma_memcpy(data, offset, len))
> -		return;
> -#endif
>  	memcpy(data, offset, len);
>  }
>  
> @@ -482,7 +478,12 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>  			return ret;
>  		}
>  		spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP);
> -		spi_flash_copy_mmap(data, flash->memory_map + offset, len);
> +		if (flash->dma)
> +			dma_memcpy(flash->dma, data,
> +				   flash->memory_map + offset, len);
> +		else
> +			spi_flash_copy_mmap(data, flash->memory_map + offset,
> +					    len);
>  		spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END);
>  		spi_release_bus(spi);
>  		return 0;
> diff --git a/include/dma-uclass.h b/include/dma-uclass.h
> new file mode 100644
> index 0000000000..0315e54441
> --- /dev/null
> +++ b/include/dma-uclass.h
> @@ -0,0 +1,110 @@
> +/*
> + * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
> + * Copyright (C) 2015 Texas Instruments Incorporated, <www.ti.com>
> + * Written by Mugunthan V N <mugunthanvnm@ti.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef _DMA_UCLASS_H
> +#define _DMA_UCLASS_H
> +
> +/* See dma.h for background documentation. */
> +
> +#include <dma.h>
> +#include <fdtdec.h>
> +
> +/**
> + * struct dma_ops - The functions that a DMA driver must implement.
> + */
> +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 fdtdec_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);
> +	/**
> +	 * free - 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 (*free)(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);
> +	/**
> +	 * transfer() - Issue a DMA transfer.
> +	 *
> +	 * @dma: The DMA Channel to manipulate.
> +	 * @dir: The direction of data transfer.
> +	 * @dst: The destination pointer.
> +	 * @src: The source pointer.
> +	 * @len: Length of the data to be copied.
> +	 * @return zero on success, or -ve error code.
> +	 */
> +	int (*transfer)(struct dma *dma, enum dma_transfer_direction dir,
> +			void *dst, void *src, size_t len);
> +	/**
> +	 * receive() - Receive a DMA transfer.
> +	 *
> +	 * @dma: The DMA Channel to manipulate.
> +	 * @dst: The destination pointer.
> +	 * @return zero on success, or -ve error code.
> +	 */
> +	int (*receive)(struct dma *dma, void **dst);
> +	/**
> +	 * send() - Send a DMA transfer.
> +	 *
> +	 * @dma: The DMA Channel to manipulate.
> +	 * @src: The source pointer.
> +	 * @len: Length of the data to be copied.
> +	 * @return zero on success, or -ve error code.
> +	 */
> +	int (*send)(struct dma *dma, void *src, size_t len);
> +

I wonder why separate send()/receive() is needed. Isn't transfer() alone
sufficient with appropriate direction flags? If its very specific to
networking use case then, please explain that more clearly as to why
this interface is provided in the comments (especially receive()
interface that does not pass length).



Regards
Vignesh
> +};
> +
> +#endif /* _DMA_UCLASS_H */
> diff --git a/include/dma.h b/include/dma.h
> index 71fa77f2ea..3632e3256d 100644
> --- a/include/dma.h
> +++ b/include/dma.h
> @@ -1,86 +1,206 @@
>  /*
> - * (C) Copyright 2015
> - *     Texas Instruments Incorporated, <www.ti.com>
> + * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
> + * Copyright (C) 2015 Texas Instruments Incorporated, <www.ti.com>
> + * Written by Mugunthan V N <mugunthanvnm@ti.com>
>   *
> - * SPDX-License-Identifier:     GPL-2.0+
> + * SPDX-License-Identifier:	GPL-2.0+
>   */
>  
>  #ifndef _DMA_H_
>  #define _DMA_H_
>  
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +
>  /*
> - * enum dma_direction - dma transfer direction indicator
> + * enum dma_transfer_direction - dma transfer direction indicator
>   * @DMA_MEM_TO_MEM: Memcpy mode
>   * @DMA_MEM_TO_DEV: From Memory to Device
>   * @DMA_DEV_TO_MEM: From Device to Memory
>   * @DMA_DEV_TO_DEV: From Device to Device
>   */
> -enum dma_direction {
> +enum dma_transfer_direction {
>  	DMA_MEM_TO_MEM,
>  	DMA_MEM_TO_DEV,
>  	DMA_DEV_TO_MEM,
>  	DMA_DEV_TO_DEV,
>  };
>  
> -#define DMA_SUPPORTS_MEM_TO_MEM	BIT(0)
> -#define DMA_SUPPORTS_MEM_TO_DEV	BIT(1)
> -#define DMA_SUPPORTS_DEV_TO_MEM	BIT(2)
> -#define DMA_SUPPORTS_DEV_TO_DEV	BIT(3)
> +/**
> + * A DMA is a feature of computer systems that allows certain hardware 
> + * subsystems to access main system memory, independent of the CPU.
> + * DMA channels are typically generated externally to the HW module
> + * consuming them, by an entity this API calls a DMA provider. This API
> + * provides a standard means for drivers to enable and disable DMAs, and to
> + * copy, send and receive data using DMA.
> + * 
> + * A driver that implements UCLASS_DMA is a DMA provider. A provider will
> + * often implement multiple separate DMAs, since the hardware it manages
> + * often has this capability. dma_uclass.h describes the interface which
> + * DMA providers must implement.
> + * 
> + * DMA consumers/clients are the HW modules driven by the DMA channels. This
> + * header file describes the API used by drivers for those HW modules.
> + */
>  
> -/*
> - * struct dma_ops - Driver model DMA operations
> +struct udevice;
> +
> +/**
> + * struct dma - A handle to (allowing control of) a single DMA.
> + *
> + * Clients provide storage for DMA handles. The content of the structure is
> + * managed solely by the DMA API and DMA drivers. A DMA struct is
> + * initialized by "get"ing the DMA struct. The DMA struct is passed to all
> + * other DMA APIs to identify which DMA channel to operate upon.
>   *
> - * The uclass interface is implemented by all DMA devices which use
> - * driver model.
> + * @dev: The device which implements the DMA channel.
> + * @id: The DMA channel ID within the provider.
> + *
> + * Currently, the DMA API assumes that a single integer ID is enough to
> + * identify and configure any DMA channel for any DMA provider. If this
> + * assumption becomes invalid in the future, the struct could be expanded to
> + * either (a) add more fields to allow DMA providers to store additional
> + * information, or (b) replace the id field with an opaque pointer, which the
> + * provider would dynamically allocated during its .of_xlate op, and process
> + * during is .request op. This may require the addition of an extra op to clean
> + * up the allocation.
>   */
> -struct dma_ops {
> +struct dma {
> +	struct udevice *dev;
>  	/*
> -	 * Get the current timer count
> -	 *
> -	 * @dev: The DMA device
> -	 * @direction: direction of data transfer should be one from
> -		       enum dma_direction
> -	 * @dst: Destination pointer
> -	 * @src: Source pointer
> -	 * @len: Length of the data to be copied.
> -	 * @return: 0 if OK, -ve on error
> +	 * Written by of_xlate. We assume a single id is enough for now. In the
> +	 * future, we might add more fields here.
>  	 */
> -	int (*transfer)(struct udevice *dev, int direction, void *dst,
> -			void *src, size_t len);
> +	unsigned long id;
>  };
>  
> -/*
> - * struct dma_dev_priv - information about a device used by the uclass
> +#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DMA)
> +struct phandle_2_cell;
> +int dma_get_by_index_platdata(struct udevice *dev, int index,
> +			      struct phandle_2_cell *cells, struct dma *dma);
> +
> +/**
> + * dma_get_by_index - Get/request a DMA by integer index.
> + *
> + * This looks up and requests a DMA. The index is relative to the client
> + * device; each device is assumed to have n DMAs associated with it somehow,
> + * and this function finds and requests one of them. The mapping of client
> + * device DMA indices to provider DMAs may be via device-tree properties,
> + * board-provided mapping tables, or some other mechanism.
>   *
> - * @supported: mode of transfers that DMA can support, should be
> - *	       one/multiple of DMA_SUPPORTS_*
> + * @dev:	The client device.
> + * @index:	The index of the DMA to request, within the client's list of
> + *		DMA channels.
> + * @dma:	A pointer to a DMA struct to initialize.
> + * @return 0 if OK, or a negative error code.
>   */
> -struct dma_dev_priv {
> -	u32 supported;
> -};
> +int dma_get_by_index(struct udevice *dev, int index, struct dma *dma);
>  
> -/*
> - * dma_get_device - get a DMA device which supports transfer
> - * type of transfer_type
> - *
> - * @transfer_type - transfer type should be one/multiple of
> - *		    DMA_SUPPORTS_*
> - * @devp - udevice pointer to return the found device
> - * @return - will return on success and devp will hold the
> - *	     pointer to the device
> +/**
> + * dma_get_by_name - Get/request a DMA by name.
> + *
> + * This looks up and requests a DMA. The name is relative to the client
> + * device; each device is assumed to have n DMAs associated with it somehow,
> + * and this function finds and requests one of them. The mapping of client
> + * device DMA names to provider DMAs may be via device-tree properties,
> + * board-provided mapping tables, or some other mechanism.
> + *
> + * @dev:	The client device.
> + * @name:	The name of the DMA to request, within the client's list of
> + *		DMA channels.
> + * @dma:	A pointer to a DMA struct to initialize.
> + * @return 0 if OK, or a negative error code.
>   */
> -int dma_get_device(u32 transfer_type, struct udevice **devp);
> +int dma_get_by_name(struct udevice *dev, const char *name, struct dma *dma);
> +#else
> +static inline int dma_get_by_index(struct udevice *dev, int index,
> +				   struct dma *dma)
> +{
> +	return -ENOSYS;
> +}
>  
> -/*
> - * dma_memcpy - try to use DMA to do a mem copy which will be
> - *		much faster than CPU mem copy
> - *
> - * @dst - destination pointer
> - * @src - souce pointer
> - * @len - data length to be copied
> - * @return - on successful transfer returns no of bytes
> -	     transferred and on failure return error code.
> +static inline int dma_get_by_name(struct udevice *dev, const char *name,
> +			   struct dma *dma)
> +{
> +	return -ENOSYS;
> +}
> +#endif
> +
> +/**
> + * dma_request - Request a DMA by provider-specific ID.
> + *
> + * This requests a DMA using a provider-specific ID. Generally, this function
> + * should not be used, since dma_get_by_index/name() provide an interface that
> + * better separates clients from intimate knowledge of DMA providers.
> + * However, this function may be useful in core SoC-specific code.
> + *
> + * @dev: The DMA provider device.
> + * @dma: A pointer to a DMA struct to initialize. The caller must
> + *	 have already initialized any field in this struct which the
> + *	 DMA provider uses to identify the DMA channel.
> + * @return 0 if OK, or a negative error code.
> + */
> +int dma_request(struct udevice *dev, struct dma *dma);
> +
> +/**
> + * dma_free - Free a previously requested DMA.
> + *
> + * @dma: A DMA struct that was previously successfully requested by
> + *	 dma_request/get_by_*().
> + * @return 0 if OK, or a negative error code.
> + */
> +int dma_free(struct dma *dma);
> +
> +/**
> + * dma_enable() - Enable (turn on) a DMA channel.
> + *
> + * @dma: A DMA struct that was previously successfully requested by
> + *	 dma_request/get_by_*().
> + * @return zero on success, or -ve error code.
> + */
> +int dma_enable(struct dma *dma);
> +
> +/**
> + * dma_disable() - Disable (turn off) a DMA channel.
> + *
> + * @dma: A DMA struct that was previously successfully requested by
> + *	 dma_request/get_by_*().
> + * @return zero on success, or -ve error code.
> + */
> +int dma_disable(struct dma *dma);
> +
> +/**
> + * dma_memcpy() - Try to use DMA to do a mem copy which will be much faster
> + *		  than CPU mem copy.
> + *
> + * @dma: A DMA struct that was previously successfully requested by
> + *	 dma_request/get_by_*().
> + * @dst: The destination pointer.
> + * @src: The source pointer.
> + * @len: Length of the data to be copied.
> + * @return zero on success, or -ve error code.
> + */
> +int dma_memcpy(struct dma *dma, void *dst, void *src, size_t len);
> +
> +/**
> + * dma_receive() - Receive a DMA transfer.
> + *
> + * @dma: A DMA struct that was previously successfully requested by
> + *	 dma_request/get_by_*().
> + * @dst: The destination pointer.
> + * @return zero on success, or -ve error code.
> + */
> +int dma_receive(struct dma *dma, void **dst);
> +
> +/**
> + * dma_send() - Send a DMA transfer.
> + *
> + * @dma: A DMA struct that was previously successfully requested by
> + *	 dma_request/get_by_*().
> + * @src: The source pointer.
> + * @len: Length of the data to be copied.
> + * @return zero on success, or -ve error code.
>   */
> -int dma_memcpy(void *dst, void *src, size_t len);
> +int dma_send(struct dma *dma, void *src, size_t len);
>  
> -#endif	/* _DMA_H_ */
> +#endif /* _DMA_H_ */
> diff --git a/include/spi_flash.h b/include/spi_flash.h
> index f3c4e83424..f31e8b8145 100644
> --- a/include/spi_flash.h
> +++ b/include/spi_flash.h
> @@ -65,6 +65,9 @@ struct spi_flash {
>  #ifdef CONFIG_DM_SPI_FLASH
>  	struct udevice *dev;
>  #endif
> +#ifdef CONFIG_DMA
> +	struct dma *dma;
> +#endif
>  	const char *name;
>  	u8 dual_flash;
>  	u8 shift;
>
diff mbox series

Patch

diff --git a/drivers/dma/dma-uclass.c b/drivers/dma/dma-uclass.c
index 3d0ce22fbc..7d04d98493 100644
--- a/drivers/dma/dma-uclass.c
+++ b/drivers/dma/dma-uclass.c
@@ -1,59 +1,176 @@ 
 /*
- * Direct Memory Access U-Class driver
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
+ * Copyright (C) 2015 Texas Instruments Incorporated, <www.ti.com>
+ * Written by Mugunthan V N <mugunthanvnm@ti.com>
  *
- * (C) Copyright 2015
- *     Texas Instruments Incorporated, <www.ti.com>
- *
- * Author: Mugunthan V N <mugunthanvnm@ti.com>
- *
- * SPDX-License-Identifier:     GPL-2.0+
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #include <common.h>
-#include <dma.h>
 #include <dm.h>
-#include <dm/uclass-internal.h>
-#include <dm/device-internal.h>
+#include <dma.h>
+#include <dma-uclass.h>
+#include <dt-structs.h>
 #include <errno.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
-int dma_get_device(u32 transfer_type, struct udevice **devp)
+static inline struct dma_ops *dma_dev_ops(struct udevice *dev)
+{
+	return (struct dma_ops *)dev->driver->ops;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+# if CONFIG_IS_ENABLED(OF_PLATDATA)
+int dma_get_by_index_platdata(struct udevice *dev, int index,
+			      struct phandle_2_cell *cells, struct dma *dma)
 {
-	struct udevice *dev;
 	int ret;
 
-	for (ret = uclass_first_device(UCLASS_DMA, &dev); dev && !ret;
-	     ret = uclass_next_device(&dev)) {
-		struct dma_dev_priv *uc_priv;
+	if (index != 0)
+		return -ENOSYS;
+	ret = uclass_get_device(UCLASS_DMA, 0, &dma->dev);
+	if (ret)
+		return ret;
+	dma->id = cells[0].id;
 
-		uc_priv = dev_get_uclass_priv(dev);
-		if (uc_priv->supported & transfer_type)
-			break;
-	}
+	return 0;
+}
+# else
+static int dma_of_xlate_default(struct dma *dma,
+				struct fdtdec_phandle_args *args)
+{
+	debug("%s(dma=%p)\n", __func__, dma);
 
-	if (!dev) {
-		pr_err("No DMA device found that supports %x type\n",
-		      transfer_type);
-		return -EPROTONOSUPPORT;
+	if (args->args_count > 1) {
+		pr_err("Invaild args_count: %d\n", args->args_count);
+		return -EINVAL;
 	}
 
-	*devp = dev;
+	if (args->args_count)
+		dma->id = args->args[0];
+	else
+		dma->id = 0;
 
-	return ret;
+	return 0;
 }
 
-int dma_memcpy(void *dst, void *src, size_t len)
+int dma_get_by_index(struct udevice *dev, int index, struct dma *dma)
 {
-	struct udevice *dev;
-	const struct dma_ops *ops;
 	int ret;
+	struct fdtdec_phandle_args args;
+	struct udevice *dev_dma;
+	struct dma_ops *ops;
+
+	debug("%s(dev=%p, index=%d, dma=%p)\n", __func__, dev, index, dma);
 
-	ret = dma_get_device(DMA_SUPPORTS_MEM_TO_MEM, &dev);
-	if (ret < 0)
+	assert(dma);
+	ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
+					     "dmas", "#dma-cells", 0, index,
+					     &args);
+	if (ret) {
+		pr_err("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
+		      __func__, ret);
 		return ret;
+	}
+
+	ret = uclass_get_device_by_of_offset(UCLASS_DMA, args.node, &dev_dma);
+	if (ret) {
+		pr_err("%s: uclass_get_device_by_of_offset failed: err=%d\n",
+		      __func__, ret);
+		return ret;
+	}
+
+	dma->dev = dev_dma;
+
+	ops = dma_dev_ops(dev_dma);
+
+	if (ops->of_xlate)
+		ret = ops->of_xlate(dma, &args);
+	else
+		ret = dma_of_xlate_default(dma, &args);
+	if (ret) {
+		pr_err("of_xlate() failed: %d\n", ret);
+		return ret;
+	}
+
+	return dma_request(dev_dma, dma);
+}
+# endif /* OF_PLATDATA */
+
+int dma_get_by_name(struct udevice *dev, const char *name, struct dma *dma)
+{
+	int index;
+
+	debug("%s(dev=%p, name=%s, dma=%p)\n", __func__, dev, name, dma);
+
+	index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev),
+				      "dma-names", name);
+	if (index < 0) {
+		pr_err("fdt_stringlist_search() failed: %d\n", index);
+		return index;
+	}
+
+	return dma_get_by_index(dev, index, dma);
+}
+#endif /* OF_CONTROL */
+
+int dma_request(struct udevice *dev, struct dma *dma)
+{
+	struct dma_ops *ops = dma_dev_ops(dev);
+
+	debug("%s(dev=%p, dma=%p)\n", __func__, dev, dma);
+
+	dma->dev = dev;
+
+	if (!ops->request)
+		return 0;
+
+	return ops->request(dma);
+}
+
+int dma_free(struct dma *dma)
+{
+	struct dma_ops *ops = dma_dev_ops(dma->dev);
+
+	debug("%s(dma=%p)\n", __func__, dma);
+
+	if (!ops->free)
+		return 0;
+
+	return ops->free(dma);
+}
+
+int dma_enable(struct dma *dma)
+{
+	struct dma_ops *ops = dma_dev_ops(dma->dev);
+
+	debug("%s(dma=%p)\n", __func__, dma);
+
+	if (!ops->enable)
+		return -ENOSYS;
+
+	return ops->enable(dma);
+}
+
+int dma_disable(struct dma *dma)
+{
+	struct dma_ops *ops = dma_dev_ops(dma->dev);
+
+	debug("%s(dma=%p)\n", __func__, dma);
+
+	if (!ops->disable)
+		return -ENOSYS;
+
+	return ops->disable(dma);
+}
+
+int dma_memcpy(struct dma *dma, void *dst, void *src, size_t len)
+{
+	struct dma_ops *ops = dma_dev_ops(dma->dev);
+
+	debug("%s(dma=%p)\n", __func__, dma);
 
-	ops = device_get_ops(dev);
 	if (!ops->transfer)
 		return -ENOSYS;
 
@@ -61,12 +178,35 @@  int dma_memcpy(void *dst, void *src, size_t len)
 	invalidate_dcache_range((unsigned long)dst, (unsigned long)dst +
 				roundup(len, ARCH_DMA_MINALIGN));
 
-	return ops->transfer(dev, DMA_MEM_TO_MEM, dst, src, len);
+	return ops->transfer(dma, DMA_MEM_TO_MEM, dst, src, len);
+}
+
+int dma_receive(struct dma *dma, void **dst)
+{
+	struct dma_ops *ops = dma_dev_ops(dma->dev);
+
+	debug("%s(dma=%p)\n", __func__, dma);
+
+	if (!ops->receive)
+		return -1;
+
+	return ops->receive(dma, dst);
+}
+
+int dma_send(struct dma *dma, void *src, size_t len)
+{
+	struct dma_ops *ops = dma_dev_ops(dma->dev);
+
+	debug("%s(dma=%p)\n", __func__, dma);
+
+	if (!ops->send)
+		return -1;
+
+	return ops->send(dma, src, len);
 }
 
 UCLASS_DRIVER(dma) = {
-	.id		= UCLASS_DMA,
-	.name		= "dma",
-	.flags		= DM_UC_FLAG_SEQ_ALIAS,
-	.per_device_auto_alloc_size = sizeof(struct dma_dev_priv),
+	.id = UCLASS_DMA,
+	.name = "dma",
+	.flags = DM_UC_FLAG_SEQ_ALIAS,
 };
diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c
index 83876485fe..992dead1a2 100644
--- a/drivers/mtd/spi/sf-uclass.c
+++ b/drivers/mtd/spi/sf-uclass.c
@@ -6,6 +6,7 @@ 
 
 #include <common.h>
 #include <dm.h>
+#include <dma.h>
 #include <spi.h>
 #include <spi_flash.h>
 #include <dm/device-internal.h>
@@ -72,6 +73,22 @@  int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
 		return ret;
 
 	*devp = slave->dev;
+
+#ifdef CONFIG_DMA
+	if (fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(*devp),
+				  "dmas") > 0) {
+		struct spi_flash *priv = dev_get_uclass_priv(*devp);
+
+		priv->dma = calloc(1, sizeof(struct dma));
+		if (!priv->dma)
+			return -ENOMEM;
+
+		ret = dma_get_by_index(*devp, 0, priv->dma);
+		if (ret)
+			return -EINVAL;
+	}
+#endif
+
 	return 0;
 }
 
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 294d9f9d79..8b1c6a27de 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -458,10 +458,6 @@  int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
  */
 void __weak spi_flash_copy_mmap(void *data, void *offset, size_t len)
 {
-#ifdef CONFIG_DMA
-	if (!dma_memcpy(data, offset, len))
-		return;
-#endif
 	memcpy(data, offset, len);
 }
 
@@ -482,7 +478,12 @@  int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 			return ret;
 		}
 		spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP);
-		spi_flash_copy_mmap(data, flash->memory_map + offset, len);
+		if (flash->dma)
+			dma_memcpy(flash->dma, data,
+				   flash->memory_map + offset, len);
+		else
+			spi_flash_copy_mmap(data, flash->memory_map + offset,
+					    len);
 		spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END);
 		spi_release_bus(spi);
 		return 0;
diff --git a/include/dma-uclass.h b/include/dma-uclass.h
new file mode 100644
index 0000000000..0315e54441
--- /dev/null
+++ b/include/dma-uclass.h
@@ -0,0 +1,110 @@ 
+/*
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
+ * Copyright (C) 2015 Texas Instruments Incorporated, <www.ti.com>
+ * Written by Mugunthan V N <mugunthanvnm@ti.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DMA_UCLASS_H
+#define _DMA_UCLASS_H
+
+/* See dma.h for background documentation. */
+
+#include <dma.h>
+#include <fdtdec.h>
+
+/**
+ * struct dma_ops - The functions that a DMA driver must implement.
+ */
+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 fdtdec_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);
+	/**
+	 * free - 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 (*free)(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);
+	/**
+	 * transfer() - Issue a DMA transfer.
+	 *
+	 * @dma: The DMA Channel to manipulate.
+	 * @dir: The direction of data transfer.
+	 * @dst: The destination pointer.
+	 * @src: The source pointer.
+	 * @len: Length of the data to be copied.
+	 * @return zero on success, or -ve error code.
+	 */
+	int (*transfer)(struct dma *dma, enum dma_transfer_direction dir,
+			void *dst, void *src, size_t len);
+	/**
+	 * receive() - Receive a DMA transfer.
+	 *
+	 * @dma: The DMA Channel to manipulate.
+	 * @dst: The destination pointer.
+	 * @return zero on success, or -ve error code.
+	 */
+	int (*receive)(struct dma *dma, void **dst);
+	/**
+	 * send() - Send a DMA transfer.
+	 *
+	 * @dma: The DMA Channel to manipulate.
+	 * @src: The source pointer.
+	 * @len: Length of the data to be copied.
+	 * @return zero on success, or -ve error code.
+	 */
+	int (*send)(struct dma *dma, void *src, size_t len);
+
+};
+
+#endif /* _DMA_UCLASS_H */
diff --git a/include/dma.h b/include/dma.h
index 71fa77f2ea..3632e3256d 100644
--- a/include/dma.h
+++ b/include/dma.h
@@ -1,86 +1,206 @@ 
 /*
- * (C) Copyright 2015
- *     Texas Instruments Incorporated, <www.ti.com>
+ * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
+ * Copyright (C) 2015 Texas Instruments Incorporated, <www.ti.com>
+ * Written by Mugunthan V N <mugunthanvnm@ti.com>
  *
- * SPDX-License-Identifier:     GPL-2.0+
+ * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #ifndef _DMA_H_
 #define _DMA_H_
 
+#include <linux/errno.h>
+#include <linux/types.h>
+
 /*
- * enum dma_direction - dma transfer direction indicator
+ * enum dma_transfer_direction - dma transfer direction indicator
  * @DMA_MEM_TO_MEM: Memcpy mode
  * @DMA_MEM_TO_DEV: From Memory to Device
  * @DMA_DEV_TO_MEM: From Device to Memory
  * @DMA_DEV_TO_DEV: From Device to Device
  */
-enum dma_direction {
+enum dma_transfer_direction {
 	DMA_MEM_TO_MEM,
 	DMA_MEM_TO_DEV,
 	DMA_DEV_TO_MEM,
 	DMA_DEV_TO_DEV,
 };
 
-#define DMA_SUPPORTS_MEM_TO_MEM	BIT(0)
-#define DMA_SUPPORTS_MEM_TO_DEV	BIT(1)
-#define DMA_SUPPORTS_DEV_TO_MEM	BIT(2)
-#define DMA_SUPPORTS_DEV_TO_DEV	BIT(3)
+/**
+ * A DMA is a feature of computer systems that allows certain hardware 
+ * subsystems to access main system memory, independent of the CPU.
+ * DMA channels are typically generated externally to the HW module
+ * consuming them, by an entity this API calls a DMA provider. This API
+ * provides a standard means for drivers to enable and disable DMAs, and to
+ * copy, send and receive data using DMA.
+ * 
+ * A driver that implements UCLASS_DMA is a DMA provider. A provider will
+ * often implement multiple separate DMAs, since the hardware it manages
+ * often has this capability. dma_uclass.h describes the interface which
+ * DMA providers must implement.
+ * 
+ * DMA consumers/clients are the HW modules driven by the DMA channels. This
+ * header file describes the API used by drivers for those HW modules.
+ */
 
-/*
- * struct dma_ops - Driver model DMA operations
+struct udevice;
+
+/**
+ * struct dma - A handle to (allowing control of) a single DMA.
+ *
+ * Clients provide storage for DMA handles. The content of the structure is
+ * managed solely by the DMA API and DMA drivers. A DMA struct is
+ * initialized by "get"ing the DMA struct. The DMA struct is passed to all
+ * other DMA APIs to identify which DMA channel to operate upon.
  *
- * The uclass interface is implemented by all DMA devices which use
- * driver model.
+ * @dev: The device which implements the DMA channel.
+ * @id: The DMA channel ID within the provider.
+ *
+ * Currently, the DMA API assumes that a single integer ID is enough to
+ * identify and configure any DMA channel for any DMA provider. If this
+ * assumption becomes invalid in the future, the struct could be expanded to
+ * either (a) add more fields to allow DMA providers to store additional
+ * information, or (b) replace the id field with an opaque pointer, which the
+ * provider would dynamically allocated during its .of_xlate op, and process
+ * during is .request op. This may require the addition of an extra op to clean
+ * up the allocation.
  */
-struct dma_ops {
+struct dma {
+	struct udevice *dev;
 	/*
-	 * Get the current timer count
-	 *
-	 * @dev: The DMA device
-	 * @direction: direction of data transfer should be one from
-		       enum dma_direction
-	 * @dst: Destination pointer
-	 * @src: Source pointer
-	 * @len: Length of the data to be copied.
-	 * @return: 0 if OK, -ve on error
+	 * Written by of_xlate. We assume a single id is enough for now. In the
+	 * future, we might add more fields here.
 	 */
-	int (*transfer)(struct udevice *dev, int direction, void *dst,
-			void *src, size_t len);
+	unsigned long id;
 };
 
-/*
- * struct dma_dev_priv - information about a device used by the uclass
+#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DMA)
+struct phandle_2_cell;
+int dma_get_by_index_platdata(struct udevice *dev, int index,
+			      struct phandle_2_cell *cells, struct dma *dma);
+
+/**
+ * dma_get_by_index - Get/request a DMA by integer index.
+ *
+ * This looks up and requests a DMA. The index is relative to the client
+ * device; each device is assumed to have n DMAs associated with it somehow,
+ * and this function finds and requests one of them. The mapping of client
+ * device DMA indices to provider DMAs may be via device-tree properties,
+ * board-provided mapping tables, or some other mechanism.
  *
- * @supported: mode of transfers that DMA can support, should be
- *	       one/multiple of DMA_SUPPORTS_*
+ * @dev:	The client device.
+ * @index:	The index of the DMA to request, within the client's list of
+ *		DMA channels.
+ * @dma:	A pointer to a DMA struct to initialize.
+ * @return 0 if OK, or a negative error code.
  */
-struct dma_dev_priv {
-	u32 supported;
-};
+int dma_get_by_index(struct udevice *dev, int index, struct dma *dma);
 
-/*
- * dma_get_device - get a DMA device which supports transfer
- * type of transfer_type
- *
- * @transfer_type - transfer type should be one/multiple of
- *		    DMA_SUPPORTS_*
- * @devp - udevice pointer to return the found device
- * @return - will return on success and devp will hold the
- *	     pointer to the device
+/**
+ * dma_get_by_name - Get/request a DMA by name.
+ *
+ * This looks up and requests a DMA. The name is relative to the client
+ * device; each device is assumed to have n DMAs associated with it somehow,
+ * and this function finds and requests one of them. The mapping of client
+ * device DMA names to provider DMAs may be via device-tree properties,
+ * board-provided mapping tables, or some other mechanism.
+ *
+ * @dev:	The client device.
+ * @name:	The name of the DMA to request, within the client's list of
+ *		DMA channels.
+ * @dma:	A pointer to a DMA struct to initialize.
+ * @return 0 if OK, or a negative error code.
  */
-int dma_get_device(u32 transfer_type, struct udevice **devp);
+int dma_get_by_name(struct udevice *dev, const char *name, struct dma *dma);
+#else
+static inline int dma_get_by_index(struct udevice *dev, int index,
+				   struct dma *dma)
+{
+	return -ENOSYS;
+}
 
-/*
- * dma_memcpy - try to use DMA to do a mem copy which will be
- *		much faster than CPU mem copy
- *
- * @dst - destination pointer
- * @src - souce pointer
- * @len - data length to be copied
- * @return - on successful transfer returns no of bytes
-	     transferred and on failure return error code.
+static inline int dma_get_by_name(struct udevice *dev, const char *name,
+			   struct dma *dma)
+{
+	return -ENOSYS;
+}
+#endif
+
+/**
+ * dma_request - Request a DMA by provider-specific ID.
+ *
+ * This requests a DMA using a provider-specific ID. Generally, this function
+ * should not be used, since dma_get_by_index/name() provide an interface that
+ * better separates clients from intimate knowledge of DMA providers.
+ * However, this function may be useful in core SoC-specific code.
+ *
+ * @dev: The DMA provider device.
+ * @dma: A pointer to a DMA struct to initialize. The caller must
+ *	 have already initialized any field in this struct which the
+ *	 DMA provider uses to identify the DMA channel.
+ * @return 0 if OK, or a negative error code.
+ */
+int dma_request(struct udevice *dev, struct dma *dma);
+
+/**
+ * dma_free - Free a previously requested DMA.
+ *
+ * @dma: A DMA struct that was previously successfully requested by
+ *	 dma_request/get_by_*().
+ * @return 0 if OK, or a negative error code.
+ */
+int dma_free(struct dma *dma);
+
+/**
+ * dma_enable() - Enable (turn on) a DMA channel.
+ *
+ * @dma: A DMA struct that was previously successfully requested by
+ *	 dma_request/get_by_*().
+ * @return zero on success, or -ve error code.
+ */
+int dma_enable(struct dma *dma);
+
+/**
+ * dma_disable() - Disable (turn off) a DMA channel.
+ *
+ * @dma: A DMA struct that was previously successfully requested by
+ *	 dma_request/get_by_*().
+ * @return zero on success, or -ve error code.
+ */
+int dma_disable(struct dma *dma);
+
+/**
+ * dma_memcpy() - Try to use DMA to do a mem copy which will be much faster
+ *		  than CPU mem copy.
+ *
+ * @dma: A DMA struct that was previously successfully requested by
+ *	 dma_request/get_by_*().
+ * @dst: The destination pointer.
+ * @src: The source pointer.
+ * @len: Length of the data to be copied.
+ * @return zero on success, or -ve error code.
+ */
+int dma_memcpy(struct dma *dma, void *dst, void *src, size_t len);
+
+/**
+ * dma_receive() - Receive a DMA transfer.
+ *
+ * @dma: A DMA struct that was previously successfully requested by
+ *	 dma_request/get_by_*().
+ * @dst: The destination pointer.
+ * @return zero on success, or -ve error code.
+ */
+int dma_receive(struct dma *dma, void **dst);
+
+/**
+ * dma_send() - Send a DMA transfer.
+ *
+ * @dma: A DMA struct that was previously successfully requested by
+ *	 dma_request/get_by_*().
+ * @src: The source pointer.
+ * @len: Length of the data to be copied.
+ * @return zero on success, or -ve error code.
  */
-int dma_memcpy(void *dst, void *src, size_t len);
+int dma_send(struct dma *dma, void *src, size_t len);
 
-#endif	/* _DMA_H_ */
+#endif /* _DMA_H_ */
diff --git a/include/spi_flash.h b/include/spi_flash.h
index f3c4e83424..f31e8b8145 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -65,6 +65,9 @@  struct spi_flash {
 #ifdef CONFIG_DM_SPI_FLASH
 	struct udevice *dev;
 #endif
+#ifdef CONFIG_DMA
+	struct dma *dma;
+#endif
 	const char *name;
 	u8 dual_flash;
 	u8 shift;