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@xxxxxxxxx> I like it a lot. Reviewed-by: Nicolas Ferre <nicolas.ferre@xxxxxxxxx> Thanks to all of you for making this happen! > Cc: Benoit Cousson <b-cousson@xxxxxx> > Cc: Stephen Warren <swarren@xxxxxxxxxx> > Cc: Grant Likely <grant.likely@xxxxxxxxxxxx> > Cc: Russell King <linux@xxxxxxxxxxxxxxxx> > Cc: Rob Herring <rob.herring@xxxxxxxxxxx> > Cc: Arnd Bergmann <arnd@xxxxxxxx> > Cc: Vinod Koul <vinod.koul@xxxxxxxxx> > Cc: Dan Williams <djbw@xxxxxx> > > Signed-off-by: Jon Hunter <jon-hunter@xxxxxx> > --- > 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@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@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 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html