From 9a6cecc846169159bfce511f4c0034bb96eea1ca Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 14 Sep 2012 17:41:57 -0500 Subject: dmaengine: add helper function to request a slave DMA channel Currently slave DMA channels are requested by calling dma_request_channel() and requires DMA clients to pass various filter parameters to obtain the appropriate channel. With device-tree being used by architectures such as arm and the addition of device-tree helper functions to extract the relevant DMA client information from device-tree, add a new function to request a slave DMA channel using device-tree. This function is currently a simple wrapper that calls the device-tree of_dma_request_slave_channel() function. Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Acked-by: Arnd Bergmann Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d3201e438d16..8cd0e2556d04 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -974,6 +974,7 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie); enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); void dma_issue_pending_all(void); struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param); +struct dma_chan *dma_request_slave_channel(struct device *dev, char *name); void dma_release_channel(struct dma_chan *chan); #else static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx) @@ -988,6 +989,11 @@ static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, { return NULL; } +static inline struct dma_chan *dma_request_slave_channel(struct device *dev, + char *name) +{ + return NULL +} static inline void dma_release_channel(struct dma_chan *chan) { } -- cgit v1.2.3 From aa3da644c76d1c2083d085e453c18f7c51f19bc4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 14 Sep 2012 17:41:56 -0500 Subject: of: Add generic device tree DMA helpers 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]. v6: - minor corrections in DMA binding documentation 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 Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Reviewed-by: Arnd Bergmann Reviewed-by: Nicolas Ferre Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 81 ++++++++++ drivers/of/Makefile | 2 +- drivers/of/dma.c | 219 ++++++++++++++++++++++++++ include/linux/of_dma.h | 45 ++++++ 4 files changed, 346 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 (limited to 'include') 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 */ -- cgit v1.2.3 From 4c26bc601d20fa67eefcb27477feda130c10790e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 25 Sep 2012 09:57:36 +0530 Subject: of: dma- fix build break for !CONFIG_OF Signed-off-by: Vinod Koul --- include/linux/of_dma.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'include') diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 337823dc6b90..67158ddd1f3e 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -32,6 +32,7 @@ struct of_dma_filter_info { dma_filter_fn filter_fn; }; +#ifdef CONFIG_OF extern int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), @@ -41,5 +42,31 @@ 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); +#else +static int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + return -ENODEV; +} + +static void of_dma_controller_free(struct device_node *np) +{ +} + +static struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name) +{ + return NULL; +} + +static struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + return NULL; +} + +#endif #endif /* __LINUX_OF_DMA_H */ -- cgit v1.2.3 From d18d5f59797009d86a0ee46feb2a56d53707455a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 25 Sep 2012 16:18:55 +0530 Subject: dmaengine: fix build failure due to missing semi-colon Reported-by: Fengguang Wu Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8cd0e2556d04..c88f302d91c9 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -992,7 +992,7 @@ static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, static inline struct dma_chan *dma_request_slave_channel(struct device *dev, char *name) { - return NULL + return NULL; } static inline void dma_release_channel(struct dma_chan *chan) { -- cgit v1.2.3 From 177d2bf5c7d3ab41bfb4ce2597dde668225958dd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:16 +0530 Subject: dmaengine: dw_dmac: Update documentation style comments for dw_dma_platform_data Documentation style comments were missing for few fields in struct dw_dma_platform_data. Add these. Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko Signed-off-by: Vinod Koul --- include/linux/dw_dmac.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index e1c8c9e919ac..62a6190dee22 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -19,6 +19,8 @@ * @nr_channels: Number of channels supported by hardware (max 8) * @is_private: The device channels should be marked as private and not for * by the general purpose DMA channel allocator. + * @chan_allocation_order: Allocate channels starting from 0 or 7 + * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master -- cgit v1.2.3 From a9ddb575d6d6c58c39e8c44a22b84445fedb0521 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:17 +0530 Subject: dmaengine: dw_dmac: Enhance device tree support dw_dmac driver already supports device tree but it used to have its platform data passed the non-DT way. This patch does following changes: - pass platform data via DT, non-DT way still takes precedence if both are used. - create generic filter routine - Earlier slave information was made available by slave specific filter routines in chan->private field. Now, this information would be passed from within dmac DT node. Slave drivers would now be required to pass bus_id (a string) as parameter to this generic filter(), which would be compared against the slave data passed from DT, by the generic filter routine. - Update binding document Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko [Fixed __devinit usage] Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps-dma.txt | 44 +++++++ drivers/dma/dw_dmac.c | 134 +++++++++++++++++++++ drivers/dma/dw_dmac_regs.h | 4 + include/linux/dw_dmac.h | 43 ++++--- 4 files changed, 208 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index c0d85dbcada5..5bb3dfb6f1d8 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -6,6 +6,26 @@ Required properties: - interrupt-parent: Should be the phandle for the interrupt controller that services interrupts for this device - interrupt: Should contain the DMAC interrupt number +- nr_channels: Number of channels supported by hardware +- is_private: The device channels should be marked as private and not for by the + general purpose DMA channel allocator. False if not passed. +- chan_allocation_order: order of allocation of channel, 0 (default): ascending, + 1: descending +- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1: + increase from chan n->0 +- block_size: Maximum block size supported by the controller +- nr_masters: Number of AHB masters supported by the controller +- data_width: Maximum data width supported by hardware per AHB master + (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) +- slave_info: + - bus_id: name of this device channel, not just a device name since + devices may have more than one channel e.g. "foo_tx". For using the + dw_generic_filter(), slave drivers must pass exactly this string as + param to filter function. + - cfg_hi: Platform-specific initializer for the CFG_HI register + - cfg_lo: Platform-specific initializer for the CFG_LO register + - src_master: src master for transfers on allocated channel. + - dst_master: dest master for transfers on allocated channel. Example: @@ -14,4 +34,28 @@ Example: reg = <0xfc000000 0x1000>; interrupt-parent = <&vic1>; interrupts = <12>; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; + + slave_info { + uart0-tx { + bus_id = "uart0-tx"; + cfg_hi = <0x4000>; /* 0x8 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <1>; + }; + spi0-tx { + bus_id = "spi0-tx"; + cfg_hi = <0x2000>; /* 0x4 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + }; }; diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 8f0b111af4de..27b8e1d1845e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1179,6 +1179,50 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } +bool dw_dma_generic_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + static struct dw_dma *last_dw; + static char *last_bus_id; + int i = -1; + + /* + * dmaengine framework calls this routine for all channels of all dma + * controller, until true is returned. If 'param' bus_id is not + * registered with a dma controller (dw), then there is no need of + * running below function for all channels of dw. + * + * This block of code does this by saving the parameters of last + * failure. If dw and param are same, i.e. trying on same dw with + * different channel, return false. + */ + if ((last_dw == dw) && (last_bus_id == param)) + return false; + /* + * Return true: + * - If dw_dma's platform data is not filled with slave info, then all + * dma controllers are fine for transfer. + * - Or if param is NULL + */ + if (!dw->sd || !param) + return true; + + while (++i < dw->sd_count) { + if (!strcmp(dw->sd[i].bus_id, param)) { + chan->private = &dw->sd[i]; + last_dw = NULL; + last_bus_id = NULL; + + return true; + } + } + + last_dw = dw; + last_bus_id = param; + return false; +} +EXPORT_SYMBOL(dw_dma_generic_filter); + /* --------------------- Cyclic DMA API extensions -------------------- */ /** @@ -1462,6 +1506,91 @@ static void dw_dma_off(struct dw_dma *dw) dw->chan[i].initialized = false; } +#ifdef CONFIG_OF +static struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + struct device_node *sn, *cn, *np = pdev->dev.of_node; + struct dw_dma_platform_data *pdata; + struct dw_dma_slave *sd; + u32 tmp, arr[4]; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "nr_channels", &pdata->nr_channels)) + return NULL; + + if (of_property_read_bool(np, "is_private")) + pdata->is_private = true; + + if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) + pdata->chan_allocation_order = (unsigned char)tmp; + + if (!of_property_read_u32(np, "chan_priority", &tmp)) + pdata->chan_priority = tmp; + + if (!of_property_read_u32(np, "block_size", &tmp)) + pdata->block_size = tmp; + + if (!of_property_read_u32(np, "nr_masters", &tmp)) { + if (tmp > 4) + return NULL; + + pdata->nr_masters = tmp; + } + + if (!of_property_read_u32_array(np, "data_width", arr, + pdata->nr_masters)) + for (tmp = 0; tmp < pdata->nr_masters; tmp++) + pdata->data_width[tmp] = arr[tmp]; + + /* parse slave data */ + sn = of_find_node_by_name(np, "slave_info"); + if (!sn) + return pdata; + + /* calculate number of slaves */ + tmp = of_get_child_count(sn); + if (!tmp) + return NULL; + + sd = devm_kzalloc(&pdev->dev, sizeof(*sd) * tmp, GFP_KERNEL); + if (!sd) + return NULL; + + pdata->sd = sd; + pdata->sd_count = tmp; + + for_each_child_of_node(sn, cn) { + sd->dma_dev = &pdev->dev; + of_property_read_string(cn, "bus_id", &sd->bus_id); + of_property_read_u32(cn, "cfg_hi", &sd->cfg_hi); + of_property_read_u32(cn, "cfg_lo", &sd->cfg_lo); + if (!of_property_read_u32(cn, "src_master", &tmp)) + sd->src_master = tmp; + + if (!of_property_read_u32(cn, "dst_master", &tmp)) + sd->dst_master = tmp; + sd++; + } + + return pdata; +} +#else +static inline struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + static int dw_probe(struct platform_device *pdev) { struct dw_dma_platform_data *pdata; @@ -1478,6 +1607,9 @@ static int dw_probe(struct platform_device *pdev) int i; pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) return -EINVAL; @@ -1512,6 +1644,8 @@ static int dw_probe(struct platform_device *pdev) clk_prepare_enable(dw->clk); dw->regs = regs; + dw->sd = pdata->sd; + dw->sd_count = pdata->sd_count; /* get hardware configuration parameters */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88965597b7d0..88a069f66b89 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -239,6 +239,10 @@ struct dw_dma { struct tasklet_struct tasklet; struct clk *clk; + /* slave information */ + struct dw_dma_slave *sd; + unsigned int sd_count; + u8 all_chan_mask; /* hardware configuration */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 62a6190dee22..41766de66e33 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -14,6 +14,26 @@ #include +/** + * struct dw_dma_slave - Controller-specific information about a slave + * + * @dma_dev: required DMA master device. Depricated. + * @bus_id: name of this device channel, not just a device name since + * devices may have more than one channel e.g. "foo_tx" + * @cfg_hi: Platform-specific initializer for the CFG_HI register + * @cfg_lo: Platform-specific initializer for the CFG_LO register + * @src_master: src master for transfers on allocated channel. + * @dst_master: dest master for transfers on allocated channel. + */ +struct dw_dma_slave { + struct device *dma_dev; + const char *bus_id; + u32 cfg_hi; + u32 cfg_lo; + u8 src_master; + u8 dst_master; +}; + /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) @@ -25,6 +45,8 @@ * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master * (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) + * @sd: slave specific data. Used for configuring channels + * @sd_count: count of slave data structures passed. */ struct dw_dma_platform_data { unsigned int nr_channels; @@ -38,6 +60,9 @@ struct dw_dma_platform_data { unsigned short block_size; unsigned char nr_masters; unsigned char data_width[4]; + + struct dw_dma_slave *sd; + unsigned int sd_count; }; /* bursts size */ @@ -52,23 +77,6 @@ enum dw_dma_msize { DW_DMA_MSIZE_256, }; -/** - * struct dw_dma_slave - Controller-specific information about a slave - * - * @dma_dev: required DMA master device - * @cfg_hi: Platform-specific initializer for the CFG_HI register - * @cfg_lo: Platform-specific initializer for the CFG_LO register - * @src_master: src master for transfers on allocated channel. - * @dst_master: dest master for transfers on allocated channel. - */ -struct dw_dma_slave { - struct device *dma_dev; - u32 cfg_hi; - u32 cfg_lo; - u8 src_master; - u8 dst_master; -}; - /* Platform-configurable bits in CFG_HI */ #define DWC_CFGH_FCMODE (1 << 0) #define DWC_CFGH_FIFO_MODE (1 << 1) @@ -106,5 +114,6 @@ void dw_dma_cyclic_stop(struct dma_chan *chan); dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan); dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan); +bool dw_dma_generic_filter(struct dma_chan *chan, void *param); #endif /* DW_DMAC_H */ -- cgit v1.2.3 From e5a087fdc1ebe5bba40bcecb53c28a0af70e3b47 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 26 Oct 2012 23:35:15 +0900 Subject: dmaengine: use for_each_set_bit Use for_each_set_bit() to implement for_each_dma_cap_mask() and remove unused first_dma_cap() and next_dma_cap(). Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c88f302d91c9..4c8643794e0d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -849,20 +849,6 @@ static inline bool async_tx_test_ack(struct dma_async_tx_descriptor *tx) return (tx->flags & DMA_CTRL_ACK) == DMA_CTRL_ACK; } -#define first_dma_cap(mask) __first_dma_cap(&(mask)) -static inline int __first_dma_cap(const dma_cap_mask_t *srcp) -{ - return min_t(int, DMA_TX_TYPE_END, - find_first_bit(srcp->bits, DMA_TX_TYPE_END)); -} - -#define next_dma_cap(n, mask) __next_dma_cap((n), &(mask)) -static inline int __next_dma_cap(int n, const dma_cap_mask_t *srcp) -{ - return min_t(int, DMA_TX_TYPE_END, - find_next_bit(srcp->bits, DMA_TX_TYPE_END, n+1)); -} - #define dma_cap_set(tx, mask) __dma_cap_set((tx), &(mask)) static inline void __dma_cap_set(enum dma_transaction_type tx_type, dma_cap_mask_t *dstp) @@ -891,9 +877,7 @@ __dma_has_cap(enum dma_transaction_type tx_type, dma_cap_mask_t *srcp) } #define for_each_dma_cap_mask(cap, mask) \ - for ((cap) = first_dma_cap(mask); \ - (cap) < DMA_TX_TYPE_END; \ - (cap) = next_dma_cap((cap), (mask))) + for_each_set_bit(cap, mask.bits, DMA_TX_TYPE_END) /** * dma_async_issue_pending - flush pending transactions to HW -- cgit v1.2.3 From 9743a3b62dee8c9d8af1319f8d1c1ff39130267d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 11 Oct 2012 14:43:01 -0500 Subject: of: dma: fix protection of DMA controller data stored by DMA helpers In the current implementation of the OF DMA helpers, read-copy-update (RCU) linked lists are being used for storing and accessing the DMA controller data. This part of implementation is based upon V2 of the DMA helpers by Nicolas [1]. During a recent review of RCU, it became apparent that the code is missing the required rcu_read_lock()/unlock() calls as well as synchronisation calls before freeing any memory protected by RCU. Having looked into adding the appropriate RCU calls to protect the DMA data it became apparent that with the current DMA helper implementation, using RCU is not as attractive as it may have been before. The main reasons being that ... 1. We need to protect the DMA data around calls to the xlate function. 2. The of_dma_simple_xlate() function calls the DMA engine function dma_request_channel() which employs a mutex and so could sleep. 3. The RCU read-side critical sections must not sleep and so we cannot hold an RCU read lock around the xlate function. Therefore, instead of using RCU, an alternative for this use-case is to employ a simple spinlock inconjunction with a usage count variable to keep track of how many current users of the DMA data structure there are. With this implementation, the DMA data cannot be freed until all current users of the DMA data are finished. This patch is based upon the DMA helpers fix for potential deadlock [2]. [1] http://article.gmane.org/gmane.linux.ports.arm.omap/73622 [2] http://marc.info/?l=linux-arm-kernel&m=134859982520984&w=2 Signed-off-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/of/dma.c | 89 +++++++++++++++++++++++++++++++++++++------------- include/linux/of_dma.h | 5 +-- 2 files changed, 70 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 4bed490a69e4..59631b2c4666 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -19,27 +19,60 @@ #include static LIST_HEAD(of_dma_list); +static DEFINE_SPINLOCK(of_dma_lock); /** - * of_dma_find_controller - Find a DMA controller in DT DMA helpers list - * @np: device node of DMA controller + * of_dma_get_controller - Get a DMA controller in DT DMA helpers list + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Finds a DMA controller with matching device node and number for dma cells + * in a list of registered DMA controllers. If a match is found the use_count + * variable is increased and a valid pointer to the DMA data stored is retuned. + * A NULL pointer is returned if no match is found. */ -static struct of_dma *of_dma_find_controller(struct device_node *np) +static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec) { struct of_dma *ofdma; + spin_lock(&of_dma_lock); + if (list_empty(&of_dma_list)) { - pr_err("empty DMA controller list\n"); + spin_unlock(&of_dma_lock); return NULL; } - list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers) - if (ofdma->of_node == np) + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if ((ofdma->of_node == dma_spec->np) && + (ofdma->of_dma_nbcells == dma_spec->args_count)) { + ofdma->use_count++; + spin_unlock(&of_dma_lock); return ofdma; + } + + spin_unlock(&of_dma_lock); + + pr_debug("%s: can't find DMA controller %s\n", __func__, + dma_spec->np->full_name); return NULL; } +/** + * of_dma_put_controller - Decrement use count for a registered DMA controller + * @of_dma: pointer to DMA controller data + * + * Decrements the use_count variable in the DMA data structure. This function + * should be called only when a valid pointer is returned from + * of_dma_get_controller() and no further accesses to data referenced by that + * pointer are needed. + */ +static void of_dma_put_controller(struct of_dma *ofdma) +{ + spin_lock(&of_dma_lock); + ofdma->use_count--; + spin_unlock(&of_dma_lock); +} + /** * of_dma_controller_register - Register a DMA controller to DT DMA helpers * @np: device node of DMA controller @@ -81,9 +114,10 @@ int of_dma_controller_register(struct device_node *np, ofdma->of_dma_nbcells = nbcells; ofdma->of_dma_xlate = of_dma_xlate; ofdma->of_dma_data = data; + ofdma->use_count = 0; /* Now queue of_dma controller structure in list */ - list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list); + list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); return 0; } @@ -95,15 +129,32 @@ EXPORT_SYMBOL_GPL(of_dma_controller_register); * * Memory allocated by of_dma_controller_register() is freed here. */ -void of_dma_controller_free(struct device_node *np) +int 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); + spin_lock(&of_dma_lock); + + if (list_empty(&of_dma_list)) { + spin_unlock(&of_dma_lock); + return -ENODEV; } + + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) { + if (ofdma->use_count) { + spin_unlock(&of_dma_lock); + return -EBUSY; + } + + list_del(&ofdma->of_dma_controllers); + spin_unlock(&of_dma_lock); + kfree(ofdma); + return 0; + } + + spin_unlock(&of_dma_lock); + return -ENODEV; } EXPORT_SYMBOL_GPL(of_dma_controller_free); @@ -166,21 +217,15 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, if (of_dma_match_channel(np, name, i, &dma_spec)) continue; - 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; - } + ofdma = of_dma_get_controller(&dma_spec); - 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); + if (!ofdma) continue; - } chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + of_dma_put_controller(ofdma); + of_node_put(dma_spec.np); if (chan) diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 67158ddd1f3e..84b64f857e23 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -25,6 +25,7 @@ struct of_dma { struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *); void *of_dma_data; + int use_count; }; struct of_dma_filter_info { @@ -37,7 +38,7 @@ 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 int 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, @@ -51,7 +52,7 @@ static int of_dma_controller_register(struct device_node *np, return -ENODEV; } -static void of_dma_controller_free(struct device_node *np) +static int of_dma_controller_free(struct device_node *np) { } -- cgit v1.2.3 From 91f8aecc501e456c97a6f49f7ea3797e874d5d89 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 29 Nov 2012 12:17:22 +0530 Subject: dmaengine: fix !of_dma compilation warning Reported-by: Stephen Rothwell Reported-by: Fengguang Wu Signed-off-by: Vinod Koul --- include/linux/of_dma.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 84b64f857e23..d15073e080dd 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -44,7 +44,7 @@ extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma); #else -static int of_dma_controller_register(struct device_node *np, +static inline int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data) @@ -52,17 +52,18 @@ static int of_dma_controller_register(struct device_node *np, return -ENODEV; } -static int of_dma_controller_free(struct device_node *np) +static inline int of_dma_controller_free(struct device_node *np) { + return -ENODEV; } -static struct dma_chan *of_dma_request_slave_channel(struct device_node *np, +static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name) { return NULL; } -static struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, +static inline struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { return NULL; -- cgit v1.2.3 From a14acb4ac2a1486f6633c55eb7f7ded07f3ec9fc Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 6 Nov 2012 21:32:39 +0800 Subject: DMAEngine: add dmaengine_prep_interleaved_dma wrapper for interleaved api commit b14dab792dee(DMAEngine: Define interleaved transfer request api) adds interleaved request api, this patch adds the dmaengine_prep_interleaved_dma just like we have dmaengine_prep_ for other modes to avoid drivers call: xxx_chan->device->device_prep_interleaved_dma(). Signed-off-by: Barry Song Cc: Jassi Brar Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4c8643794e0d..3aa76dbed166 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -660,6 +660,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( period_len, dir, flags, NULL); } +static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + return chan->device->device_prep_interleaved_dma(chan, xt, flags); +} + static inline int dmaengine_terminate_all(struct dma_chan *chan) { return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); -- cgit v1.2.3 From 944ea4dd38b8575e30a5699633c81945bff1864d Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Sun, 11 Nov 2012 23:03:20 +0000 Subject: dmatest: Fix NULL pointer dereference on ioat device_control is an optional and not implemented in all DMA drivers. Any calls to these will result in a NULL pointer dereference. dmatest makes two of these calls when completing the kernel thread and removing the module. These are corrected by calling the dmaengine_device_control wrapper and checking for a non-existant device_control function pointer there. Signed-off-by: Jon Mason CC: Vinod Koul CC: Dan Williams Reviewed-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 4 ++-- include/linux/dmaengine.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 3a8047b1f108..99a75e5d66be 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -536,7 +536,7 @@ err_srcs: thread_name, total_tests, failed_tests, ret); /* terminate all transfers on specified channels */ - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); @@ -561,7 +561,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) } /* terminate all transfers on specified channels */ - dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(dtc->chan); kfree(dtc); } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 3aa76dbed166..be6e95395b11 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -608,7 +608,10 @@ static inline int dmaengine_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { - return chan->device->device_control(chan, cmd, arg); + if (chan->device->device_control) + return chan->device->device_control(chan, cmd, arg); + else + return -ENOSYS; } static inline int dmaengine_slave_config(struct dma_chan *chan, -- cgit v1.2.3 From b9ee86830f34737a08deead93872a79a37419a13 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 09:59:54 +0000 Subject: dmaengine: remove dma_async_memcpy_pending() macro Just use dma_async_issue_pending() directly. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/misc/carma/carma-fpga-program.c | 2 +- drivers/misc/carma/carma-fpga.c | 2 +- include/linux/dmaengine.h | 2 -- net/ipv4/tcp.c | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index eaddfe9db149..736c7714f565 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -546,7 +546,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) goto out_dma_unmap; } - dma_async_memcpy_issue_pending(chan); + dma_async_issue_pending(chan); /* Set the total byte count */ fpga_set_byte_count(priv->regs, priv->bytes); diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 6b43f8c7b3be..7508cafff103 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -751,7 +751,7 @@ static irqreturn_t data_irq(int irq, void *dev_id) submitted = true; /* Start the DMA Engine */ - dma_async_memcpy_issue_pending(priv->chan); + dma_async_issue_pending(priv->chan); out: /* If no DMA was submitted, re-enable interrupts */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index be6e95395b11..cd15958d4d1d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -901,8 +901,6 @@ static inline void dma_async_issue_pending(struct dma_chan *chan) chan->device->device_issue_pending(chan); } -#define dma_async_memcpy_issue_pending(chan) dma_async_issue_pending(chan) - /** * dma_async_is_tx_complete - poll for transaction completion * @chan: DMA channel diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1ca253635f7a..cf949a119a54 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1406,7 +1406,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait) return; last_issued = tp->ucopy.dma_cookie; - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); do { if (dma_async_memcpy_complete(tp->ucopy.dma_chan, @@ -1744,7 +1744,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, tcp_service_net_dma(sk, true); tcp_cleanup_rbuf(sk, copied); } else - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); } #endif if (copied >= target) { @@ -1837,7 +1837,7 @@ do_prequeue: break; } - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); if ((offset + used) == skb->len) copied_early = true; -- cgit v1.2.3 From e239345f642e6a255d0ba1e3d92c2f9ec5a44fbe Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:01:01 +0000 Subject: dmaengine: remove dma_async_memcpy_complete() macro Just use dma_async_is_tx_complete() directly. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- include/linux/dmaengine.h | 5 +---- net/ipv4/tcp.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cd15958d4d1d..4ca9cf73ad3f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -926,16 +926,13 @@ static inline enum dma_status dma_async_is_tx_complete(struct dma_chan *chan, return status; } -#define dma_async_memcpy_complete(chan, cookie, last, used)\ - dma_async_is_tx_complete(chan, cookie, last, used) - /** * dma_async_is_complete - test a cookie against chan state * @cookie: transaction identifier to test status of * @last_complete: last know completed transaction * @last_used: last cookie value handed out * - * dma_async_is_complete() is used in dma_async_memcpy_complete() + * dma_async_is_complete() is used in dma_async_is_tx_complete() * the test logic is separated for lightweight testing of multiple cookies */ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cf949a119a54..db0856ad70cb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1409,7 +1409,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait) dma_async_issue_pending(tp->ucopy.dma_chan); do { - if (dma_async_memcpy_complete(tp->ucopy.dma_chan, + if (dma_async_is_tx_complete(tp->ucopy.dma_chan, last_issued, &done, &used) == DMA_SUCCESS) { /* Safe to free early-copied skbs now */ -- cgit v1.2.3 From 61cc13a51bcff737ce02d2047834171c0365b00d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:56 +0200 Subject: dmaengine: introduce is_slave_direction function This function helps to distinguish the slave type of transfer by checking the direction parameter. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Reviewed-by: Mika Westerberg Reviewed-by: Linus Walleij Cc: Nicolas Ferre Cc: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4ca9cf73ad3f..bfcdecb5d87a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -621,6 +621,11 @@ static inline int dmaengine_slave_config(struct dma_chan *chan, (unsigned long)config); } +static inline bool is_slave_direction(enum dma_transfer_direction direction) +{ + return (direction == DMA_MEM_TO_DEV) || (direction == DMA_DEV_TO_MEM); +} + static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( struct dma_chan *chan, dma_addr_t buf, size_t len, enum dma_transfer_direction dir, unsigned long flags) -- cgit v1.2.3 From 3a95b9fbba893ebfa9b83de105707539e0228e0c Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sat, 24 Nov 2012 00:22:56 +0000 Subject: pl080.h: moved from arm/include/asm/hardware to include/linux/amba/ The header is used by drivers/dma/amba-pl08x.c, which can be compiled under x86, where PL080 exists under a PCI-to-AMBA bridge. This patche moves it where it can be accessed by other architectures, and fixes all users. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- arch/arm/include/asm/hardware/pl080.h | 146 ---------------------------------- arch/arm/mach-s3c64xx/dma.c | 2 +- arch/arm/mach-spear3xx/spear3xx.c | 1 - arch/arm/mach-spear6xx/spear6xx.c | 2 +- drivers/dma/amba-pl08x.c | 2 +- include/linux/amba/pl080.h | 146 ++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 150 deletions(-) delete mode 100644 arch/arm/include/asm/hardware/pl080.h create mode 100644 include/linux/amba/pl080.h (limited to 'include') diff --git a/arch/arm/include/asm/hardware/pl080.h b/arch/arm/include/asm/hardware/pl080.h deleted file mode 100644 index 4eea2107214b..000000000000 --- a/arch/arm/include/asm/hardware/pl080.h +++ /dev/null @@ -1,146 +0,0 @@ -/* arch/arm/include/asm/hardware/pl080.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * ARM PrimeCell PL080 DMA controller - * - * 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. -*/ - -/* Note, there are some Samsung updates to this controller block which - * make it not entierly compatible with the PL080 specification from - * ARM. When in doubt, check the Samsung documentation first. - * - * The Samsung defines are PL080S, and add an extra control register, - * the ability to move more than 2^11 counts of data and some extra - * OneNAND features. -*/ - -#ifndef ASM_PL080_H -#define ASM_PL080_H - -#define PL080_INT_STATUS (0x00) -#define PL080_TC_STATUS (0x04) -#define PL080_TC_CLEAR (0x08) -#define PL080_ERR_STATUS (0x0C) -#define PL080_ERR_CLEAR (0x10) -#define PL080_RAW_TC_STATUS (0x14) -#define PL080_RAW_ERR_STATUS (0x18) -#define PL080_EN_CHAN (0x1c) -#define PL080_SOFT_BREQ (0x20) -#define PL080_SOFT_SREQ (0x24) -#define PL080_SOFT_LBREQ (0x28) -#define PL080_SOFT_LSREQ (0x2C) - -#define PL080_CONFIG (0x30) -#define PL080_CONFIG_M2_BE (1 << 2) -#define PL080_CONFIG_M1_BE (1 << 1) -#define PL080_CONFIG_ENABLE (1 << 0) - -#define PL080_SYNC (0x34) - -/* Per channel configuration registers */ - -#define PL080_Cx_STRIDE (0x20) -#define PL080_Cx_BASE(x) ((0x100 + (x * 0x20))) -#define PL080_Cx_SRC_ADDR(x) ((0x100 + (x * 0x20))) -#define PL080_Cx_DST_ADDR(x) ((0x104 + (x * 0x20))) -#define PL080_Cx_LLI(x) ((0x108 + (x * 0x20))) -#define PL080_Cx_CONTROL(x) ((0x10C + (x * 0x20))) -#define PL080_Cx_CONFIG(x) ((0x110 + (x * 0x20))) -#define PL080S_Cx_CONTROL2(x) ((0x110 + (x * 0x20))) -#define PL080S_Cx_CONFIG(x) ((0x114 + (x * 0x20))) - -#define PL080_CH_SRC_ADDR (0x00) -#define PL080_CH_DST_ADDR (0x04) -#define PL080_CH_LLI (0x08) -#define PL080_CH_CONTROL (0x0C) -#define PL080_CH_CONFIG (0x10) -#define PL080S_CH_CONTROL2 (0x10) -#define PL080S_CH_CONFIG (0x14) - -#define PL080_LLI_ADDR_MASK (0x3fffffff << 2) -#define PL080_LLI_ADDR_SHIFT (2) -#define PL080_LLI_LM_AHB2 (1 << 0) - -#define PL080_CONTROL_TC_IRQ_EN (1 << 31) -#define PL080_CONTROL_PROT_MASK (0x7 << 28) -#define PL080_CONTROL_PROT_SHIFT (28) -#define PL080_CONTROL_PROT_CACHE (1 << 30) -#define PL080_CONTROL_PROT_BUFF (1 << 29) -#define PL080_CONTROL_PROT_SYS (1 << 28) -#define PL080_CONTROL_DST_INCR (1 << 27) -#define PL080_CONTROL_SRC_INCR (1 << 26) -#define PL080_CONTROL_DST_AHB2 (1 << 25) -#define PL080_CONTROL_SRC_AHB2 (1 << 24) -#define PL080_CONTROL_DWIDTH_MASK (0x7 << 21) -#define PL080_CONTROL_DWIDTH_SHIFT (21) -#define PL080_CONTROL_SWIDTH_MASK (0x7 << 18) -#define PL080_CONTROL_SWIDTH_SHIFT (18) -#define PL080_CONTROL_DB_SIZE_MASK (0x7 << 15) -#define PL080_CONTROL_DB_SIZE_SHIFT (15) -#define PL080_CONTROL_SB_SIZE_MASK (0x7 << 12) -#define PL080_CONTROL_SB_SIZE_SHIFT (12) -#define PL080_CONTROL_TRANSFER_SIZE_MASK (0xfff << 0) -#define PL080_CONTROL_TRANSFER_SIZE_SHIFT (0) - -#define PL080_BSIZE_1 (0x0) -#define PL080_BSIZE_4 (0x1) -#define PL080_BSIZE_8 (0x2) -#define PL080_BSIZE_16 (0x3) -#define PL080_BSIZE_32 (0x4) -#define PL080_BSIZE_64 (0x5) -#define PL080_BSIZE_128 (0x6) -#define PL080_BSIZE_256 (0x7) - -#define PL080_WIDTH_8BIT (0x0) -#define PL080_WIDTH_16BIT (0x1) -#define PL080_WIDTH_32BIT (0x2) - -#define PL080N_CONFIG_ITPROT (1 << 20) -#define PL080N_CONFIG_SECPROT (1 << 19) -#define PL080_CONFIG_HALT (1 << 18) -#define PL080_CONFIG_ACTIVE (1 << 17) /* RO */ -#define PL080_CONFIG_LOCK (1 << 16) -#define PL080_CONFIG_TC_IRQ_MASK (1 << 15) -#define PL080_CONFIG_ERR_IRQ_MASK (1 << 14) -#define PL080_CONFIG_FLOW_CONTROL_MASK (0x7 << 11) -#define PL080_CONFIG_FLOW_CONTROL_SHIFT (11) -#define PL080_CONFIG_DST_SEL_MASK (0xf << 6) -#define PL080_CONFIG_DST_SEL_SHIFT (6) -#define PL080_CONFIG_SRC_SEL_MASK (0xf << 1) -#define PL080_CONFIG_SRC_SEL_SHIFT (1) -#define PL080_CONFIG_ENABLE (1 << 0) - -#define PL080_FLOW_MEM2MEM (0x0) -#define PL080_FLOW_MEM2PER (0x1) -#define PL080_FLOW_PER2MEM (0x2) -#define PL080_FLOW_SRC2DST (0x3) -#define PL080_FLOW_SRC2DST_DST (0x4) -#define PL080_FLOW_MEM2PER_PER (0x5) -#define PL080_FLOW_PER2MEM_PER (0x6) -#define PL080_FLOW_SRC2DST_SRC (0x7) - -/* DMA linked list chain structure */ - -struct pl080_lli { - u32 src_addr; - u32 dst_addr; - u32 next_lli; - u32 control0; -}; - -struct pl080s_lli { - u32 src_addr; - u32 dst_addr; - u32 next_lli; - u32 control0; - u32 control1; -}; - -#endif /* ASM_PL080_H */ diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c index f2a7a1725596..a77f5214bbe8 100644 --- a/arch/arm/mach-s3c64xx/dma.c +++ b/arch/arm/mach-s3c64xx/dma.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,6 @@ #include -#include /* dma channel state information */ diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 38fe95db31a7..3d9b1b5e8ed9 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c index 5a5a52db252b..8ce65a23b06e 100644 --- a/arch/arm/mach-spear6xx/spear6xx.c +++ b/arch/arm/mach-spear6xx/spear6xx.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6eb6a5c210bb..8bad254a498d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -83,7 +83,7 @@ #include #include #include -#include +#include #include "dmaengine.h" #include "virt-dma.h" diff --git a/include/linux/amba/pl080.h b/include/linux/amba/pl080.h new file mode 100644 index 000000000000..3e7b62fbefbd --- /dev/null +++ b/include/linux/amba/pl080.h @@ -0,0 +1,146 @@ +/* include/linux/amba/pl080.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * ARM PrimeCell PL080 DMA controller + * + * 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. +*/ + +/* Note, there are some Samsung updates to this controller block which + * make it not entierly compatible with the PL080 specification from + * ARM. When in doubt, check the Samsung documentation first. + * + * The Samsung defines are PL080S, and add an extra control register, + * the ability to move more than 2^11 counts of data and some extra + * OneNAND features. +*/ + +#ifndef ASM_PL080_H +#define ASM_PL080_H + +#define PL080_INT_STATUS (0x00) +#define PL080_TC_STATUS (0x04) +#define PL080_TC_CLEAR (0x08) +#define PL080_ERR_STATUS (0x0C) +#define PL080_ERR_CLEAR (0x10) +#define PL080_RAW_TC_STATUS (0x14) +#define PL080_RAW_ERR_STATUS (0x18) +#define PL080_EN_CHAN (0x1c) +#define PL080_SOFT_BREQ (0x20) +#define PL080_SOFT_SREQ (0x24) +#define PL080_SOFT_LBREQ (0x28) +#define PL080_SOFT_LSREQ (0x2C) + +#define PL080_CONFIG (0x30) +#define PL080_CONFIG_M2_BE (1 << 2) +#define PL080_CONFIG_M1_BE (1 << 1) +#define PL080_CONFIG_ENABLE (1 << 0) + +#define PL080_SYNC (0x34) + +/* Per channel configuration registers */ + +#define PL080_Cx_STRIDE (0x20) +#define PL080_Cx_BASE(x) ((0x100 + (x * 0x20))) +#define PL080_Cx_SRC_ADDR(x) ((0x100 + (x * 0x20))) +#define PL080_Cx_DST_ADDR(x) ((0x104 + (x * 0x20))) +#define PL080_Cx_LLI(x) ((0x108 + (x * 0x20))) +#define PL080_Cx_CONTROL(x) ((0x10C + (x * 0x20))) +#define PL080_Cx_CONFIG(x) ((0x110 + (x * 0x20))) +#define PL080S_Cx_CONTROL2(x) ((0x110 + (x * 0x20))) +#define PL080S_Cx_CONFIG(x) ((0x114 + (x * 0x20))) + +#define PL080_CH_SRC_ADDR (0x00) +#define PL080_CH_DST_ADDR (0x04) +#define PL080_CH_LLI (0x08) +#define PL080_CH_CONTROL (0x0C) +#define PL080_CH_CONFIG (0x10) +#define PL080S_CH_CONTROL2 (0x10) +#define PL080S_CH_CONFIG (0x14) + +#define PL080_LLI_ADDR_MASK (0x3fffffff << 2) +#define PL080_LLI_ADDR_SHIFT (2) +#define PL080_LLI_LM_AHB2 (1 << 0) + +#define PL080_CONTROL_TC_IRQ_EN (1 << 31) +#define PL080_CONTROL_PROT_MASK (0x7 << 28) +#define PL080_CONTROL_PROT_SHIFT (28) +#define PL080_CONTROL_PROT_CACHE (1 << 30) +#define PL080_CONTROL_PROT_BUFF (1 << 29) +#define PL080_CONTROL_PROT_SYS (1 << 28) +#define PL080_CONTROL_DST_INCR (1 << 27) +#define PL080_CONTROL_SRC_INCR (1 << 26) +#define PL080_CONTROL_DST_AHB2 (1 << 25) +#define PL080_CONTROL_SRC_AHB2 (1 << 24) +#define PL080_CONTROL_DWIDTH_MASK (0x7 << 21) +#define PL080_CONTROL_DWIDTH_SHIFT (21) +#define PL080_CONTROL_SWIDTH_MASK (0x7 << 18) +#define PL080_CONTROL_SWIDTH_SHIFT (18) +#define PL080_CONTROL_DB_SIZE_MASK (0x7 << 15) +#define PL080_CONTROL_DB_SIZE_SHIFT (15) +#define PL080_CONTROL_SB_SIZE_MASK (0x7 << 12) +#define PL080_CONTROL_SB_SIZE_SHIFT (12) +#define PL080_CONTROL_TRANSFER_SIZE_MASK (0xfff << 0) +#define PL080_CONTROL_TRANSFER_SIZE_SHIFT (0) + +#define PL080_BSIZE_1 (0x0) +#define PL080_BSIZE_4 (0x1) +#define PL080_BSIZE_8 (0x2) +#define PL080_BSIZE_16 (0x3) +#define PL080_BSIZE_32 (0x4) +#define PL080_BSIZE_64 (0x5) +#define PL080_BSIZE_128 (0x6) +#define PL080_BSIZE_256 (0x7) + +#define PL080_WIDTH_8BIT (0x0) +#define PL080_WIDTH_16BIT (0x1) +#define PL080_WIDTH_32BIT (0x2) + +#define PL080N_CONFIG_ITPROT (1 << 20) +#define PL080N_CONFIG_SECPROT (1 << 19) +#define PL080_CONFIG_HALT (1 << 18) +#define PL080_CONFIG_ACTIVE (1 << 17) /* RO */ +#define PL080_CONFIG_LOCK (1 << 16) +#define PL080_CONFIG_TC_IRQ_MASK (1 << 15) +#define PL080_CONFIG_ERR_IRQ_MASK (1 << 14) +#define PL080_CONFIG_FLOW_CONTROL_MASK (0x7 << 11) +#define PL080_CONFIG_FLOW_CONTROL_SHIFT (11) +#define PL080_CONFIG_DST_SEL_MASK (0xf << 6) +#define PL080_CONFIG_DST_SEL_SHIFT (6) +#define PL080_CONFIG_SRC_SEL_MASK (0xf << 1) +#define PL080_CONFIG_SRC_SEL_SHIFT (1) +#define PL080_CONFIG_ENABLE (1 << 0) + +#define PL080_FLOW_MEM2MEM (0x0) +#define PL080_FLOW_MEM2PER (0x1) +#define PL080_FLOW_PER2MEM (0x2) +#define PL080_FLOW_SRC2DST (0x3) +#define PL080_FLOW_SRC2DST_DST (0x4) +#define PL080_FLOW_MEM2PER_PER (0x5) +#define PL080_FLOW_PER2MEM_PER (0x6) +#define PL080_FLOW_SRC2DST_SRC (0x7) + +/* DMA linked list chain structure */ + +struct pl080_lli { + u32 src_addr; + u32 dst_addr; + u32 next_lli; + u32 control0; +}; + +struct pl080s_lli { + u32 src_addr; + u32 dst_addr; + u32 next_lli; + u32 control0; + u32 control1; +}; + +#endif /* ASM_PL080_H */ -- cgit v1.2.3 From 47db92f4a63499b1605b4c66f9347ba5479e7b19 Mon Sep 17 00:00:00 2001 From: Gerald Baeza Date: Fri, 21 Sep 2012 21:21:37 +0200 Subject: dmaengine: ste_dma40: physical channels number correction DMAC_ICFG[0:2]=SCHNB only allows to count 'multiple of 4' physical channels so it was ok with platforms having 8 channels but cannot be used for next versions (with 10 or 14 channels). This patch allows to provide the number of physical channels for a DMA device via platform_data, or still rely on SCHNB if platform_data announces 0 channel. Signed-off-by: Gerald Baeza Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 15 ++++++++++----- include/linux/platform_data/dma-ste-dma40.h | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 5feab7db9449..ca18117def0a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3004,14 +3004,21 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) * ? has revision 1 * DB8500v1 has revision 2 * DB8500v2 has revision 3 + * AP9540v1 has revision 4 + * DB8540v1 has revision 4 */ rev = AMBA_REV_BITS(pid); + plat_data = pdev->dev.platform_data; + /* The number of physical channels on this HW */ - num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + if (plat_data->num_of_phy_chans) + num_phy_chans = plat_data->num_of_phy_chans; + else + num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; - dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n", - rev, res->start); + dev_info(&pdev->dev, "hardware revision: %d @ 0x%x with %d physical channels\n", + rev, res->start, num_phy_chans); if (rev < 2) { d40_err(&pdev->dev, "hardware revision: %d is not supported", @@ -3019,8 +3026,6 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) goto failure; } - plat_data = pdev->dev.platform_data; - /* Count the number of logical channels in use */ for (i = 0; i < plat_data->dev_len; i++) if (plat_data->dev_rx[i] != 0) diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 9ff93b065686..833cb959f3df 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,9 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @num_of_phy_chans: The number of physical channels implemented in HW. + * 0 means reading the number of channels from DMA HW but this is only valid + * for 'multiple of 4' channels, like 8. */ struct stedma40_platform_data { u32 dev_len; @@ -158,6 +161,7 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; bool use_esram_lcla; + int num_of_phy_chans; }; #ifdef CONFIG_STE_DMA40 -- cgit v1.2.3 From 762eb33fdebed34d98943d85ee1425663d7cceaa Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 11:38:39 +0100 Subject: dmaengine: ste_dma40: add missing kernel-doc entry Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- include/linux/platform_data/dma-ste-dma40.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 833cb959f3df..b99024ba94ec 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,7 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @use_esram_lcla: flag for mapping the lcla into esram region * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid * for 'multiple of 4' channels, like 8. -- cgit v1.2.3 From 7407048bec896268b50e3c43c1d012a4764dc210 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 18 Dec 2012 12:25:14 +0100 Subject: dmaengine: ste_dma40: add software lli support This patch add support to manage LLI by SW for select phy channels. There is a HW issue in certain controllers due to which on certain occassions HW LLI cannot be used on some physical channels. To avoid the HW issue on a specific phy channel, the phy channel number can be added to the list of soft_lli_channels and there after all the transfers on that channel will use software LLI, for peripheral to memory transfers. SoftLLI introduces relink overhead, that could impact performace for certain use cases. This is based on a previous patch of Narayanan Gopalakrishnan. Cc: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 20 +++++++++++++++++++- include/linux/platform_data/dma-ste-dma40.h | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index e317debdbe5a..2ecefb7113b9 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -355,6 +355,7 @@ struct d40_lcla_pool { * @allocated_dst: Same as for src but is dst. * allocated_dst and allocated_src uses the D40_ALLOC* defines as well as * event line number. + * @use_soft_lli: To mark if the linked lists of channel are managed by SW. */ struct d40_phy_res { spinlock_t lock; @@ -362,6 +363,7 @@ struct d40_phy_res { int num; u32 allocated_src; u32 allocated_dst; + bool use_soft_lli; }; struct d40_base; @@ -783,7 +785,16 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) * can't link back to the one in LCPA space */ if (linkback || (lli_len - lli_current > 1)) { - curr_lcla = d40_lcla_alloc_one(chan, desc); + /* + * If the channel is expected to use only soft_lli don't + * allocate a lcla. This is to avoid a HW issue that exists + * in some controller during a peripheral to memory transfer + * that uses linked lists. + */ + if (!(chan->phy_chan->use_soft_lli && + chan->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)) + curr_lcla = d40_lcla_alloc_one(chan, desc); + first_lcla = curr_lcla; } @@ -3063,6 +3074,13 @@ static int __init d40_phy_res_init(struct d40_base *base) num_phy_chans_avail--; } + /* Mark soft_lli channels */ + for (i = 0; i < base->plat_data->num_of_soft_lli_chans; i++) { + int chan = base->plat_data->soft_lli_chans[i]; + + base->phy_res[chan].use_soft_lli = true; + } + dev_info(base->dev, "%d of %d physical DMA channels available\n", num_phy_chans_avail, base->num_phy_chans); diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index b99024ba94ec..4b781014b0a0 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,12 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW + * which avoids HW bug that exists in some versions of the controller. + * SoftLLI introduces relink overhead that could impact performace for + * certain use cases. + * @num_of_soft_lli_chans: The number of channels that needs to be configured + * to use SoftLLI. * @use_esram_lcla: flag for mapping the lcla into esram region * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid @@ -161,6 +167,8 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_phy; struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; + int *soft_lli_chans; + int num_of_soft_lli_chans; bool use_esram_lcla; int num_of_phy_chans; }; -- cgit v1.2.3 From 978c4172af48f0adc082f8b1d94acb817d947730 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:16 +0200 Subject: dmaengine.h: remove redundant else keyword dmaengine_device_control returns -ENOSYS in case the dma driver doesn't have such functionality. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index bfcdecb5d87a..f5939999cb65 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -610,8 +610,8 @@ static inline int dmaengine_device_control(struct dma_chan *chan, { if (chan->device->device_control) return chan->device->device_control(chan, cmd, arg); - else - return -ENOSYS; + + return -ENOSYS; } static inline int dmaengine_slave_config(struct dma_chan *chan, -- cgit v1.2.3