[PATCH V5 1/2] of: Add generic device tree DMA helpers

Nicolas Ferre nicolas.ferre at atmel.com
Fri Sep 14 11:37:37 EDT 2012


On 09/14/2012 05:18 PM, Jon Hunter :
> This is based upon the work by Benoit Cousson [1] and Nicolas Ferre [2]
> to add some basic helpers to retrieve a DMA controller device_node and the
> DMA request/channel information.
> 
> Aim of DMA helpers
> - The purpose of device-tree is to describe the capabilites of the hardware.
>   Thinking about DMA controllers purely from the context of the hardware to
>   begin with, we can describe a device in terms of a DMA controller as
>   follows ...
>   	1. Number of DMA controllers
> 	2. Number of channels (maybe physical or logical)
> 	3. Mapping of DMA requests signals to DMA controller
> 	4. Number of DMA interrupts
> 	5. Mapping of DMA interrupts to channels
> - With the above in mind the aim of the DT DMA helper functions is to extract
>   the above information from the DT and provide to the appropriate driver.
>   However, due to the vast number of DMA controllers and not all are using a
>   common driver (such as DMA Engine) it has been seen that this is not a
>   trivial task. In previous discussions on this topic the following concerns
>   have been raised ...
> 	1. How does the binding support devices with multiple DMA controllers?
>   	2. How to support both legacy DMA controllers not using DMA Engine as
> 	   well as those that support DMA Engine.
> 	3. When using with DMA Engine how do we support the various
> 	   implementations where the opaque filter function parameter differs
> 	   between implementations?
> 	4. How do we handle DMA channels that are identified with a string
> 	   versus a integer?
> - Hence the design of the DMA helpers has to accomodate the above or align on
>   an agreement what can be or should be supported.
> 
> Design of DMA helpers
> 
> 1. Registering DMA controllers
> 
>    In the case of DMA controllers that are using DMA Engine, requesting a
>    channel is performed by calling the following function.
> 
> 	struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
> 			dma_filter_fn filter_fn,
> 			void *filter_param);
> 
>    The mask variable is used to match a type of the device controller in a list
>    of controllers. The filter_fn and filter_param are used to identify the
>    required dma channel and return a handle to the dma channel of type dma_chan.
> 
>    From the examples I have seen, the mask and filter_fn are constant
>    for a given DMA controller and therefore, we can specify these as controller
>    specific data when registering the DMA controller with the device-tree DMA
>    helpers.
> 
>    The filter_param variable is of an unknown type and is typically specific
>    to the DMA engine implementation for a given DMA controller. To allow some
>    flexibility in the type and formating of this filter_param we employ an
>    xlate to translate the device-tree binding information into the appropriate
>    format. The xlate function used for a DMA controller can also be specified
>    when registering the DMA controller with the device-tree DMA helpers.
> 
>    Based upon the above, a function for registering the DMA controller with the
>    DMA helpers now looks like the below. The data variable is used to pass a
>    pointer to DMA controller specific data used by the xlate function.
> 
> 	int of_dma_controller_register(struct device_node *np,
> 		struct dma_chan *(*of_dma_xlate)
> 		(struct of_phandle_args *, struct of_dma *),
> 		void *data)
> 
>    For example, in the case where DMA engine is used, we define the following
>    structure (that stores the DMA engine capability mask and filter function)
>    and pass this to the data variable in the above function.
> 
> 	struct of_dma_filter_info {
> 		dma_cap_mask_t  dma_cap;
> 		dma_filter_fn   filter_fn;
> 	};
> 
> 2. Representing and requesting channel information
> 
>    Please see the dma binding documentation included in this patch for a
>    description of how DMA controllers and client information should be
>    represented with device-tree. For more information on how this binding
>    came about please see [3]. In addition to this, feedback received from
>    the Linux kernel summit showed a consensus (among those who attended) to
>    use a name to identify DMA client information [4].
> 
>    A DMA channel can be requested by calling the following function, where name
>    is a required parameter used for identifying a DMA channel. This function
>    has been designed to return a structure of type dma_chan to work with the
>    DMA engine driver. Note that if DMA engine is used then drivers should be
>    using the DMA engine API dma_request_slave_channel() (implemented in part 2
>    of this series, "dmaengine: add helper function to request a slave DMA
>    channel") which will in turn call the below function if device-tree is
>    present. The aim being to have a common DMA engine interface regardless of
>    whether device tree is being used.
> 
> 	struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
> 						      char *name)
> 
> 3. Supporting legacy devices not using DMA Engine
> 
>    These devices present a problem, as there may not be a uniform way to easily
>    support them with regard to device tree. Ideally, these should be migrated
>    to DMA engine. However, if this is not possible, then they should still be
>    able to use this binding, the only constaint imposed by this implementation
>    is that when requesting a DMA channel via of_dma_request_slave_channel(), it
>    will return a type of dma_chan.
> 
> This implementation has been tested on OMAP4430 using the kernel v3.6-rc5. I
> have validated that MMC is working on the PANDA board with this implementation.
> My development branch for testing on OMAP can be found here [5].
> 
> v5: - minor update to binding documentation
>     - added loop to exhaustively search for a slave channel in the case where
>       there could be alternative channels available
> v4: - revert the removal of xlate function from v3
>     - update the proposed binding format and APIs based upon discussions [3]
> v3: - avoid passing an xlate function and instead pass DMA engine parameters
>     - define number of dma channels and requests in dma-controller node
> v2: - remove of_dma_to_resource API
>     - make property #dma-cells required (no fallback anymore)
>     - another check in of_dma_xlate_onenumbercell() function
> 
> [1] http://article.gmane.org/gmane.linux.drivers.devicetree/12022
> [2] http://article.gmane.org/gmane.linux.ports.arm.omap/73622
> [3] http://marc.info/?l=linux-omap&m=133582085008539&w=2
> [4] http://pad.linaro.org/arm-mini-summit-2012
> [5] https://github.com/jonhunter/linux/tree/dev-dt-dma
> 
> Cc: Nicolas Ferre <nicolas.ferre at atmel.com>

I like it a lot.

Reviewed-by: Nicolas Ferre <nicolas.ferre at atmel.com>

Thanks to all of you for making this happen!


> Cc: Benoit Cousson <b-cousson at ti.com>
> Cc: Stephen Warren <swarren at nvidia.com>
> Cc: Grant Likely <grant.likely at secretlab.ca>
> Cc: Russell King <linux at arm.linux.org.uk>
> Cc: Rob Herring <rob.herring at calxeda.com>
> Cc: Arnd Bergmann <arnd at arndb.de>
> Cc: Vinod Koul <vinod.koul at intel.com>
> Cc: Dan Williams <djbw at fb.com>
> 
> Signed-off-by: Jon Hunter <jon-hunter at ti.com>
> ---
>  Documentation/devicetree/bindings/dma/dma.txt |   80 +++++++++
>  drivers/of/Makefile                           |    2 +-
>  drivers/of/dma.c                              |  219 +++++++++++++++++++++++++
>  include/linux/of_dma.h                        |   45 +++++
>  4 files changed, 345 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/dma/dma.txt
>  create mode 100644 drivers/of/dma.c
>  create mode 100644 include/linux/of_dma.h
> 
> diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt
> new file mode 100644
> index 0000000..c5f8430
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/dma.txt
> @@ -0,0 +1,80 @@
> +* Generic DMA Controller and DMA request bindings
> +
> +Generic binding to provide a way for a driver using DMA Engine to retrieve the
> +DMA request or channel information that goes from a hardware device to a DMA
> +controller.
> +
> +
> +* DMA controller
> +
> +Required property:
> +- #dma-cells: 		Must be at least 1. Used to provide DMA controller
> +			specific information. See DMA client binding below for
> +			more details.
> +
> +Optional properties:
> +- #dma-channels: 	Number of DMA channels supported by the controller.
> +- #dma-requests: 	Number of DMA requests signals supported by the
> +			controller.
> +
> +Example:
> +
> +	dma: dma at 48000000 {
> +		compatible = "ti,omap-sdma"
> +		reg = <0x48000000 0x1000>;
> +		interrupts = <0 12 0x4
> +			      0 13 0x4
> +			      0 14 0x4
> +			      0 15 0x4>;
> +		#dma-cells = <1>;
> +		#dma-channels = <32>;
> +		#dma-requests = <127>;
> +	};
> +
> +
> +* DMA client
> +
> +Client drivers should specify the DMA property using a phandle to the controller
> +followed by DMA controller specific data.
> +
> +Required property:
> +- dmas:			List of one or more DMA specifiers, each consisting of
> +			- A phandle pointing to DMA controller node
> +			- A single integer cell containing DMA controller
> +			  specific information. This typically contains a dma
> +			  request line number or a channel number, but can
> +			  contain any data that is used required for configuring
> +			  a channel.
> +- dma-names: 		Contains one identifier string for each DMA specifier in
> +			the dmas property. The specific strings that can be used
> +			are defined in the binding of the DMA client device.
> +			Multiple DMA specifiers can be used to represent
> +			alternatives and in this case the dma-names for those
> +			DMA specifiers must be identical (see examples).
> +
> +Examples:
> +
> +1. A device with one DMA read channel, one DMA write channel:
> +
> +	i2c1: i2c at 1 {
> +		...
> +		dmas = <&dma 2		/* read channel */
> +			&dma 3>;	/* write channel */
> +		dma-names = "rx", "tx"
> +		...
> +	};
> +
> +2. A single read-write channel with two alternative dma controllers:
> +
> +	dmas = <&dma1 5
> +		&dma2 7
> +		&dma3 2>;
> +	dma-names = "rx-tx", "rx-tx", "rx-tx"
> +
> +3. A device with three channels, one of which has two alternatives:
> +
> +	dmas = <&dma1 2			/* read channel */
> +		&dma1 3			/* write channel */
> +		&dma2 0			/* error read */
> +		&dma3 0>;		/* alternative error read */
> +	dma-names = "rx", "tx", "error", "error";
> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> index e027f44..eafa107 100644
> --- a/drivers/of/Makefile
> +++ b/drivers/of/Makefile
> @@ -1,4 +1,4 @@
> -obj-y = base.o
> +obj-y = base.o dma.o
>  obj-$(CONFIG_OF_FLATTREE) += fdt.o
>  obj-$(CONFIG_OF_PROMTREE) += pdt.o
>  obj-$(CONFIG_OF_ADDRESS)  += address.o
> diff --git a/drivers/of/dma.c b/drivers/of/dma.c
> new file mode 100644
> index 0000000..19ad37c
> --- /dev/null
> +++ b/drivers/of/dma.c
> @@ -0,0 +1,219 @@
> +/*
> + * Device tree helpers for DMA request / controller
> + *
> + * Based on of_gpio.c
> + *
> + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/rculist.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_dma.h>
> +
> +static LIST_HEAD(of_dma_list);
> +
> +/**
> + * of_dma_find_controller - Find a DMA controller in DT DMA helpers list
> + * @np:		device node of DMA controller
> + */
> +static struct of_dma *of_dma_find_controller(struct device_node *np)
> +{
> +	struct of_dma *ofdma;
> +
> +	if (list_empty(&of_dma_list)) {
> +		pr_err("empty DMA controller list\n");
> +		return NULL;
> +	}
> +
> +	list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers)
> +		if (ofdma->of_node == np)
> +			return ofdma;
> +
> +	return NULL;
> +}
> +
> +/**
> + * of_dma_controller_register - Register a DMA controller to DT DMA helpers
> + * @np:			device node of DMA controller
> + * @of_dma_xlate:	translation function which converts a phandle
> + *			arguments list into a dma_chan structure
> + * @data		pointer to controller specific data to be used by
> + *			translation function
> + *
> + * Returns 0 on success or appropriate errno value on error.
> + *
> + * Allocated memory should be freed with appropriate of_dma_controller_free()
> + * call.
> + */
> +int of_dma_controller_register(struct device_node *np,
> +				struct dma_chan *(*of_dma_xlate)
> +				(struct of_phandle_args *, struct of_dma *),
> +				void *data)
> +{
> +	struct of_dma	*ofdma;
> +	int		nbcells;
> +
> +	if (!np || !of_dma_xlate) {
> +		pr_err("%s: not enough information provided\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL);
> +	if (!ofdma)
> +		return -ENOMEM;
> +
> +	nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL));
> +	if (!nbcells) {
> +		pr_err("%s: #dma-cells property is missing or invalid\n",
> +		       __func__);
> +		return -EINVAL;
> +	}
> +
> +	ofdma->of_node = np;
> +	ofdma->of_dma_nbcells = nbcells;
> +	ofdma->of_dma_xlate = of_dma_xlate;
> +	ofdma->of_dma_data = data;
> +
> +	/* Now queue of_dma controller structure in list */
> +	list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_dma_controller_register);
> +
> +/**
> + * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list
> + * @np:		device node of DMA controller
> + *
> + * Memory allocated by of_dma_controller_register() is freed here.
> + */
> +void of_dma_controller_free(struct device_node *np)
> +{
> +	struct of_dma *ofdma;
> +
> +	ofdma = of_dma_find_controller(np);
> +	if (ofdma) {
> +		list_del_rcu(&ofdma->of_dma_controllers);
> +		kfree(ofdma);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(of_dma_controller_free);
> +
> +/**
> + * of_dma_find_channel - Find a DMA channel by name
> + * @np:		device node to look for DMA channels
> + * @name:	name of desired channel
> + * @dma_spec:	pointer to DMA specifier as found in the device tree
> + *
> + * Find a DMA channel by the name. Returns 0 on success or appropriate
> + * errno value on error.
> + */
> +static int of_dma_find_channel(struct device_node *np, char *name,
> +			       struct of_phandle_args *dma_spec)
> +{
> +	int count, i;
> +	const char *s;
> +
> +	count = of_property_count_strings(np, "dma-names");
> +	if (count < 0)
> +		return count;
> +
> +	for (i = 0; i < count; i++) {
> +		if (of_property_read_string_index(np, "dma-names", i, &s))
> +			continue;
> +
> +		if (strcmp(name, s))
> +			continue;
> +
> +		if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i,
> +						dma_spec))
> +			return 0;
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +/**
> + * of_dma_request_slave_channel - Get the DMA slave channel
> + * @np:		device node to get DMA request from
> + * @name:	name of desired channel
> + *
> + * Returns pointer to appropriate dma channel on success or NULL on error.
> + */
> +struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
> +					      char *name)
> +{
> +	struct of_phandle_args	dma_spec;
> +	struct of_dma		*ofdma;
> +	struct dma_chan		*chan;
> +	int			r;
> +
> +	if (!np || !name) {
> +		pr_err("%s: not enough information provided\n", __func__);
> +		return NULL;
> +	}
> +
> +	do {
> +		r = of_dma_find_channel(np, name, &dma_spec);
> +		if (r) {
> +			pr_err("%s: can't find DMA channel\n", np->full_name);
> +			return NULL;
> +		}
> +
> +		ofdma = of_dma_find_controller(dma_spec.np);
> +		if (!ofdma) {
> +			pr_debug("%s: can't find DMA controller %s\n",
> +				 np->full_name, dma_spec.np->full_name);
> +			continue;
> +		}
> +
> +		if (dma_spec.args_count != ofdma->of_dma_nbcells) {
> +			pr_debug("%s: wrong #dma-cells for %s\n", np->full_name,
> +				 dma_spec.np->full_name);
> +			continue;
> +		}
> +
> +		chan = ofdma->of_dma_xlate(&dma_spec, ofdma);
> +
> +		of_node_put(dma_spec.np);
> +
> +	} while (!chan);
> +
> +	return chan;
> +}
> +
> +/**
> + * of_dma_simple_xlate - Simple DMA engine translation function
> + * @dma_spec:	pointer to DMA specifier as found in the device tree
> + * @of_dma:	pointer to DMA controller data
> + *
> + * A simple translation function for devices that use a 32-bit value for the
> + * filter_param when calling the DMA engine dma_request_channel() function.
> + * Note that this translation function requires that #dma-cells is equal to 1
> + * and the argument of the dma specifier is the 32-bit filter_param. Returns
> + * pointer to appropriate dma channel on success or NULL on error.
> + */
> +struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
> +						struct of_dma *ofdma)
> +{
> +	int count = dma_spec->args_count;
> +	struct of_dma_filter_info *info = ofdma->of_dma_data;
> +
> +	if (!info || !info->filter_fn)
> +		return NULL;
> +
> +	if (count != 1)
> +		return NULL;
> +
> +	return dma_request_channel(info->dma_cap, info->filter_fn,
> +			&dma_spec->args[0]);
> +}
> +EXPORT_SYMBOL_GPL(of_dma_simple_xlate);
> diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h
> new file mode 100644
> index 0000000..337823d
> --- /dev/null
> +++ b/include/linux/of_dma.h
> @@ -0,0 +1,45 @@
> +/*
> + * OF helpers for DMA request / controller
> + *
> + * Based on of_gpio.h
> + *
> + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __LINUX_OF_DMA_H
> +#define __LINUX_OF_DMA_H
> +
> +#include <linux/of.h>
> +#include <linux/dmaengine.h>
> +
> +struct device_node;
> +
> +struct of_dma {
> +	struct list_head	of_dma_controllers;
> +	struct device_node	*of_node;
> +	int			of_dma_nbcells;
> +	struct dma_chan		*(*of_dma_xlate)
> +				(struct of_phandle_args *, struct of_dma *);
> +	void			*of_dma_data;
> +};
> +
> +struct of_dma_filter_info {
> +	dma_cap_mask_t	dma_cap;
> +	dma_filter_fn	filter_fn;
> +};
> +
> +extern int of_dma_controller_register(struct device_node *np,
> +		struct dma_chan *(*of_dma_xlate)
> +		(struct of_phandle_args *, struct of_dma *),
> +		void *data);
> +extern void of_dma_controller_free(struct device_node *np);
> +extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
> +						     char *name);
> +extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
> +		struct of_dma *ofdma);
> +
> +#endif /* __LINUX_OF_DMA_H */
> 


-- 
Nicolas Ferre



More information about the linux-arm-kernel mailing list