diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt new file mode 100644 index 000000000000..a4f59a5967d4 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -0,0 +1,81 @@ +* 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 number of integer cells, as determined by the + #dma-cells property in the node referenced by phandle + 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 three 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 e027f444d10c..eafa107aed40 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 000000000000..19ad37c066f5 --- /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 +#include +#include +#include +#include +#include +#include + +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 000000000000..337823dc6b90 --- /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 +#include + +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 */