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 diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a815d44..d37cf95 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -62,6 +62,7 @@ #include #include #include +#include static DEFINE_MUTEX(dma_list_mutex); static DEFINE_IDR(dma_idr); @@ -546,6 +547,21 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v } EXPORT_SYMBOL_GPL(__dma_request_channel); +/** + * dma_request_slave_channel - try to allocate an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + */ +struct dma_chan *dma_request_slave_channel(struct device *dev, char *name) +{ + /* If device-tree is present get slave info from here */ + if (dev->of_node) + return of_dma_request_slave_channel(dev->of_node, name); + + return NULL; +} +EXPORT_SYMBOL_GPL(dma_request_slave_channel); + void dma_release_channel(struct dma_chan *chan) { mutex_lock(&dma_list_mutex); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d3201e4..8cd0e25 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 v0.10.2 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 diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt new file mode 100644 index 0000000..a4f59a5 --- /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 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 +#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 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 +#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 v0.10.2 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 diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 337823d..67158dd 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 v0.10.2 From deef12443785add808af39ff7f836b54bc5c4253 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 19 Sep 2012 10:49:48 -0400 Subject: of: dma: fix typos in generic dma binding definition Some semicolons were left out in the examples. The #dma-channels and #dma-requests properties have a prefix that is, by convention, reserved for cell size properties. Rename those properties to dma-channels and dma-requests. Signed-off-by: Matt Porter Acked-by: Arnd Bergmann Acked-by: Jon Hunter Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt index a4f59a5..8f504e6 100644 --- a/Documentation/devicetree/bindings/dma/dma.txt +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -13,22 +13,22 @@ Required property: more details. Optional properties: -- #dma-channels: Number of DMA channels supported by the controller. -- #dma-requests: Number of DMA requests signals supported by the +- 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" + 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-channels = <32>; + dma-requests = <127>; }; @@ -61,7 +61,7 @@ Examples: ... dmas = <&dma 2 /* read channel */ &dma 3>; /* write channel */ - dma-names = "rx", "tx" + dma-names = "rx", "tx"; ... }; @@ -70,7 +70,7 @@ Examples: dmas = <&dma1 5 &dma2 7 &dma3 2>; - dma-names = "rx-tx", "rx-tx", "rx-tx" + dma-names = "rx-tx", "rx-tx", "rx-tx"; 3. A device with three channels, one of which has two alternatives: -- cgit v0.10.2 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 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8cd0e25..c88f302 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 v0.10.2 From c6a0aec9216064f756c4eda80be0720707c898d5 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Oct 2012 13:01:54 -0700 Subject: drivers/dma: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Vinod Koul CC: Dan Williams Signed-off-by: Kees Cook Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d4c1218..93ea87e 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -51,7 +51,7 @@ config ASYNC_TX_ENABLE_CHANNEL_SWITCH config AMBA_PL08X bool "ARM PrimeCell PL080 or PL081 support" - depends on ARM_AMBA && EXPERIMENTAL + depends on ARM_AMBA select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help -- cgit v0.10.2 From 53b9989bc75e185d0bf946e939ebe8534c8a9740 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 10 Oct 2012 21:04:58 +0800 Subject: pch_dma: use module_pci_driver to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Vinod Koul diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index eca1c4d..aed6e0c 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -1029,18 +1029,7 @@ static struct pci_driver pch_dma_driver = { #endif }; -static int __init pch_dma_init(void) -{ - return pci_register_driver(&pch_dma_driver); -} - -static void __exit pch_dma_exit(void) -{ - pci_unregister_driver(&pch_dma_driver); -} - -module_init(pch_dma_init); -module_exit(pch_dma_exit); +module_pci_driver(pch_dma_driver); MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH " "DMA controller driver"); -- cgit v0.10.2 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 diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index e1c8c9e..62a6190 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 v0.10.2 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 diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index c0d85db..5bb3dfb 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 8f0b111..27b8e1d 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 8896559..88a069f 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 62a6190..41766de 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -15,6 +15,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) * @is_private: The device channels should be marked as private and not for @@ -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 v0.10.2 From f9965aa20706860077cfa093d04a6351c0c1e940 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:18 +0530 Subject: ARM: SPEAr13xx: Pass DW DMAC platform data from DT This patch adds dw_dmac's platform data to DT node. It also creates slave info node for SPEAr13xx, for the devices which were using dw_dmac. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi index 34da11a..b2d41b7 100644 --- a/arch/arm/boot/dts/spear1340.dtsi +++ b/arch/arm/boot/dts/spear1340.dtsi @@ -88,6 +88,25 @@ status = "disabled"; }; + dma@ea800000 { + slave_info { + uart1_tx { + bus_id = "uart1_tx"; + cfg_hi = <0x6000>; /* 0xC << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <1>; + }; + uart1_tx { + bus_id = "uart1_tx"; + cfg_hi = <0x680>; /* 0xD << 7 */ + cfg_lo = <0>; + src_master = <1>; + dst_master = <0>; + }; + }; + }; + spi1: spi@5d400000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x5d400000 0x1000>; diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index b4ca60f..585f641 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -105,6 +105,37 @@ reg = <0xea800000 0x1000>; interrupts = <0 19 0x4>; status = "disabled"; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; + + slave_info { + ssp0_tx { + bus_id = "ssp0_tx"; + cfg_hi = <0x2000>; /* 0x4 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + ssp0_rx { + bus_id = "ssp0_rx"; + cfg_hi = <0x280>; /* 0x5 << 7 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + cf { + bus_id = "cf"; + cfg_hi = <0>; + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + }; }; dma@eb000000 { @@ -112,6 +143,13 @@ reg = <0xeb000000 0x1000>; interrupts = <0 59 0x4>; status = "disabled"; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; }; fsmc: flash@b0000000 { diff --git a/arch/arm/mach-spear13xx/include/mach/spear.h b/arch/arm/mach-spear13xx/include/mach/spear.h index 7cfa681..972a151 100644 --- a/arch/arm/mach-spear13xx/include/mach/spear.h +++ b/arch/arm/mach-spear13xx/include/mach/spear.h @@ -43,8 +43,6 @@ #define VA_L2CC_BASE IOMEM(UL(0xFB000000)) /* others */ -#define DMAC0_BASE UL(0xEA800000) -#define DMAC1_BASE UL(0xEB000000) #define MCIF_CF_BASE UL(0xB2800000) /* Debug uart for linux, will be used for debug and uncompress messages */ diff --git a/arch/arm/mach-spear13xx/spear1310.c b/arch/arm/mach-spear13xx/spear1310.c index 02f4724..ec72c47 100644 --- a/arch/arm/mach-spear13xx/spear1310.c +++ b/arch/arm/mach-spear13xx/spear1310.c @@ -36,7 +36,7 @@ static struct arasan_cf_pdata cf_pdata = { .cf_if_clk = CF_IF_CLK_166M, .quirk = CF_BROKEN_UDMA, - .dma_priv = &cf_dma_priv, + .dma_priv = "cf", }; /* ssp device registration */ @@ -47,10 +47,7 @@ static struct pl022_ssp_controller ssp1_plat_data = { /* Add SPEAr1310 auxdata to pass platform data */ static struct of_dev_auxdata spear1310_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data), OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data), - OF_DEV_AUXDATA("arm,pl022", SPEAR1310_SSP1_BASE, NULL, &ssp1_plat_data), {} }; diff --git a/arch/arm/mach-spear13xx/spear1340.c b/arch/arm/mach-spear13xx/spear1340.c index 081014f..69c8f72 100644 --- a/arch/arm/mach-spear13xx/spear1340.c +++ b/arch/arm/mach-spear13xx/spear1340.c @@ -18,9 +18,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -78,26 +78,16 @@ (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) -static struct dw_dma_slave uart1_dma_param[] = { - { - /* Tx */ - .cfg_hi = DWC_CFGH_DST_PER(SPEAR1340_DMA_REQ_UART1_TX), - .cfg_lo = 0, - .src_master = DMA_MASTER_MEMORY, - .dst_master = SPEAR1340_DMA_MASTER_UART1, - }, { - /* Rx */ - .cfg_hi = DWC_CFGH_SRC_PER(SPEAR1340_DMA_REQ_UART1_RX), - .cfg_lo = 0, - .src_master = SPEAR1340_DMA_MASTER_UART1, - .dst_master = DMA_MASTER_MEMORY, - } +static struct amba_pl011_data uart1_data = { + .dma_filter = dw_dma_generic_filter, + .dma_tx_param = "uart1_tx", + .dma_rx_param = "uart1_rx", }; -static struct amba_pl011_data uart1_data = { - .dma_filter = dw_dma_filter, - .dma_tx_param = &uart1_dma_param[0], - .dma_rx_param = &uart1_dma_param[1], +static struct arasan_cf_pdata cf_pdata = { + .cf_if_clk = CF_IF_CLK_166M, + .quirk = CF_BROKEN_UDMA, + .dma_priv = "cf", }; /* SATA device registration */ @@ -158,11 +148,8 @@ static struct ahci_platform_data sata_pdata = { /* Add SPEAr1340 auxdata to pass platform data */ static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_dma_priv), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data), + OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata), OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data), - OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL, &sata_pdata), OF_DEV_AUXDATA("arm,pl011", SPEAR1340_UART1_BASE, NULL, &uart1_data), diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c index c4af775..b074db8 100644 --- a/arch/arm/mach-spear13xx/spear13xx.c +++ b/arch/arm/mach-spear13xx/spear13xx.c @@ -22,63 +22,16 @@ #include #include #include -#include #include #include -/* common dw_dma filter routine to be used by peripherals */ -bool dw_dma_filter(struct dma_chan *chan, void *slave) -{ - struct dw_dma_slave *dws = (struct dw_dma_slave *)slave; - - if (chan->device->dev == dws->dma_dev) { - chan->private = slave; - return true; - } else { - return false; - } -} - /* ssp device registration */ -static struct dw_dma_slave ssp_dma_param[] = { - { - /* Tx */ - .cfg_hi = DWC_CFGH_DST_PER(DMA_REQ_SSP0_TX), - .cfg_lo = 0, - .src_master = DMA_MASTER_MEMORY, - .dst_master = DMA_MASTER_SSP0, - }, { - /* Rx */ - .cfg_hi = DWC_CFGH_SRC_PER(DMA_REQ_SSP0_RX), - .cfg_lo = 0, - .src_master = DMA_MASTER_SSP0, - .dst_master = DMA_MASTER_MEMORY, - } -}; - struct pl022_ssp_controller pl022_plat_data = { .enable_dma = 1, - .dma_filter = dw_dma_filter, - .dma_rx_param = &ssp_dma_param[1], - .dma_tx_param = &ssp_dma_param[0], -}; - -/* CF device registration */ -struct dw_dma_slave cf_dma_priv = { - .cfg_hi = 0, - .cfg_lo = 0, - .src_master = 0, - .dst_master = 0, -}; - -/* dmac device registeration */ -struct dw_dma_platform_data dmac_plat_data = { - .nr_channels = 8, - .chan_allocation_order = CHAN_ALLOCATION_DESCENDING, - .chan_priority = CHAN_PRIORITY_DESCENDING, - .block_size = 4095U, - .nr_masters = 2, - .data_width = { 3, 3, 0, 0 }, + .dma_filter = dw_dma_generic_filter, + .dma_rx_param = "ssp0_rx", + .dma_tx_param = "ssp0_tx", + .num_chipselect = 3, }; void __init spear13xx_l2x0_init(void) -- cgit v0.10.2 From b73d6c0ae4d74588a2653835a2452944722cc64b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 18 Oct 2012 17:34:07 +0300 Subject: dmaengine: dw_dmac: remove CLK dependency This driver could be used on different platforms. Thus, the HAVE_CLK dependency is dropped away. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Reviewed-by: Felipe Balbi Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 93ea87e..6761cc8 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -83,7 +83,6 @@ config INTEL_IOP_ADMA config DW_DMAC tristate "Synopsys DesignWare AHB DMA support" - depends on HAVE_CLK select DMA_ENGINE default y if CPU_AT32AP7000 help -- cgit v0.10.2 From b801479bb6f5b66f5a35feaa72451ac5ac152ef5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 18 Oct 2012 17:34:08 +0300 Subject: dmaengine: dw_dmac: amend description and indentation The driver will be used as a core part for various implementations of the DesignWare DMA device. The patch adjusts description on the top and corrects paragraph indentation in few places across the code. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 27b8e1d..62bd85c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1,6 +1,5 @@ /* - * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on - * AVR32 systems.) + * Core driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics @@ -9,6 +8,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #include #include #include @@ -222,7 +222,6 @@ static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) channel_readl(dwc, CTL_LO)); } - static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) { channel_clear_bit(dw, CH_EN, dwc->mask); @@ -1813,6 +1812,7 @@ static int dw_resume_noirq(struct device *dev) clk_prepare_enable(dw->clk); dma_writel(dw, CFG, DW_CFG_DMA_EN); + return 0; } -- cgit v0.10.2 From 21d43f49cb5e132e74bde9e34ac0760f79ab85a9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:09 +0300 Subject: dw_dmac: change dev_printk() to corresponding macros Change printk(KERN_INFO ..., dev_name(...), ...) to dev_info() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 62bd85c..155d3f1 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -456,9 +456,8 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) { - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", - lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); + dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); } static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) @@ -492,10 +491,8 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) * controller flagged an error instead of scribbling over * random memory locations. */ - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - "Bad descriptor submitted for DMA!\n"); - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " cookie: %d\n", bad_desc->txd.cookie); + dev_crit(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); + dev_crit(chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); @@ -1759,8 +1756,8 @@ static int dw_probe(struct platform_device *pdev) dma_writel(dw, CFG, DW_CFG_DMA_EN); - printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", - dev_name(&pdev->dev), nr_channels); + dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", + nr_channels); dma_async_device_register(&dw->dma); -- cgit v0.10.2 From 6168d5670bd764557b5e06b1842964a44cf34a45 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:10 +0300 Subject: dw_dmac: don't call platform_get_drvdata twice There is no need to call platform_get_drvdata twice as we have it already in dw variable. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 155d3f1..d60ef9c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1787,7 +1787,7 @@ static void dw_shutdown(struct platform_device *pdev) { struct dw_dma *dw = platform_get_drvdata(pdev); - dw_dma_off(platform_get_drvdata(pdev)); + dw_dma_off(dw); clk_disable_unprepare(dw->clk); } @@ -1796,7 +1796,7 @@ static int dw_suspend_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); - dw_dma_off(platform_get_drvdata(pdev)); + dw_dma_off(dw); clk_disable_unprepare(dw->clk); return 0; -- cgit v0.10.2 From ba84bd7146b9244de0ce04cdc668521a73f5336f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:11 +0300 Subject: dw_dmac: change dev_crit to dev_WARN in dwc_handle_error In case of handling a bad descriptor the dwc_handle_error() will dump a stack as well. It's a lot more verbose and more likely to get user's attention. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d60ef9c..9a27fb7 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -485,14 +485,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_dostart(dwc, dwc_first_active(dwc)); /* - * KERN_CRITICAL may seem harsh, but since this only happens + * WARN may seem harsh, but since this only happens * when someone submits a bad physical address in a * descriptor, we should consider ourselves lucky that the * controller flagged an error instead of scribbling over * random memory locations. */ - dev_crit(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); - dev_crit(chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); + dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" + " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); -- cgit v0.10.2 From e63a47a361e03eaf79e0f2f6cdaca8e7679d1867 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:12 +0300 Subject: dw_dmac: introduce to_dw_desc() macro The to_dw_desc() macro helps to retrieve the dw_desc node from the corresponding list_head structure. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9a27fb7..476e9c8 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -93,7 +93,7 @@ static struct device *chan2parent(struct dma_chan *chan) static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) { - return list_entry(dwc->active_list.next, struct dw_desc, desc_node); + return to_dw_desc(dwc->active_list.next); } static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) @@ -600,9 +600,7 @@ static void dw_dma_tasklet(unsigned long data) if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { if (dwc->tx_node_active != dwc->tx_list) { struct dw_desc *desc = - list_entry(dwc->tx_node_active, - struct dw_desc, - desc_node); + to_dw_desc(dwc->tx_node_active); dma_writel(dw, CLEAR.XFER, dwc->mask); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88a069f..8881e9b 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -299,6 +299,8 @@ struct dw_desc { size_t len; }; +#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) + static inline struct dw_desc * txd_to_dw_desc(struct dma_async_tx_descriptor *txd) { -- cgit v0.10.2 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 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c88f302..4c86437 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 v0.10.2 From 91998261dd0d5aefb56d87cef84f7810c32f6194 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:31 +0900 Subject: dma: amba-pl08x: use vchan_dma_desc_free_list vchan_dma_desc_free_list() iterates through each virt_dma_desc in the specified list_head and calls vchan->desc_free(). We can use it instead of repeated execution of pl08x_desc_free() for each virt_dma_desc in the list_head. Because vchan->desc_free callback is set as pl08x_desc_free() for amba-pl08x driver. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index d1cc579..6eb6a5c 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1096,15 +1096,9 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, struct pl08x_dma_chan *plchan) { LIST_HEAD(head); - struct pl08x_txd *txd; vchan_get_all_descriptors(&plchan->vc, &head); - - while (!list_empty(&head)) { - txd = list_first_entry(&head, struct pl08x_txd, vd.node); - list_del(&txd->vd.node); - pl08x_desc_free(&txd->vd); - } + vchan_dma_desc_free_list(&plchan->vc, &head); } /* -- cgit v0.10.2 From 8be9e32b310cd8c4302991c8ff6692689c7d9d76 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:32 +0900 Subject: dmatest: adjust invalid module parameters for number of source buffers DMA Engine test module has module parameters to set the number of source buffers for xor and pq operations. We can set these values larger than the maximum number of sources that the device can support. These values are not adjusted and the unsupported number of source buffers are passed to the device. But most drivers don't check it, so unexpected results will happen. This makes an appropriate adjustment for these module parameters before use. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 64b048d..3a8047b 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -242,6 +242,13 @@ static inline void unmap_dst(struct device *dev, dma_addr_t *addr, size_t len, dma_unmap_single(dev, addr[count], len, DMA_BIDIRECTIONAL); } +static unsigned int min_odd(unsigned int x, unsigned int y) +{ + unsigned int val = min(x, y); + + return val % 2 ? val : val - 1; +} + /* * This function repeatedly tests DMA transfers of various lengths and * offsets for a given operation type until it is told to exit by @@ -262,6 +269,7 @@ static int dmatest_func(void *data) struct dmatest_thread *thread = data; struct dmatest_done done = { .wait = &done_wait }; struct dma_chan *chan; + struct dma_device *dev; const char *thread_name; unsigned int src_off, dst_off, len; unsigned int error_count; @@ -283,13 +291,16 @@ static int dmatest_func(void *data) smp_rmb(); chan = thread->chan; + dev = chan->device; if (thread->type == DMA_MEMCPY) src_cnt = dst_cnt = 1; else if (thread->type == DMA_XOR) { - src_cnt = xor_sources | 1; /* force odd to ensure dst = src */ + /* force odd to ensure dst = src */ + src_cnt = min_odd(xor_sources | 1, dev->max_xor); dst_cnt = 1; } else if (thread->type == DMA_PQ) { - src_cnt = pq_sources | 1; /* force odd to ensure dst = src */ + /* force odd to ensure dst = src */ + src_cnt = min_odd(pq_sources | 1, dma_maxpq(dev, 0)); dst_cnt = 2; for (i = 0; i < src_cnt; i++) pq_coefs[i] = 1; @@ -327,7 +338,6 @@ static int dmatest_func(void *data) while (!kthread_should_stop() && !(iterations && total_tests >= iterations)) { - struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx = NULL; dma_addr_t dma_srcs[src_cnt]; dma_addr_t dma_dsts[dst_cnt]; -- cgit v0.10.2 From 1ba151cdf5ac120fc829ee6524fefedc6828947f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 28 Oct 2012 01:05:44 -0700 Subject: dma: Convert dev_printk(KERN_ to dev_( dev_ calls take less code than dev_printk(KERN_ and reducing object size is good. Coalesce formats for easier grep. Signed-off-by: Joe Perches Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 116e4ad..0eb3c13 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -369,10 +369,10 @@ static void vdbg_dump_regs(struct at_dma_chan *atchan) {} static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) { - dev_printk(KERN_CRIT, chan2dev(&atchan->chan_common), - " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", - lli->saddr, lli->daddr, - lli->ctrla, lli->ctrlb, lli->dscr); + dev_crit(chan2dev(&atchan->chan_common), + " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", + lli->saddr, lli->daddr, + lli->ctrla, lli->ctrlb, lli->dscr); } diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 9072e17..62a59fd 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -936,7 +936,7 @@ static irqreturn_t iop_adma_err_handler(int irq, void *data) struct iop_adma_chan *chan = data; unsigned long status = iop_chan_get_status(chan); - dev_printk(KERN_ERR, chan->device->common.dev, + dev_err(chan->device->common.dev, "error ( %s%s%s%s%s%s%s)\n", iop_is_err_int_parity(status, chan) ? "int_parity " : "", iop_is_err_mcu_abort(status, chan) ? "mcu_abort " : "", @@ -1017,7 +1017,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device) if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1027,7 +1027,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_sync_single_for_cpu(&iop_chan->device->pdev->dev, dest_dma, IOP_ADMA_TEST_SIZE, DMA_FROM_DEVICE); if (memcmp(src, dest, IOP_ADMA_TEST_SIZE)) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test copy failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1117,7 +1117,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test xor timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1129,7 +1129,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { u32 *ptr = page_address(dest); if (ptr[i] != cmp_word) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test xor failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1163,14 +1163,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test zero sum timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (zero_sum_result != 0) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test zero sum failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1187,7 +1187,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test memset timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1196,7 +1196,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { u32 *ptr = page_address(dest); if (ptr[i]) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test memset failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1219,14 +1219,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test non-zero sum timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (zero_sum_result != 1) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test non-zero sum failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1579,15 +1579,14 @@ static int iop_adma_probe(struct platform_device *pdev) goto err_free_iop_chan; } - dev_printk(KERN_INFO, &pdev->dev, "Intel(R) IOP: " - "( %s%s%s%s%s%s%s)\n", - dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", - dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", - dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", - dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + dev_info(&pdev->dev, "Intel(R) IOP: ( %s%s%s%s%s%s%s)\n", + dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", + dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); dma_async_device_register(dma_dev); goto out; @@ -1651,8 +1650,8 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan) /* run the descriptor */ iop_chan_enable(iop_chan); } else - dev_printk(KERN_ERR, iop_chan->device->common.dev, - "failed to allocate null descriptor\n"); + dev_err(iop_chan->device->common.dev, + "failed to allocate null descriptor\n"); spin_unlock_bh(&iop_chan->lock); } @@ -1704,7 +1703,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) /* run the descriptor */ iop_chan_enable(iop_chan); } else - dev_printk(KERN_ERR, iop_chan->device->common.dev, + dev_err(iop_chan->device->common.dev, "failed to allocate null descriptor\n"); spin_unlock_bh(&iop_chan->lock); } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index ac71f55..e26de4f 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -210,7 +210,7 @@ static void mv_set_mode(struct mv_xor_chan *chan, break; default: dev_err(mv_chan_to_devp(chan), - "error: unsupported operation %d.\n", + "error: unsupported operation %d\n", type); BUG(); return; @@ -828,28 +828,22 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan) u32 val; val = __raw_readl(XOR_CONFIG(chan)); - dev_err(mv_chan_to_devp(chan), - "config 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "config 0x%08x\n", val); val = __raw_readl(XOR_ACTIVATION(chan)); - dev_err(mv_chan_to_devp(chan), - "activation 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "activation 0x%08x\n", val); val = __raw_readl(XOR_INTR_CAUSE(chan)); - dev_err(mv_chan_to_devp(chan), - "intr cause 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "intr cause 0x%08x\n", val); val = __raw_readl(XOR_INTR_MASK(chan)); - dev_err(mv_chan_to_devp(chan), - "intr mask 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "intr mask 0x%08x\n", val); val = __raw_readl(XOR_ERROR_CAUSE(chan)); - dev_err(mv_chan_to_devp(chan), - "error cause 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "error cause 0x%08x\n", val); val = __raw_readl(XOR_ERROR_ADDR(chan)); - dev_err(mv_chan_to_devp(chan), - "error addr 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "error addr 0x%08x\n", val); } static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, @@ -862,7 +856,7 @@ static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, } dev_err(mv_chan_to_devp(chan), - "error on chan %d. intr cause 0x%08x.\n", + "error on chan %d. intr cause 0x%08x\n", chan->idx, intr_cause); mv_dump_xor_regs(chan); @@ -1052,9 +1046,8 @@ mv_xor_xor_self_test(struct mv_xor_chan *mv_chan) u32 *ptr = page_address(dest); if (ptr[i] != cmp_word) { dev_err(dma_chan->device->dev, - "Self-test xor failed compare, disabling." - " index %d, data %x, expected %x\n", i, - ptr[i], cmp_word); + "Self-test xor failed compare, disabling. index %d, data %x, expected %x\n", + i, ptr[i], cmp_word); err = -ENODEV; goto free_resources; } @@ -1194,12 +1187,11 @@ mv_xor_channel_add(struct mv_xor_device *xordev, goto err_free_irq; } - dev_info(&pdev->dev, "Marvell XOR: " - "( %s%s%s%s)\n", - dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", - dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s%s)\n", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); dma_async_device_register(dma_dev); return mv_chan; @@ -1253,7 +1245,7 @@ static int mv_xor_probe(struct platform_device *pdev) struct resource *res; int i, ret; - dev_notice(&pdev->dev, "Marvell XOR driver\n"); + dev_notice(&pdev->dev, "Marvell shared XOR driver\n"); xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL); if (!xordev) -- cgit v0.10.2 From 2c88ae90939c2ef8ae80b07713b898c577b81598 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:33 +0900 Subject: async_tx: use memchr_inv Use memchr_inv() to check the specified page is filled with zero. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 154cc84..8ade0a0 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -230,9 +230,7 @@ EXPORT_SYMBOL_GPL(async_xor); static int page_is_zero(struct page *p, unsigned int offset, size_t len) { - char *a = page_address(p) + offset; - return ((*(u32 *) a) == 0 && - memcmp(a, a + 4, len - 4) == 0); + return !memchr_inv(page_address(p) + offset, 0, len); } static inline struct dma_chan * -- cgit v0.10.2 From 35fa4dbc8c877c69144736cfe144a95a1e7ccc1a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:12 +0000 Subject: async_tx: add missing DMA unmap to async_memcpy() Do DMA unmap on ->device_prep_dma_memcpy failure. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c index 361b5e8..9e62fef 100644 --- a/crypto/async_tx/async_memcpy.c +++ b/crypto/async_tx/async_memcpy.c @@ -67,6 +67,12 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, dma_prep_flags); + if (!tx) { + dma_unmap_page(device->dev, dma_dest, len, + DMA_FROM_DEVICE); + dma_unmap_page(device->dev, dma_src, len, + DMA_TO_DEVICE); + } } if (tx) { -- cgit v0.10.2 From 522d974451743abcf674cbebd7c29d44fbd63586 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:13 +0000 Subject: ioat: add missing DMA unmap to ioat_dma_self_test() Make ioat_dma_self_test() do DMA unmapping itself and fix handling of failure cases. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 73b2b65..464138a 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -833,14 +833,14 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); - flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE | + flags = DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT; tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src, IOAT_TEST_SIZE, flags); if (!tx) { dev_err(dev, "Self-test prep failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } async_tx_ack(tx); @@ -851,7 +851,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test setup failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } dma->device_issue_pending(dma_chan); @@ -862,7 +862,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) != DMA_SUCCESS) { dev_err(dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } if (memcmp(src, dest, IOAT_TEST_SIZE)) { dev_err(dev, "Self-test copy failed compare, disabling\n"); @@ -870,6 +870,9 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) goto free_resources; } +unmap_dma: + dma_unmap_single(dev, dma_src, IOAT_TEST_SIZE, DMA_TO_DEVICE); + dma_unmap_single(dev, dma_dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); free_resources: dma->device_free_chan_resources(dma_chan); out: -- cgit v0.10.2 From d1806a5c4d2248d2799f4367dbdb1800be94a26f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:14 +0000 Subject: mtd: fsmc_nand: add missing DMA unmap to dma_xfer() Make dma_xfer() do DMA unmapping itself and fix handling of failure cases. Cc: David Woodhouse Cc: Vipin Kumar Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1d74464..a665768 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -573,23 +573,22 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, dma_dev = chan->device; dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + flags |= DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP; + if (direction == DMA_TO_DEVICE) { dma_src = dma_addr; dma_dst = host->data_pa; - flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP; } else { dma_src = host->data_pa; dma_dst = dma_addr; - flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP; } tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); - if (!tx) { dev_err(host->dev, "device_prep_dma_memcpy error\n"); - dma_unmap_single(dma_dev->dev, dma_addr, len, direction); - return -EIO; + ret = -EIO; + goto unmap_dma; } tx->callback = dma_complete; @@ -599,7 +598,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, ret = dma_submit_error(cookie); if (ret) { dev_err(host->dev, "dma_submit_error %d\n", cookie); - return ret; + goto unmap_dma; } dma_async_issue_pending(chan); @@ -610,10 +609,17 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, if (ret <= 0) { chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); dev_err(host->dev, "wait_for_completion_timeout\n"); - return ret ? ret : -ETIMEDOUT; + if (!ret) + ret = -ETIMEDOUT; + goto unmap_dma; } - return 0; + ret = 0; + +unmap_dma: + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + + return ret; } /* -- cgit v0.10.2 From bfc191ea568a9c00ab652750686f83ad2daf92a8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:15 +0000 Subject: carma-fpga: pass correct flags to ->device_prep_dma_memcpy() DMA unmapping is handled by a driver so tell fsldma.c driver (which is the DMA engine driver used by carma-fpga) to skip unmapping destination and source buffers. Cc: Ira W. Snyder Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 8835eab..6b43f8c 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -631,6 +631,8 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf) struct dma_async_tx_descriptor *tx; dma_cookie_t cookie; dma_addr_t dst, src; + unsigned long dma_flags = DMA_COMPL_SKIP_DEST_UNMAP | + DMA_COMPL_SKIP_SRC_UNMAP; dst_sg = buf->vb.sglist; dst_nents = buf->vb.sglen; @@ -666,7 +668,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf) src = SYS_FPGA_BLOCK; tx = chan->device->device_prep_dma_memcpy(chan, dst, src, REG_BLOCK_SIZE, - 0); + dma_flags); if (!tx) { dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n"); return -ENOMEM; -- cgit v0.10.2 From 5ca7c109e8eee8d97267d3308548509bb80d8bf0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 25 Sep 2012 13:59:31 -0500 Subject: of: dma: fix potential deadlock when requesting a slave channel In the latest version of the OF dma handlers I added support (rather hastily) to exhaustively search for an available dma slave channel, for the use-case where we have alternative slave channels that can be used. In the current implementation a deadlock scenario can occur causing the CPU to loop forever. The scenario is as follows ... 1. There are alternative channels avaialble 2. The first channel that is found by calling of_dma_find_channel() is not available and so the call to the xlate function returns NULL. In this case we will call of_dma_find_channel() again but we will return the same channel that we found the first time and hence, again the xlate will return NULL and we will loop here forever. Fix this potential deadlock by just using a single for-loop and not a for-loop nested in a do-while loop. This change also replaces the function of_dma_find_channel() with of_dma_match_channel() which performs a simple check to see if a DMA channel matches the name specified. I have tested this implementation on an OMAP4 panda board by adding a dummy DMA specifier, that will cause the xlate function to return NULL, to the beginning of a list of DMA specifiers for a DMA client. 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 Signed-off-by: Jon Hunter Signed-off-by: Vinod Koul diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 19ad37c..4bed490 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -108,37 +108,32 @@ void of_dma_controller_free(struct device_node *np) EXPORT_SYMBOL_GPL(of_dma_controller_free); /** - * of_dma_find_channel - Find a DMA channel by name + * of_dma_match_channel - Check if a DMA specifier matches name * @np: device node to look for DMA channels - * @name: name of desired channel + * @name: channel name to be matched + * @index: index of DMA specifier in list of DMA specifiers * @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. + * Check if the DMA specifier pointed to by the index in a list of DMA + * specifiers, matches the name provided. Returns 0 if the name matches and + * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. */ -static int of_dma_find_channel(struct device_node *np, char *name, - struct of_phandle_args *dma_spec) +static int of_dma_match_channel(struct device_node *np, char *name, int index, + 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; + if (of_property_read_string_index(np, "dma-names", index, &s)) + return -ENODEV; - for (i = 0; i < count; i++) { - if (of_property_read_string_index(np, "dma-names", i, &s)) - continue; + if (strcmp(name, s)) + return -ENODEV; - if (strcmp(name, s)) - continue; + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, + dma_spec)) + return -ENODEV; - if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, - dma_spec)) - return 0; - } - - return -ENODEV; + return 0; } /** @@ -154,19 +149,22 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, struct of_phandle_args dma_spec; struct of_dma *ofdma; struct dma_chan *chan; - int r; + int count, i; 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; - } + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + pr_err("%s: dma-names property missing or empty\n", __func__); + return NULL; + } + + for (i = 0; i < count; i++) { + if (of_dma_match_channel(np, name, i, &dma_spec)) + continue; ofdma = of_dma_find_controller(dma_spec.np); if (!ofdma) { @@ -185,9 +183,11 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, of_node_put(dma_spec.np); - } while (!chan); + if (chan) + return chan; + } - return chan; + return NULL; } /** -- cgit v0.10.2 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 diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 4bed490..59631b2 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -19,28 +19,61 @@ #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 * @of_dma_xlate: translation function which converts a phandle @@ -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 67158dd..84b64f8 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 v0.10.2 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 diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 84b64f8..d15073e 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 v0.10.2 From f7d935dcc34fae9ad4a39f2cf8e2a96199c48948 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 1 Nov 2012 22:54:43 +0800 Subject: dmaengine: sirf: enable the driver support new SiRFmarco SoC The driver supports old up SiRFprimaII SoCs, this patch makes it support the new SiRFmarco as well. SiRFmarco, as a SMP SoC, adds new DMA_INT_EN_CLR and DMA_CH_LOOP_CTRL_CLR registers, to disable IRQ/Channel, we should write 1 to the corresponding bit in the two CLEAR register. Tested on SiRFmarco using SPI driver: $ /mnt/spidev-sirftest -D /dev/spidev32766.0 spi mode: 0 bits per word: 8 max speed: 500000 Hz (500 KHz) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 $ cat /proc/interrupts CPU0 CPU1 32: 1593 0 GIC sirfsoc_timer0 33: 0 3533 GIC sirfsoc_timer1 44: 0 0 GIC sirfsoc_dma 45: 16 0 GIC sirfsoc_dma 47: 6 0 GIC sirfsoc_spi 50: 5654 0 GIC sirfsoc-uart ... Signed-off-by: Barry Song Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6761cc8..0b408bb 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -212,8 +212,8 @@ config TIMB_DMA Enable support for the Timberdale FPGA DMA engine. config SIRF_DMA - tristate "CSR SiRFprimaII DMA support" - depends on ARCH_PRIMA2 + tristate "CSR SiRFprimaII/SiRFmarco DMA support" + depends on ARCH_SIRF select DMA_ENGINE help Enable support for the CSR SiRFprimaII DMA engine. diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index c3de6ed..3c210ba 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -32,7 +32,9 @@ #define SIRFSOC_DMA_CH_VALID 0x140 #define SIRFSOC_DMA_CH_INT 0x144 #define SIRFSOC_DMA_INT_EN 0x148 +#define SIRFSOC_DMA_INT_EN_CLR 0x14C #define SIRFSOC_DMA_CH_LOOP_CTRL 0x150 +#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR 0x15C #define SIRFSOC_DMA_MODE_CTRL_BIT 4 #define SIRFSOC_DMA_DIR_CTRL_BIT 5 @@ -76,6 +78,7 @@ struct sirfsoc_dma { struct sirfsoc_dma_chan channels[SIRFSOC_DMA_CHANNELS]; void __iomem *base; int irq; + bool is_marco; }; #define DRV_NAME "sirfsoc_dma" @@ -288,13 +291,19 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) int cid = schan->chan.chan_id; unsigned long flags; - writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & - ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); - writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); - - writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) - & ~((1 << cid) | 1 << (cid + 16)), + if (!sdma->is_marco) { + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & + ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) + & ~((1 << cid) | 1 << (cid + 16)), sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + } else { + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_INT_EN_CLR); + writel_relaxed((1 << cid) | 1 << (cid + 16), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR); + } + + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); spin_lock_irqsave(&schan->lock, flags); list_splice_tail_init(&schan->active, &schan->free); @@ -568,6 +577,9 @@ static int sirfsoc_dma_probe(struct platform_device *op) return -ENOMEM; } + if (of_device_is_compatible(dn, "sirf,marco-dmac")) + sdma->is_marco = true; + if (of_property_read_u32(dn, "cell-index", &id)) { dev_err(dev, "Fail to get DMAC index\n"); return -ENODEV; @@ -668,6 +680,7 @@ static int __devexit sirfsoc_dma_remove(struct platform_device *op) static struct of_device_id sirfsoc_dma_match[] = { { .compatible = "sirf,prima2-dmac", }, + { .compatible = "sirf,marco-dmac", }, {}, }; -- cgit v0.10.2 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 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4c86437..3aa76db 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 v0.10.2 From e4d43c1764bc3ee1150f24e530db2b5b23e91425 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 15 Nov 2012 06:27:50 +0000 Subject: DMA: PL330: Use devm_* functions devm_* functions are device managed and make the code and error handling a bit simpler. Cc: Jassi Brar Signed-off-by: Sachin Kamat Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 95555f3..f7edb6f 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2866,7 +2866,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pdat = adev->dev.platform_data; /* Allocate a new DMAC and its Channels */ - pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL); + pdmac = devm_kzalloc(&adev->dev, sizeof(*pdmac), GFP_KERNEL); if (!pdmac) { dev_err(&adev->dev, "unable to allocate mem\n"); return -ENOMEM; @@ -2878,13 +2878,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0; res = &adev->res; - request_mem_region(res->start, resource_size(res), "dma-pl330"); - - pi->base = ioremap(res->start, resource_size(res)); - if (!pi->base) { - ret = -ENXIO; - goto probe_err1; - } + pi->base = devm_request_and_ioremap(&adev->dev, res); + if (!pi->base) + return -ENXIO; amba_set_drvdata(adev, pdmac); @@ -2892,11 +2888,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = request_irq(irq, pl330_irq_handler, 0, dev_name(&adev->dev), pi); if (ret) - goto probe_err2; + return ret; ret = pl330_add(pi); if (ret) - goto probe_err3; + goto probe_err1; INIT_LIST_HEAD(&pdmac->desc_pool); spin_lock_init(&pdmac->pool_lock); @@ -2918,7 +2914,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) if (!pdmac->peripherals) { ret = -ENOMEM; dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n"); - goto probe_err4; + goto probe_err2; } for (i = 0; i < num_chan; i++) { @@ -2962,7 +2958,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = dma_async_device_register(pd); if (ret) { dev_err(&adev->dev, "unable to register DMAC\n"); - goto probe_err4; + goto probe_err2; } dev_info(&adev->dev, @@ -2975,15 +2971,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) return 0; -probe_err4: - pl330_del(pi); -probe_err3: - free_irq(irq, pi); probe_err2: - iounmap(pi->base); + pl330_del(pi); probe_err1: - release_mem_region(res->start, resource_size(res)); - kfree(pdmac); + free_irq(irq, pi); return ret; } @@ -2993,7 +2984,6 @@ static int __devexit pl330_remove(struct amba_device *adev) struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev); struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; - struct resource *res; int irq; if (!pdmac) @@ -3020,13 +3010,6 @@ static int __devexit pl330_remove(struct amba_device *adev) irq = adev->irq[0]; free_irq(irq, pi); - iounmap(pi->base); - - res = &adev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pdmac); - return 0; } -- cgit v0.10.2 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 diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 3a8047b..99a75e5 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 3aa76db..be6e953 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 v0.10.2 From 7c1119bdd650fa58dad8157bc75c5fcf6ed97843 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 28 Nov 2012 06:49:47 +0000 Subject: dma: sh: Don't use ENODEV for failing slave lookup If dmaengine driver's .device_alloc_chan_resources() method returns -ENODEV, dma_request_channel() will decide, that the driver has been removed and will remove the device from its list. To prevent this use ENXIO if a slave lookup fails. Reported-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Cc: stable@vger.kernel.org Signed-off-by: Vinod Koul diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 8201bb4..62eff48 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -326,7 +326,7 @@ static int sh_dmae_set_slave(struct shdma_chan *schan, shdma_chan); const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id); if (!cfg) - return -ENODEV; + return -ENXIO; if (!try) sh_chan->config = cfg; -- cgit v0.10.2 From 5e034f7b659be9d94e64aaaa985ab530dd847fdb Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 9 Nov 2012 15:26:29 +0000 Subject: dmaengine/dmatest: terminate transfers only in case of errors dmatest erroneously terminated transfers in normal cases also leading to test failures for multiple threads over a channel. Fix this and terminate transfers only in case of errors. Signed-off-by: Shiraz Hashim Signed-off-by: Deepak Sikri Signed-off-by: Vinod Koul diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 99a75e5..a2c8904 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -536,7 +536,9 @@ err_srcs: thread_name, total_tests, failed_tests, ret); /* terminate all transfers on specified channels */ - dmaengine_terminate_all(chan); + if (ret) + dmaengine_terminate_all(chan); + if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); -- cgit v0.10.2 From 7369f56e3e7193583576ec705d95647b04838b05 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:19 +0000 Subject: ioat3: add missing DMA unmap to ioat_xor_val_self_test() Make ioat_xor_val_self_test() do DMA unmapping itself and fix handling of failure cases. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index f7f1dc6..6456f7d 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -863,6 +863,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) unsigned long tmo; struct device *dev = &device->pdev->dev; struct dma_device *dma = &device->common; + u8 op = 0; dev_dbg(dev, "%s\n", __func__); @@ -908,18 +909,22 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) } /* test xor */ + op = IOAT_OP_XOR; + dest_dma = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); for (i = 0; i < IOAT_NUM_SRC_TEST; i++) dma_srcs[i] = dma_map_page(dev, xor_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs, IOAT_NUM_SRC_TEST, PAGE_SIZE, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test xor prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -930,7 +935,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test xor setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -939,9 +944,13 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test xor timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + dma_sync_single_for_cpu(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { u32 *ptr = page_address(dest); @@ -957,6 +966,8 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) goto free_resources; + op = IOAT_OP_XOR_VAL; + /* validate the sources with the destintation page */ for (i = 0; i < IOAT_NUM_SRC_TEST; i++) xor_val_srcs[i] = xor_srcs[i]; @@ -969,11 +980,13 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) DMA_TO_DEVICE); tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, - &xor_val_result, DMA_PREP_INTERRUPT); + &xor_val_result, DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test zero prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -984,7 +997,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test zero setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -993,9 +1006,12 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test validate timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + if (xor_val_result != 0) { dev_err(dev, "Self-test validate failed compare\n"); err = -ENODEV; @@ -1007,14 +1023,18 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) goto free_resources; /* test memset */ + op = IOAT_OP_FILL; + dma_addr = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); tx = dma->device_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test memset prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -1025,7 +1045,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test memset setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -1034,9 +1054,11 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test memset timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { u32 *ptr = page_address(dest); if (ptr[i]) { @@ -1047,17 +1069,21 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) } /* test for non-zero parity sum */ + op = IOAT_OP_XOR_VAL; + xor_val_result = 0; for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, - &xor_val_result, DMA_PREP_INTERRUPT); + &xor_val_result, DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test 2nd zero prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -1068,7 +1094,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test 2nd zero setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -1077,15 +1103,31 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test 2nd validate timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } if (xor_val_result != SUM_CHECK_P_RESULT) { dev_err(dev, "Self-test validate failed compare\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + + goto free_resources; +dma_unmap: + if (op == IOAT_OP_XOR) { + dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, + DMA_TO_DEVICE); + } else if (op == IOAT_OP_XOR_VAL) { + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, + DMA_TO_DEVICE); + } else if (op == IOAT_OP_FILL) + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); free_resources: dma->device_free_chan_resources(dma_chan); out: -- cgit v0.10.2 From 06eeb114026804a9a9cb83eaeb00e24aaa40ba0b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:20 +0000 Subject: async_tx: fix build for async_memset Add missing include. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c index 58e4a87..05a4d1e 100644 --- a/crypto/async_tx/async_memset.c +++ b/crypto/async_tx/async_memset.c @@ -25,6 +25,7 @@ */ #include #include +#include #include #include #include -- cgit v0.10.2 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 diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index eaddfe9..736c771 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 6b43f8c..7508caf 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 be6e953..cd15958 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 1ca2536..cf949a1 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 v0.10.2 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 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cd15958..4ca9cf7 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 cf949a1..db0856a 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 v0.10.2 From 2cbe7feba1ac521b5668609c35b94536bbbcd52f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:02:07 +0000 Subject: dmaengine: add cpu_relax() to busy-loop in dma_sync_wait() Removal of the busy-loop from dma_sync_wait() is not a trivial task so just add cpu_relax() to the loop for now. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index d37cf95..242b8c0 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -267,7 +267,10 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) pr_err("%s: timeout!\n", __func__); return DMA_ERROR; } - } while (status == DMA_IN_PROGRESS); + if (status != DMA_IN_PROGRESS) + break; + cpu_relax(); + } while (1); return status; } -- cgit v0.10.2 From 7d283397ade3c9e51de644676a6593e1f724ac00 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:03:16 +0000 Subject: async_tx: fix checking of dma_wait_for_async_tx() return value dma_wait_for_async_tx() can also return DMA_PAUSED (which should be considered as error). Cc: Vinod Koul Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index 8421209..7be3424 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -128,8 +128,8 @@ async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx, } device->device_issue_pending(chan); } else { - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", + if (dma_wait_for_async_tx(depend_tx) != DMA_SUCCESS) + panic("%s: DMA error waiting for depend_tx\n", __func__); tx->tx_submit(tx); } @@ -280,8 +280,9 @@ void async_tx_quiesce(struct dma_async_tx_descriptor **tx) * we are referring to the correct operation */ BUG_ON(async_tx_test_ack(*tx)); - if (dma_wait_for_async_tx(*tx) == DMA_ERROR) - panic("DMA_ERROR waiting for transaction\n"); + if (dma_wait_for_async_tx(*tx) != DMA_SUCCESS) + panic("%s: DMA error waiting for transaction\n", + __func__); async_tx_ack(*tx); *tx = NULL; } -- cgit v0.10.2 From 1a363068dcc2269931daef360ed14d2a262f19f7 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 3 Dec 2012 16:08:37 -0700 Subject: ioat: Add alignment workaround for IVB platforms The PCI IDs for IvyBridge IOAT DMA needs to go into a header file since dma_v3.c looks them up for certain hardware workarounds. Need to add to the alignment workaround for IOAT 3.2 since it wasn't fixed in IVB. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 6456f7d..9d6f3bb 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1229,6 +1229,26 @@ static bool is_snb_ioat(struct pci_dev *pdev) } } +static bool is_ivb_ioat(struct pci_dev *pdev) +{ + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IOAT_IVB0: + case PCI_DEVICE_ID_INTEL_IOAT_IVB1: + case PCI_DEVICE_ID_INTEL_IOAT_IVB2: + case PCI_DEVICE_ID_INTEL_IOAT_IVB3: + case PCI_DEVICE_ID_INTEL_IOAT_IVB4: + case PCI_DEVICE_ID_INTEL_IOAT_IVB5: + case PCI_DEVICE_ID_INTEL_IOAT_IVB6: + case PCI_DEVICE_ID_INTEL_IOAT_IVB7: + case PCI_DEVICE_ID_INTEL_IOAT_IVB8: + case PCI_DEVICE_ID_INTEL_IOAT_IVB9: + return true; + default: + return false; + } + +} + int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) { struct pci_dev *pdev = device->pdev; @@ -1249,7 +1269,7 @@ int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; dma->device_free_chan_resources = ioat2_free_chan_resources; - if (is_jf_ioat(pdev) || is_snb_ioat(pdev)) + if (is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev)) dma->copy_align = 6; dma_cap_set(DMA_INTERRUPT, dma->cap_mask); diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index d2ff3fd..7cb74c6 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -35,6 +35,17 @@ #define IOAT_VER_3_0 0x30 /* Version 3.0 */ #define IOAT_VER_3_2 0x32 /* Version 3.2 */ +#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB3 0x0e23 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB4 0x0e24 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB5 0x0e25 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB6 0x0e26 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB7 0x0e27 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e +#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f + int system_has_dca_enabled(struct pci_dev *pdev); struct ioat_dma_descriptor { diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index bfa9a35..f4e6163 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -40,17 +40,6 @@ MODULE_VERSION(IOAT_DMA_VERSION); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel Corporation"); -#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB3 0x0e23 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB4 0x0e24 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB5 0x0e25 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB6 0x0e26 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB7 0x0e27 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e -#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f - static struct pci_device_id ioat_pci_tbl[] = { /* I/OAT v1 platforms */ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT) }, -- cgit v0.10.2 From 6decffd5f6afaf55722d9c85b8739621dca63d0f Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 27 Nov 2012 15:16:08 -0700 Subject: ioat: remove chanerr mask setting for IOAT v3.x The existing code set a value in the PCI_CHANERRMSK_INT register for a workaround to address a pre-silicon bug on the Intel 5520 IO hub that has been fixed when the hardware was released. There is no need for this code. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 9d6f3bb..e52cf1e 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1168,12 +1168,7 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan) chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET); - /* -= IOAT ver.3 workarounds =- */ - /* Write CHANERRMSK_INT with 3E07h to mask out the errors - * that can cause stability issues for IOAT ver.3, and clear any - * pending errors - */ - pci_write_config_dword(pdev, IOAT_PCI_CHANERRMASK_INT_OFFSET, 0x3e07); + /* clear any pending errors */ err = pci_read_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, &chanerr); if (err) { dev_err(&pdev->dev, "channel error register unreachable\n"); -- cgit v0.10.2 From c419fcfd071cf34ba00f9f65282583772d2655e7 Mon Sep 17 00:00:00 2001 From: Maciej Sosnowski Date: Wed, 23 May 2012 17:27:07 +0200 Subject: dca: check against empty dca_domains list before unregister provider When providers get blocked unregister_dca_providers() is called ending up with dca_providers and dca_domain lists emptied. Dca should be prevented from trying to unregister any provider if dca_domain list is found empty. Cc: Reported-by: Jiang Liu Tested-by: Gaohuai Han Signed-off-by: Maciej Sosnowski Signed-off-by: Dan Williams diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bc6f5fa..819dfda 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -420,6 +420,11 @@ void unregister_dca_provider(struct dca_provider *dca, struct device *dev) raw_spin_lock_irqsave(&dca_lock, flags); + if (list_empty(&dca_domains)) { + raw_spin_unlock_irqrestore(&dca_lock, flags); + return; + } + list_del(&dca->node); pci_rc = dca_pci_rc_from_dev(dev); -- cgit v0.10.2 From e65f32ca21faed30ce37cd6480271697fe671f74 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 6 Sep 2012 09:19:35 +0200 Subject: dma: ipu: Drop unused spinlock I was checking why this spinlock was never initialized, but it turns out it's not used anywhere, so we can drop it. Signed-off-by: Jean Delvare Cc: Vinod Koul Signed-off-by: Dan Williams diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index a5ee37d..2e284a4 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -44,7 +44,6 @@ static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg) struct ipu_irq_bank { unsigned int control; unsigned int status; - spinlock_t lock; struct ipu *ipu; }; -- cgit v0.10.2 From 1b140908c4cda43c653bb080c244d112e619008f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:02 +0530 Subject: dma: tegra: add support for channel wise pause NVIDIA's some SoCs like Tegra114 support the channel wise pause control inplace of global pause which pauses all DMA channels. When SoCs support the channel wise pause control then it uses the global pause for clock gating for register access as well as all DMA channel pause. Hence DMA registers are not accessible if DMAs are globally paused on these new SoCs. Add support for channel wise pause feature if SoCs support it. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index efdfffa..2c46ac4 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -62,6 +62,9 @@ #define TEGRA_APBDMA_STATUS_COUNT_SHIFT 2 #define TEGRA_APBDMA_STATUS_COUNT_MASK 0xFFFC +#define TEGRA_APBDMA_CHAN_CSRE 0x00C +#define TEGRA_APBDMA_CHAN_CSRE_PAUSE (1 << 31) + /* AHB memory address */ #define TEGRA_APBDMA_CHAN_AHBPTR 0x010 @@ -112,10 +115,12 @@ struct tegra_dma; * tegra_dma_chip_data Tegra chip specific DMA data * @nr_channels: Number of channels available in the controller. * @max_dma_count: Maximum DMA transfer count supported by DMA controller. + * @support_channel_pause: Support channel wise pause of dma. */ struct tegra_dma_chip_data { int nr_channels; int max_dma_count; + bool support_channel_pause; }; /* DMA channel registers */ @@ -353,6 +358,32 @@ static void tegra_dma_global_resume(struct tegra_dma_channel *tdc) spin_unlock(&tdma->global_lock); } +static void tegra_dma_pause(struct tegra_dma_channel *tdc, + bool wait_for_burst_complete) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, + TEGRA_APBDMA_CHAN_CSRE_PAUSE); + if (wait_for_burst_complete) + udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME); + } else { + tegra_dma_global_pause(tdc, wait_for_burst_complete); + } +} + +static void tegra_dma_resume(struct tegra_dma_channel *tdc) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, 0); + } else { + tegra_dma_global_resume(tdc); + } +} + static void tegra_dma_stop(struct tegra_dma_channel *tdc) { u32 csr; @@ -408,7 +439,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, * If there is already IEC status then interrupt handler need to * load new configuration. */ - tegra_dma_global_pause(tdc, false); + tegra_dma_pause(tdc, false); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); /* @@ -418,7 +449,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { dev_err(tdc2dev(tdc), "Skipping new configuration as interrupt is pending\n"); - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); return; } @@ -429,7 +460,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB); nsg_req->configured = true; - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); } static void tdc_start_head_req(struct tegra_dma_channel *tdc) @@ -690,7 +721,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) goto skip_dma_stop; /* Pause DMA before checking the queue status */ - tegra_dma_global_pause(tdc, true); + tegra_dma_pause(tdc, true); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { @@ -708,7 +739,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) sgreq->dma_desc->bytes_transferred += get_current_xferred_count(tdc, sgreq, status); } - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); skip_dma_stop: tegra_dma_abort_all(tdc); @@ -1175,6 +1206,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc) static const struct tegra_dma_chip_data tegra20_dma_chip_data = { .nr_channels = 16, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; #if defined(CONFIG_OF) @@ -1182,6 +1214,7 @@ static const struct tegra_dma_chip_data tegra20_dma_chip_data = { static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .nr_channels = 32, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; static const struct of_device_id tegra_dma_of_match[] __devinitconst = { -- cgit v0.10.2 From 5ea7caf30debefc1c4319f77146288fd5e92a803 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:03 +0530 Subject: dma: tegra: add support for Tegra114 SoC NVIDIA's Tegra114 has APB DMA controller which has 32 dma channels and support support channel wise pause control. Add support for Tegra114 which uses the channel wise pause control hardware feature. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 2c46ac4..6c14481 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1217,8 +1217,19 @@ static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .support_channel_pause = false, }; -static const struct of_device_id tegra_dma_of_match[] __devinitconst = { +/* Tegra114 specific DMA controller information */ +static const struct tegra_dma_chip_data tegra114_dma_chip_data = { + .nr_channels = 32, + .max_dma_count = 1024UL * 64, + .support_channel_pause = true, +}; + + +static const struct of_device_id tegra_dma_of_match[] = { { + .compatible = "nvidia,tegra114-apbdma", + .data = &tegra114_dma_chip_data, + }, { .compatible = "nvidia,tegra30-apbdma", .data = &tegra30_dma_chip_data, }, { -- cgit v0.10.2 From f2ad6992546674e5915a34a1bc36dcdd8fb29bd2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 7 Jan 2013 23:48:39 -0200 Subject: dma: mxs-dma: Fix build warnings with W=1 Fix the following warnings when building with W=1 option: drivers/dma/mxs-dma.c: In function 'mxs_dma_alloc_chan_resources': drivers/dma/mxs-dma.c:368:25: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c: In function 'mxs_dma_prep_slave_sg': drivers/dma/mxs-dma.c:481:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c:494:3: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c:515:14: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c: In function 'mxs_dma_prep_dma_cyclic': drivers/dma/mxs-dma.c:563:13: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 9f02e79..8f6d30d 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -109,7 +109,7 @@ struct mxs_dma_chan { struct dma_chan chan; struct dma_async_tx_descriptor desc; struct tasklet_struct tasklet; - int chan_irq; + unsigned int chan_irq; struct mxs_dma_ccw *ccw; dma_addr_t ccw_phys; int desc_count; @@ -441,7 +441,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; struct mxs_dma_ccw *ccw; struct scatterlist *sg; - int i, j; + u32 i, j; u32 *pio; bool append = flags & DMA_PREP_INTERRUPT; int idx = append ? mxs_chan->desc_count : 0; @@ -537,8 +537,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - int num_periods = buf_len / period_len; - int i = 0, buf = 0; + u32 num_periods = buf_len / period_len; + u32 i = 0, buf = 0; if (mxs_chan->status == DMA_IN_PROGRESS) return NULL; -- cgit v0.10.2 From 123de543414bce42da9729071962d4a9512612c8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:01 +0200 Subject: dw_dmac: absence of pdata isn't critical when autocfg is set The patch allows to probe the device when platform data is absent and hardware auto configuration is enabled. In that case the default platform data is used where the channel allocation order is set to ascending, channel priority is set to ascending, and private property is set to true. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 476e9c8..c0c9370 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1600,13 +1600,6 @@ static int dw_probe(struct platform_device *pdev) int err; 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; - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) return -EINVAL; @@ -1622,6 +1615,22 @@ static int dw_probe(struct platform_device *pdev) dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + + if (!pdata && autocfg) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Fill platform data with the default values */ + pdata->is_private = true; + pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; + pdata->chan_priority = CHAN_PRIORITY_ASCENDING; + } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + if (autocfg) nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; else -- cgit v0.10.2 From cbd65312ba6b508e994d40729e84a51301870bcc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:11 +0200 Subject: dw_dmac: check for mapping errors Otherwise we get a warning in case of CONFIG_DMA_API_DEBUG=y [ 45.775943] WARNING: at lib/dma-debug.c:933 check_unmap+0x5d6/0x6ac() [ 45.782369] dw_dmac dw_dmac.0: DMA-API: device driver failed to check map error[device address=0x00000000356efcc0] [size=28 bytes] [mapped as single] Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c0c9370..dd7a819 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1087,6 +1087,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; + int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1110,12 +1111,8 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); - if (!desc) { - dev_info(chan2dev(chan), - "only allocated %d descriptors\n", i); - spin_lock_irqsave(&dwc->lock, flags); - break; - } + if (!desc) + goto err_desc_alloc; INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); @@ -1123,6 +1120,10 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) desc->txd.flags = DMA_CTRL_ACK; desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, sizeof(desc->lli), DMA_TO_DEVICE); + ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); + if (ret) + goto err_desc_alloc; + dwc_desc_put(dwc, desc); spin_lock_irqsave(&dwc->lock, flags); @@ -1134,6 +1135,13 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); return i; + +err_desc_alloc: + kfree(desc); + + dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); + + return i; } static void dwc_free_chan_resources(struct dma_chan *chan) -- cgit v0.10.2 From 21e93c1e7dae0e8b1914a522c331f0f7763fa89d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:12 +0200 Subject: dw_dmac: remove redundant check There is no need to check the callback_required parameter, due to we check the callback pointer to be a non-NULL. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index dd7a819..c74a7ec 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -347,7 +347,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, spin_unlock_irqrestore(&dwc->lock, flags); - if (callback_required && callback) + if (callback) callback(param); } -- cgit v0.10.2 From f5c6a7df35b04db906577e90fa5e133e56433bcf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:13 +0200 Subject: dw_dmac: update tx_node_active in dwc_do_single_block The "else" keyword in the dw_dma_tasklet is removed as well. All together simplifies the logic of the code and understanding of what is happening there. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c74a7ec..d6b322a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -247,6 +247,9 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc, channel_writel(dwc, CTL_LO, ctllo); channel_writel(dwc, CTL_HI, desc->lli.ctlhi); channel_set_bit(dw, CH_EN, dwc->mask); + + /* Move pointer to next descriptor */ + dwc->tx_node_active = dwc->tx_node_active->next; } /* Called with dwc->lock held and bh disabled */ @@ -278,7 +281,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); dwc->tx_list = &first->tx_list; - dwc->tx_node_active = first->tx_list.next; + dwc->tx_node_active = &first->tx_list; dwc_do_single_block(dwc, first); @@ -604,18 +607,13 @@ static void dw_dma_tasklet(unsigned long data) dma_writel(dw, CLEAR.XFER, dwc->mask); - /* move pointer to next descriptor */ - dwc->tx_node_active = - dwc->tx_node_active->next; - dwc_do_single_block(dwc, desc); spin_unlock_irqrestore(&dwc->lock, flags); continue; - } else { - /* we are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } + /* we are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v0.10.2 From 21fe3c5245647d200a7ba25d42b80d21c578a8dc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:14 +0200 Subject: dma: dw_dmac: add dwc_chan_pause and dwc_chan_resume We will use at least the dwc_chan_resume() later. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d6b322a..6c9e20a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -988,6 +988,26 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return 0; } +static inline void dwc_chan_pause(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY)) + cpu_relax(); + + dwc->paused = true; +} + +static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + + dwc->paused = false; +} + static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -995,18 +1015,13 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct dw_dma *dw = to_dw_dma(chan->device); struct dw_desc *desc, *_desc; unsigned long flags; - u32 cfglo; LIST_HEAD(list); if (cmd == DMA_PAUSE) { spin_lock_irqsave(&dwc->lock, flags); - cfglo = channel_readl(dwc, CFG_LO); - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); - while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY)) - cpu_relax(); + dwc_chan_pause(dwc); - dwc->paused = true; spin_unlock_irqrestore(&dwc->lock, flags); } else if (cmd == DMA_RESUME) { if (!dwc->paused) @@ -1014,9 +1029,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, spin_lock_irqsave(&dwc->lock, flags); - cfglo = channel_readl(dwc, CFG_LO); - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); - dwc->paused = false; + dwc_chan_resume(dwc); spin_unlock_irqrestore(&dwc->lock, flags); } else if (cmd == DMA_TERMINATE_ALL) { -- cgit v0.10.2 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 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4ca9cf7..bfcdecb 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 v0.10.2 From 0e7264cc79a2d5c0ffa32c08d8f1cf84b2ec4fef Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:57 +0200 Subject: dma: at_hdmac: check direction properly for cyclic transfers dma_transfer_direction is a normal enum. It means we can't usually use the values as bit fields. Let's adjust this check and move it above the usage of the direction parameter, due to the nature of the following usage of it. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 13a02f4..6e13f26 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -778,7 +778,7 @@ err: */ static int atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len) { if (period_len > (ATC_BTSIZE_MAX << reg_width)) goto err_out; @@ -786,8 +786,6 @@ atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, goto err_out; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto err_out; - if (unlikely(!(direction & (DMA_DEV_TO_MEM | DMA_MEM_TO_DEV)))) - goto err_out; return 0; @@ -886,14 +884,16 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, return NULL; } + if (unlikely(!is_slave_direction(direction))) + goto err_out; + if (sconfig->direction == DMA_MEM_TO_DEV) reg_width = convert_buswidth(sconfig->dst_addr_width); else reg_width = convert_buswidth(sconfig->src_addr_width); /* Check for too big/unaligned periods and unaligned DMA buffer */ - if (atc_dma_cyclic_check_values(reg_width, buf_addr, - period_len, direction)) + if (atc_dma_cyclic_check_values(reg_width, buf_addr, period_len)) goto err_out; /* build cyclic linked list */ -- cgit v0.10.2 From f44b92f4dd2f6caf326b149e0b9636a1d4e50184 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:58 +0200 Subject: dma: dw_dmac: check direction properly in dw_dma_cyclic_prep dma_transfer_direction is a normal enum. It means we can't usually use the values as bit fields. Let's adjust this check and move it above the usage of the direction parameter, due to the nature of the following usage of it. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6c9e20a..ca996bc 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1355,6 +1355,9 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, retval = ERR_PTR(-EINVAL); + if (unlikely(!is_slave_direction(direction))) + goto out_err; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1369,8 +1372,6 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, goto out_err; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto out_err; - if (unlikely(!(direction & (DMA_MEM_TO_DEV | DMA_DEV_TO_MEM)))) - goto out_err; retval = ERR_PTR(-ENOMEM); -- cgit v0.10.2 From 0efcdb20f4a83967c99da3d3bef9018f86532fae Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:59 +0200 Subject: dma: ep93xx_dma: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Acked-by: Mika Westerberg Signed-off-by: Vinod Koul diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index bcfde40..f2bf8c0 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -903,8 +903,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) switch (data->port) { case EP93XX_DMA_SSP: case EP93XX_DMA_IDE: - if (data->direction != DMA_MEM_TO_DEV && - data->direction != DMA_DEV_TO_MEM) + if (!is_slave_direction(data->direction)) return -EINVAL; break; default: -- cgit v0.10.2 From 5127c4f8a314b798459985d93f7829cf9cf9bbc3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:00 +0200 Subject: dma: ipu_idmac: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Cc: Guennadi Liakhovetski Signed-off-by: Vinod Koul diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 6585537..8c61d17 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1347,7 +1347,7 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan chan->chan_id != IDMAC_IC_7) return NULL; - if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) { + if (!is_slave_direction(direction)) { dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); return NULL; } -- cgit v0.10.2 From a725dcc0342b4d9ffc6ae4aedc2973d902aabeb1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:01 +0200 Subject: dma: ste_dma40: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Reviewed-by: Linus Walleij Signed-off-by: Vinod Koul diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23c5573..e5e60bb 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2337,7 +2337,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, unsigned long dma_flags, void *context) { - if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) + if (!is_slave_direction(direction)) return NULL; return d40_prep_sg(chan, sgl, sgl, sg_len, direction, dma_flags); -- cgit v0.10.2 From 01126856ff4f7d4cc5899c208fd4d3c7d0a2b83a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:02 +0200 Subject: dw_dmac: call .probe after we have a device in place If we don't yet have the platform device for the driver when it is being loaded we fail to probe the driver. So instead of calling probe() directly we call platform_driver_register(). It will call the probe() immediately if we have the device but also makes the driver to work on platforms where the platform device is created later. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index ca996bc..e554027 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1858,6 +1858,7 @@ MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif static struct platform_driver dw_driver = { + .probe = dw_probe, .remove = dw_remove, .shutdown = dw_shutdown, .driver = { @@ -1869,7 +1870,7 @@ static struct platform_driver dw_driver = { static int __init dw_init(void) { - return platform_driver_probe(&dw_driver, dw_probe); + return platform_driver_register(&dw_driver); } subsys_initcall(dw_init); -- cgit v0.10.2 From 0fdb567fc72da906e230ce7e2aae2feba260a6be Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:03 +0200 Subject: dw_dmac: store direction in the custom channel structure Currently the direction value comes from the generic slave configuration structure and explicitly as a preparation function parameter. The first one is kinda obsoleted. Thus, we have to store the value passed to the preparation function somewhere in our structures to be able to use it later. The best candidate to provide the storage is a custom channel structure. Until now we still keep and check the direction field of the slave config structure as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e554027..a2c5a60 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -178,9 +178,9 @@ static void dwc_initialize(struct dw_dma_chan *dwc) cfghi = dws->cfg_hi; cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; } else { - if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV) + if (dwc->direction == DMA_MEM_TO_DEV) cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id); - else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM) + else if (dwc->direction == DMA_DEV_TO_MEM) cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id); } @@ -721,6 +721,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } + dwc->direction = DMA_MEM_TO_MEM; + data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], dwc->dw->data_width[dwc_get_dms(dws)]); @@ -805,6 +807,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (unlikely(!dws || !sg_len)) return NULL; + dwc->direction = direction; + prev = first = NULL; switch (direction) { @@ -981,6 +985,7 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->direction = sconfig->direction; convert_burst(&dwc->dma_sconfig.src_maxburst); convert_burst(&dwc->dma_sconfig.dst_maxburst); @@ -1358,6 +1363,8 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, if (unlikely(!is_slave_direction(direction))) goto out_err; + dwc->direction = direction; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1732,6 +1739,7 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); dwc->dw = dw; + dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 8881e9b..f9532c2 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #define DW_DMA_MAX_NR_CHANNELS 8 @@ -184,12 +185,13 @@ enum dw_dmac_flags { }; struct dw_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - u8 mask; - u8 priority; - bool paused; - bool initialized; + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + u8 priority; + enum dma_transfer_direction direction; + bool paused; + bool initialized; /* software emulation of the LLP transfers */ struct list_head *tx_list; -- cgit v0.10.2 From 495aea4b571d1b7f77883f87754247b115627f68 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 11:11:41 +0200 Subject: dw_dmac: make usage of dw_dma_slave optional The driver requires a custom slave configuration to be present to be able to make the slave transfers. Nevertheless, in some cases we need only the request line as an additional information to the generic slave configuration. The request line is provided by slave_id parameter of the dma_slave_config structure. That's why the custom slave configuration could be optional for such cases. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index a2c5a60..e5378b5 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -50,11 +50,12 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ + bool _is_slave = is_slave_direction(_dwc->direction); \ int _dms = dwc_get_dms(__slave); \ int _sms = dwc_get_sms(__slave); \ - u8 _smsize = __slave ? _sconfig->src_maxburst : \ + u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ - u8 _dmsize = __slave ? _sconfig->dst_maxburst : \ + u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ DW_DMA_MSIZE_16; \ \ (DWC_CTLL_DST_MSIZE(_dmsize) \ @@ -328,7 +329,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, list_splice_init(&desc->tx_list, &dwc->free_list); list_move(&desc->desc_node, &dwc->free_list); - if (!dwc->chan.private) { + if (!is_slave_direction(dwc->direction)) { struct device *parent = chan2parent(&dwc->chan); if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) @@ -804,7 +805,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dev_vdbg(chan2dev(chan), "%s\n", __func__); - if (unlikely(!dws || !sg_len)) + if (unlikely(!is_slave_direction(direction) || !sg_len)) return NULL; dwc->direction = direction; @@ -980,8 +981,8 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - /* Check if it is chan is configured for slave transfers */ - if (!chan->private) + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(sconfig->direction)) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); -- cgit v0.10.2 From 23d5f4ec9de43dbc73a42f1483d9339b907c3dff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:05 +0200 Subject: dw_dmac: backlink to dw_dma in dw_dma_chan is superfluous The same information could be extracted from the struct dma_chan. The patch introduces helper function dwc_get_data_width() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e5378b5..154952a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -73,6 +73,22 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + + if (master == SRC_MASTER) + return dw->data_width[dwc_get_sms(dws)]; + else if (master == DST_MASTER) + return dw->data_width[dwc_get_dms(dws)]; + + return 0; +} + /*----------------------------------------------------------------------*/ /* @@ -701,7 +717,6 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dw_desc *desc; struct dw_desc *first; struct dw_desc *prev; @@ -724,8 +739,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction = DMA_MEM_TO_MEM; - data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], - dwc->dw->data_width[dwc_get_dms(dws)]); + data_width = min_t(unsigned int, dwc_get_data_width(chan, SRC_MASTER), + dwc_get_data_width(chan, DST_MASTER)); src_width = dst_width = min_t(unsigned int, data_width, dwc_fast_fls(src | dest | len)); @@ -790,7 +805,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags, void *context) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; @@ -824,7 +838,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); - data_width = dwc->dw->data_width[dwc_get_sms(dws)]; + data_width = dwc_get_data_width(chan, SRC_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -887,7 +901,7 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); - data_width = dwc->dw->data_width[dwc_get_dms(dws)]; + data_width = dwc_get_data_width(chan, DST_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -1739,7 +1753,6 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); - dwc->dw = dw; dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index f9532c2..577f2dd 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -214,9 +214,6 @@ struct dw_dma_chan { /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; - - /* backlink to dw_dma */ - struct dw_dma *dw; }; static inline struct dw_dma_chan_regs __iomem * -- cgit v0.10.2 From a5dbff111cacecd2e79843a51cc86d21d3648af5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 10 Jan 2013 10:53:06 +0200 Subject: dma: dw_dmac: clear suspend bit during termination The DMA transfer could not be established if previously it was paused and terminated. In that case the channel's suspend bit remains set that prevents to transfer anything until channel is resumed. The patch adds the dwc_chan_resume() call instead of a plain flag assignment. That clears the DWC_CFGL_CH_SUSP bit as well during termination. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 154952a..28d5f01 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1059,7 +1059,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, dwc_chan_disable(dw, dwc); - dwc->paused = false; + dwc_chan_resume(dwc); /* active_list entries will end up before queued entries */ list_splice_init(&dwc->queue, &list); -- cgit v0.10.2 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 diff --git a/arch/arm/include/asm/hardware/pl080.h b/arch/arm/include/asm/hardware/pl080.h deleted file mode 100644 index 4eea210..0000000 --- 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 f2a7a17..a77f521 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 38fe95d..3d9b1b5 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 5a5a52d..8ce65a2 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 6eb6a5c..8bad254 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 0000000..3e7b62f --- /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 v0.10.2 From 0fd602235dd702d16722857da748d15c26b81ed1 Mon Sep 17 00:00:00 2001 From: Narayanan Date: Tue, 13 Sep 2011 17:00:22 +0530 Subject: dmaengine: ste_dma40: reset priority bit for logical channels This patch sets the SSCFG/SDCFG bit[7] PRI only for physical channel requests with high priority. For logical channels, this bit will be zero. Signed-off-by: Narayanan G Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 851ad56..d64b72a 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -102,17 +102,18 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg, src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS; dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS; + /* Set the priority bit to high for the physical channel */ + if (cfg->high_priority) { + src |= 1 << D40_SREG_CFG_PRI_POS; + dst |= 1 << D40_SREG_CFG_PRI_POS; + } + } else { /* Logical channel */ dst |= 1 << D40_SREG_CFG_LOG_GIM_POS; src |= 1 << D40_SREG_CFG_LOG_GIM_POS; } - if (cfg->high_priority) { - src |= 1 << D40_SREG_CFG_PRI_POS; - dst |= 1 << D40_SREG_CFG_PRI_POS; - } - if (cfg->src_info.big_endian) src |= 1 << D40_SREG_CFG_LBE_POS; if (cfg->dst_info.big_endian) -- cgit v0.10.2 From 8a5d2039ab9050a8a2e649eaf3ca4e372a7709f1 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Wed, 28 Sep 2011 09:32:20 +0200 Subject: dmaengine: ste_dma40: use writel_relaxed for lcxa lcpa and lcla are written often and the cache_sync() overhead in writel is costly, especially for wlan where every single network packet (in RX mode) corresponds to a separate DMA transfer. Signed-off-by: Per Forlin Reviewed-by: Narayanan Gopalakrishnan Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index d64b72a..1cfe7ab 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -332,10 +332,10 @@ void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa, { d40_log_lli_link(lli_dst, lli_src, next, flags); - writel(lli_src->lcsp02, &lcpa[0].lcsp0); - writel(lli_src->lcsp13, &lcpa[0].lcsp1); - writel(lli_dst->lcsp02, &lcpa[0].lcsp2); - writel(lli_dst->lcsp13, &lcpa[0].lcsp3); + writel_relaxed(lli_src->lcsp02, &lcpa[0].lcsp0); + writel_relaxed(lli_src->lcsp13, &lcpa[0].lcsp1); + writel_relaxed(lli_dst->lcsp02, &lcpa[0].lcsp2); + writel_relaxed(lli_dst->lcsp13, &lcpa[0].lcsp3); } void d40_log_lli_lcla_write(struct d40_log_lli *lcla, @@ -345,10 +345,10 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla, { d40_log_lli_link(lli_dst, lli_src, next, flags); - writel(lli_src->lcsp02, &lcla[0].lcsp02); - writel(lli_src->lcsp13, &lcla[0].lcsp13); - writel(lli_dst->lcsp02, &lcla[1].lcsp02); - writel(lli_dst->lcsp13, &lcla[1].lcsp13); + writel_relaxed(lli_src->lcsp02, &lcla[0].lcsp02); + writel_relaxed(lli_src->lcsp13, &lcla[0].lcsp13); + writel_relaxed(lli_dst->lcsp02, &lcla[1].lcsp02); + writel_relaxed(lli_dst->lcsp13, &lcla[1].lcsp13); } static void d40_log_fill_lli(struct d40_log_lli *lli, -- cgit v0.10.2 From b96710e5b22609aa6e4ba5c3936ea7f026a7c427 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Tue, 18 Oct 2011 18:39:47 +0200 Subject: dmaengine: ste_dma40: set dma max seg size Maximum DMA seg size is (0xffff x data_width). If max seg size is not set it deafults to 64k. This results in failure if transferring 64k in byte mode. Large seg sizes may be supported by splitting large transfer. Signed-off-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23c5573..f5724d9 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -344,6 +344,7 @@ struct d40_base { int irq; int num_phy_chans; int num_log_chans; + struct device_dma_parameters dma_parms; struct dma_device dma_both; struct dma_device dma_slave; struct dma_device dma_memcpy; @@ -3362,6 +3363,13 @@ static int __init d40_probe(struct platform_device *pdev) if (err) goto failure; + base->dev->dma_parms = &base->dma_parms; + err = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE); + if (err) { + d40_err(&pdev->dev, "Failed to set dma max seg size\n"); + goto failure; + } + d40_hw_init(base); dev_info(base->dev, "initialized\n"); -- cgit v0.10.2 From 92bb6cdb5302a4b0b3c6b6cfc0854aaed882c4bc Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Thu, 13 Oct 2011 12:11:36 +0200 Subject: dmaengine: ste_dma40: limit burst size to 16 The client is not aware of the maximum burst size in the dma driver. If the size exceeds 16 set max to 16. Signed-off-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index f5724d9..2d0c63d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2578,6 +2578,14 @@ static int d40_set_runtime_config(struct dma_chan *chan, return -EINVAL; } + if (src_maxburst > 16) { + src_maxburst = 16; + dst_maxburst = src_maxburst * src_addr_width / dst_addr_width; + } else if (dst_maxburst > 16) { + dst_maxburst = 16; + src_maxburst = dst_maxburst * dst_addr_width / src_addr_width; + } + ret = dma40_config_to_halfchannel(d40c, &cfg->src_info, src_addr_width, src_maxburst); -- cgit v0.10.2 From 42365cf0fa19473dde5fe226b0e7e9ab8ea18af8 Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Fri, 20 Jan 2012 13:56:14 +0530 Subject: dmaengine: ste_dma40: don't check for pm_runtime_suspended() The check for runtime suspend is not needed during a regular suspend, as the framework takes care of this. This fixes the issue of DMA driver not letting the system to go to deepsleep in the first attempt. Signed-off-by: Narayanan G Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2d0c63d..760576b 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2782,8 +2782,6 @@ static int dma40_pm_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct d40_base *base = platform_get_drvdata(pdev); int ret = 0; - if (!pm_runtime_suspended(dev)) - return -EBUSY; if (base->lcpa_regulator) ret = regulator_disable(base->lcpa_regulator); -- cgit v0.10.2 From ccc3d6976433aa67131117fccd2b5143d82a6f48 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 17 May 2012 13:47:38 +0530 Subject: dmaengine: ste_dma40: don't allow high priority dest event lines Hardware bug: when a logical channel is triggerred by a high priority destination event line, an extra packet transaction is generated in case of important data write response latency on previous logical channel A and if the source transfer of current logical channel B is already completed and if no other channel with a higher priority than B is waiting for execution. Software workaround: do not set the high priority level for the destination event lines that trigger logical channels. Signed-off-by: Rabin Vincent Reviewed-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 760576b..9f8964a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2180,11 +2180,24 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { bool realtime = d40c->dma_cfg.realtime; bool highprio = d40c->dma_cfg.high_priority; - u32 prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1; u32 event = D40_TYPE_TO_EVENT(dev_type); u32 group = D40_TYPE_TO_GROUP(dev_type); u32 bit = 1 << event; + u32 prioreg; + + /* + * Due to a hardware bug, in some cases a logical channel triggered by + * a high priority destination event line can generate extra packet + * transactions. + * + * The workaround is to not set the high priority level for the + * destination event lines that trigger logical channels. + */ + if (!src && chan_is_logical(d40c)) + highprio = false; + + prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; /* Destination event lines are stored in the upper halfword */ if (!src) -- cgit v0.10.2 From f000df8c5a0e2002acc5989aad99a97d32a24718 Mon Sep 17 00:00:00 2001 From: Gerald Baeza Date: Thu, 8 Nov 2012 14:39:07 +0100 Subject: dmaengine: ste_dma40: support fixed physical channel allocation This patch makes existing use_fixed_channel field (of stedma40_chan_cfg structure) applicable to physical channels. Signed-off-by: Gerald Baeza Tested-by: Yannick Fertre Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 9f8964a..5feab7d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1711,10 +1711,12 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) int i; int j; int log_num; + int num_phy_chans; bool is_src; bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL; phys = d40c->base->phy_res; + num_phy_chans = d40c->base->num_phy_chans; if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) { dev_type = d40c->dma_cfg.src_dev_type; @@ -1735,12 +1737,19 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) if (!is_log) { if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) { /* Find physical half channel */ - for (i = 0; i < d40c->base->num_phy_chans; i++) { - + if (d40c->dma_cfg.use_fixed_channel) { + i = d40c->dma_cfg.phy_channel; if (d40_alloc_mask_set(&phys[i], is_src, 0, is_log, first_phy_user)) goto found_phy; + } else { + for (i = 0; i < num_phy_chans; i++) { + if (d40_alloc_mask_set(&phys[i], is_src, + 0, is_log, + first_phy_user)) + goto found_phy; + } } } else for (j = 0; j < d40c->base->num_phy_chans; j += 8) { -- cgit v0.10.2 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 diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 5feab7d..ca18117 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 9ff93b0..833cb95 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 v0.10.2 From 3cb645dc85a050c8a6b5c2cbdcbe4b8f39dba1b8 Mon Sep 17 00:00:00 2001 From: Tong Liu Date: Wed, 26 Sep 2012 10:07:30 +0000 Subject: dmaengine: ste_dma40: support more than 128 event lines U8540 DMA controller is different from u9540 we need define new registers and use them to support handling more than 128 event lines. Signed-off-by: Tong Liu Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ca18117..67e565b 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -53,6 +53,8 @@ #define D40_ALLOC_PHY (1 << 30) #define D40_ALLOC_LOG_FREE 0 +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) + /** * enum 40_command - The different commands and/or statuses. * @@ -100,8 +102,19 @@ static u32 d40_backup_regs[] = { #define BACKUP_REGS_SZ ARRAY_SIZE(d40_backup_regs) -/* TODO: Check if all these registers have to be saved/restored on dma40 v3 */ -static u32 d40_backup_regs_v3[] = { +/* + * since 9540 and 8540 has the same HW revision + * use v4a for 9540 or ealier + * use v4b for 8540 or later + * HW revision: + * DB8500ed has revision 0 + * DB8500v1 has revision 2 + * DB8500v2 has revision 3 + * AP9540v1 has revision 4 + * DB8540v1 has revision 4 + * TODO: Check if all these registers have to be saved/restored on dma40 v4a + */ +static u32 d40_backup_regs_v4a[] = { D40_DREG_PSEG1, D40_DREG_PSEG2, D40_DREG_PSEG3, @@ -120,7 +133,32 @@ static u32 d40_backup_regs_v3[] = { D40_DREG_RCEG4, }; -#define BACKUP_REGS_SZ_V3 ARRAY_SIZE(d40_backup_regs_v3) +#define BACKUP_REGS_SZ_V4A ARRAY_SIZE(d40_backup_regs_v4a) + +static u32 d40_backup_regs_v4b[] = { + D40_DREG_CPSEG1, + D40_DREG_CPSEG2, + D40_DREG_CPSEG3, + D40_DREG_CPSEG4, + D40_DREG_CPSEG5, + D40_DREG_CPCEG1, + D40_DREG_CPCEG2, + D40_DREG_CPCEG3, + D40_DREG_CPCEG4, + D40_DREG_CPCEG5, + D40_DREG_CRSEG1, + D40_DREG_CRSEG2, + D40_DREG_CRSEG3, + D40_DREG_CRSEG4, + D40_DREG_CRSEG5, + D40_DREG_CRCEG1, + D40_DREG_CRCEG2, + D40_DREG_CRCEG3, + D40_DREG_CRCEG4, + D40_DREG_CRCEG5, +}; + +#define BACKUP_REGS_SZ_V4B ARRAY_SIZE(d40_backup_regs_v4b) static u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SSCFG, @@ -134,6 +172,102 @@ static u32 d40_backup_regs_chan[] = { }; /** + * struct d40_interrupt_lookup - lookup table for interrupt handler + * + * @src: Interrupt mask register. + * @clr: Interrupt clear register. + * @is_error: true if this is an error interrupt. + * @offset: start delta in the lookup_log_chans in d40_base. If equals to + * D40_PHY_CHAN, the lookup_phy_chans shall be used instead. + */ +struct d40_interrupt_lookup { + u32 src; + u32 clr; + bool is_error; + int offset; +}; + + +static struct d40_interrupt_lookup il_v4a[] = { + {D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0}, + {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32}, + {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64}, + {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96}, + {D40_DREG_LCEIS0, D40_DREG_LCICR0, true, 0}, + {D40_DREG_LCEIS1, D40_DREG_LCICR1, true, 32}, + {D40_DREG_LCEIS2, D40_DREG_LCICR2, true, 64}, + {D40_DREG_LCEIS3, D40_DREG_LCICR3, true, 96}, + {D40_DREG_PCTIS, D40_DREG_PCICR, false, D40_PHY_CHAN}, + {D40_DREG_PCEIS, D40_DREG_PCICR, true, D40_PHY_CHAN}, +}; + +static struct d40_interrupt_lookup il_v4b[] = { + {D40_DREG_CLCTIS1, D40_DREG_CLCICR1, false, 0}, + {D40_DREG_CLCTIS2, D40_DREG_CLCICR2, false, 32}, + {D40_DREG_CLCTIS3, D40_DREG_CLCICR3, false, 64}, + {D40_DREG_CLCTIS4, D40_DREG_CLCICR4, false, 96}, + {D40_DREG_CLCTIS5, D40_DREG_CLCICR5, false, 128}, + {D40_DREG_CLCEIS1, D40_DREG_CLCICR1, true, 0}, + {D40_DREG_CLCEIS2, D40_DREG_CLCICR2, true, 32}, + {D40_DREG_CLCEIS3, D40_DREG_CLCICR3, true, 64}, + {D40_DREG_CLCEIS4, D40_DREG_CLCICR4, true, 96}, + {D40_DREG_CLCEIS5, D40_DREG_CLCICR5, true, 128}, + {D40_DREG_CPCTIS, D40_DREG_CPCICR, false, D40_PHY_CHAN}, + {D40_DREG_CPCEIS, D40_DREG_CPCICR, true, D40_PHY_CHAN}, +}; + +/** + * struct d40_reg_val - simple lookup struct + * + * @reg: The register. + * @val: The value that belongs to the register in reg. + */ +struct d40_reg_val { + unsigned int reg; + unsigned int val; +}; + +static __initdata struct d40_reg_val dma_init_reg_v4a[] = { + /* Clock every part of the DMA block from start */ + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, + + /* Interrupts on all logical channels */ + { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF} +}; +static __initdata struct d40_reg_val dma_init_reg_v4b[] = { + /* Clock every part of the DMA block from start */ + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, + + /* Interrupts on all logical channels */ + { .reg = D40_DREG_CLCMIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS5, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR5, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS5, .val = 0xFFFFFFFF} +}; + +/** * struct d40_lli_pool - Structure for keeping LLIs in memory * * @base: Pointer to memory area when the pre_alloc_lli's are not large @@ -289,6 +423,38 @@ struct d40_chan { }; /** + * struct d40_gen_dmac - generic values to represent u8500/u8540 DMA + * controller + * + * @backup: the pointer to the registers address array for backup + * @backup_size: the size of the registers address array for backup + * @realtime_en: the realtime enable register + * @realtime_clear: the realtime clear register + * @high_prio_en: the high priority enable register + * @high_prio_clear: the high priority clear register + * @interrupt_en: the interrupt enable register + * @interrupt_clear: the interrupt clear register + * @il: the pointer to struct d40_interrupt_lookup + * @il_size: the size of d40_interrupt_lookup array + * @init_reg: the pointer to the struct d40_reg_val + * @init_reg_size: the size of d40_reg_val array + */ +struct d40_gen_dmac { + u32 *backup; + u32 backup_size; + u32 realtime_en; + u32 realtime_clear; + u32 high_prio_en; + u32 high_prio_clear; + u32 interrupt_en; + u32 interrupt_clear; + struct d40_interrupt_lookup *il; + u32 il_size; + struct d40_reg_val *init_reg; + u32 init_reg_size; +}; + +/** * struct d40_base - The big global struct, one for each probe'd instance. * * @interrupt_lock: Lock used to make sure one interrupt is handle a time. @@ -326,11 +492,13 @@ struct d40_chan { * @desc_slab: cache for descriptors. * @reg_val_backup: Here the values of some hardware registers are stored * before the DMA is powered off. They are restored when the power is back on. - * @reg_val_backup_v3: Backup of registers that only exits on dma40 v3 and - * later. + * @reg_val_backup_v4: Backup of registers that only exits on dma40 v3 and + * later * @reg_val_backup_chan: Backup data for standard channel parameter registers. * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off. * @initialized: true if the dma has been initialized + * @gen_dmac: the struct for generic registers values to represent u8500/8540 + * DMA controller */ struct d40_base { spinlock_t interrupt_lock; @@ -362,37 +530,11 @@ struct d40_base { resource_size_t lcpa_size; struct kmem_cache *desc_slab; u32 reg_val_backup[BACKUP_REGS_SZ]; - u32 reg_val_backup_v3[BACKUP_REGS_SZ_V3]; + u32 reg_val_backup_v4[MAX(BACKUP_REGS_SZ_V4A, BACKUP_REGS_SZ_V4B)]; u32 *reg_val_backup_chan; u16 gcc_pwr_off_mask; bool initialized; -}; - -/** - * struct d40_interrupt_lookup - lookup table for interrupt handler - * - * @src: Interrupt mask register. - * @clr: Interrupt clear register. - * @is_error: true if this is an error interrupt. - * @offset: start delta in the lookup_log_chans in d40_base. If equals to - * D40_PHY_CHAN, the lookup_phy_chans shall be used instead. - */ -struct d40_interrupt_lookup { - u32 src; - u32 clr; - bool is_error; - int offset; -}; - -/** - * struct d40_reg_val - simple lookup struct - * - * @reg: The register. - * @val: The value that belongs to the register in reg. - */ -struct d40_reg_val { - unsigned int reg; - unsigned int val; + struct d40_gen_dmac gen_dmac; }; static struct device *chan2dev(struct d40_chan *d40c) @@ -875,11 +1017,11 @@ static void d40_save_restore_registers(struct d40_base *base, bool save) save); /* Save/Restore registers only existing on dma40 v3 and later */ - if (base->rev >= 3) - dma40_backup(base->virtbase, base->reg_val_backup_v3, - d40_backup_regs_v3, - ARRAY_SIZE(d40_backup_regs_v3), - save); + if (base->gen_dmac.backup) + dma40_backup(base->virtbase, base->reg_val_backup_v4, + base->gen_dmac.backup, + base->gen_dmac.backup_size, + save); } #else static void d40_save_restore_registers(struct d40_base *base, bool save) @@ -1470,41 +1612,30 @@ err: static irqreturn_t d40_handle_interrupt(int irq, void *data) { - static const struct d40_interrupt_lookup il[] = { - {D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0}, - {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32}, - {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64}, - {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96}, - {D40_DREG_LCEIS0, D40_DREG_LCICR0, true, 0}, - {D40_DREG_LCEIS1, D40_DREG_LCICR1, true, 32}, - {D40_DREG_LCEIS2, D40_DREG_LCICR2, true, 64}, - {D40_DREG_LCEIS3, D40_DREG_LCICR3, true, 96}, - {D40_DREG_PCTIS, D40_DREG_PCICR, false, D40_PHY_CHAN}, - {D40_DREG_PCEIS, D40_DREG_PCICR, true, D40_PHY_CHAN}, - }; - int i; - u32 regs[ARRAY_SIZE(il)]; u32 idx; u32 row; long chan = -1; struct d40_chan *d40c; unsigned long flags; struct d40_base *base = data; + u32 regs[base->gen_dmac.il_size]; + struct d40_interrupt_lookup *il = base->gen_dmac.il; + u32 il_size = base->gen_dmac.il_size; spin_lock_irqsave(&base->interrupt_lock, flags); /* Read interrupt status of both logical and physical channels */ - for (i = 0; i < ARRAY_SIZE(il); i++) + for (i = 0; i < il_size; i++) regs[i] = readl(base->virtbase + il[i].src); for (;;) { chan = find_next_bit((unsigned long *)regs, - BITS_PER_LONG * ARRAY_SIZE(il), chan + 1); + BITS_PER_LONG * il_size, chan + 1); /* No more set bits found? */ - if (chan == BITS_PER_LONG * ARRAY_SIZE(il)) + if (chan == BITS_PER_LONG * il_size) break; row = chan / BITS_PER_LONG; @@ -2189,12 +2320,14 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { bool realtime = d40c->dma_cfg.realtime; bool highprio = d40c->dma_cfg.high_priority; - u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1; + u32 rtreg; u32 event = D40_TYPE_TO_EVENT(dev_type); u32 group = D40_TYPE_TO_GROUP(dev_type); u32 bit = 1 << event; u32 prioreg; + struct d40_gen_dmac *dmac = &d40c->base->gen_dmac; + rtreg = realtime ? dmac->realtime_en : dmac->realtime_clear; /* * Due to a hardware bug, in some cases a logical channel triggered by * a high priority destination event line can generate extra packet @@ -2206,7 +2339,7 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) if (!src && chan_is_logical(d40c)) highprio = false; - prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; + prioreg = highprio ? dmac->high_prio_en : dmac->high_prio_clear; /* Destination event lines are stored in the upper halfword */ if (!src) @@ -3056,6 +3189,36 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4); base->log_chans = &base->phy_chans[num_phy_chans]; + if (base->plat_data->num_of_phy_chans == 14) { + base->gen_dmac.backup = d40_backup_regs_v4b; + base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4B; + base->gen_dmac.interrupt_en = D40_DREG_CPCMIS; + base->gen_dmac.interrupt_clear = D40_DREG_CPCICR; + base->gen_dmac.realtime_en = D40_DREG_CRSEG1; + base->gen_dmac.realtime_clear = D40_DREG_CRCEG1; + base->gen_dmac.high_prio_en = D40_DREG_CPSEG1; + base->gen_dmac.high_prio_clear = D40_DREG_CPCEG1; + base->gen_dmac.il = il_v4b; + base->gen_dmac.il_size = ARRAY_SIZE(il_v4b); + base->gen_dmac.init_reg = dma_init_reg_v4b; + base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4b); + } else { + if (base->rev >= 3) { + base->gen_dmac.backup = d40_backup_regs_v4a; + base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4A; + } + base->gen_dmac.interrupt_en = D40_DREG_PCMIS; + base->gen_dmac.interrupt_clear = D40_DREG_PCICR; + base->gen_dmac.realtime_en = D40_DREG_RSEG1; + base->gen_dmac.realtime_clear = D40_DREG_RCEG1; + base->gen_dmac.high_prio_en = D40_DREG_PSEG1; + base->gen_dmac.high_prio_clear = D40_DREG_PCEG1; + base->gen_dmac.il = il_v4a; + base->gen_dmac.il_size = ARRAY_SIZE(il_v4a); + base->gen_dmac.init_reg = dma_init_reg_v4a; + base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4a); + } + base->phy_res = kzalloc(num_phy_chans * sizeof(struct d40_phy_res), GFP_KERNEL); if (!base->phy_res) @@ -3127,31 +3290,15 @@ failure: static void __init d40_hw_init(struct d40_base *base) { - static struct d40_reg_val dma_init_reg[] = { - /* Clock every part of the DMA block from start */ - { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, - - /* Interrupts on all logical channels */ - { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF} - }; int i; u32 prmseo[2] = {0, 0}; u32 activeo[2] = {0xFFFFFFFF, 0xFFFFFFFF}; u32 pcmis = 0; u32 pcicr = 0; + struct d40_reg_val *dma_init_reg = base->gen_dmac.init_reg; + u32 reg_size = base->gen_dmac.init_reg_size; - for (i = 0; i < ARRAY_SIZE(dma_init_reg); i++) + for (i = 0; i < reg_size; i++) writel(dma_init_reg[i].val, base->virtbase + dma_init_reg[i].reg); @@ -3184,11 +3331,14 @@ static void __init d40_hw_init(struct d40_base *base) writel(activeo[0], base->virtbase + D40_DREG_ACTIVO); /* Write which interrupt to enable */ - writel(pcmis, base->virtbase + D40_DREG_PCMIS); + writel(pcmis, base->virtbase + base->gen_dmac.interrupt_en); /* Write which interrupt to clear */ - writel(pcicr, base->virtbase + D40_DREG_PCICR); + writel(pcicr, base->virtbase + base->gen_dmac.interrupt_clear); + /* These are __initdata and cannot be accessed after init */ + base->gen_dmac.init_reg = NULL; + base->gen_dmac.init_reg_size = 0; } static int __init d40_lcla_allocate(struct d40_base *base) diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index 6d47373..fdde8ef 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h @@ -125,7 +125,7 @@ #define D40_DREG_GCC 0x000 #define D40_DREG_GCC_ENA 0x1 /* This assumes that there are only 4 event groups */ -#define D40_DREG_GCC_ENABLE_ALL 0xff01 +#define D40_DREG_GCC_ENABLE_ALL 0x3ff01 #define D40_DREG_GCC_EVTGRP_POS 8 #define D40_DREG_GCC_SRC 0 #define D40_DREG_GCC_DST 1 @@ -148,14 +148,31 @@ #define D40_DREG_LCPA 0x020 #define D40_DREG_LCLA 0x024 + +#define D40_DREG_SSEG1 0x030 +#define D40_DREG_SSEG2 0x034 +#define D40_DREG_SSEG3 0x038 +#define D40_DREG_SSEG4 0x03C + +#define D40_DREG_SCEG1 0x040 +#define D40_DREG_SCEG2 0x044 +#define D40_DREG_SCEG3 0x048 +#define D40_DREG_SCEG4 0x04C + #define D40_DREG_ACTIVE 0x050 #define D40_DREG_ACTIVO 0x054 -#define D40_DREG_FSEB1 0x058 -#define D40_DREG_FSEB2 0x05C +#define D40_DREG_CIDMOD 0x058 +#define D40_DREG_TCIDV 0x05C #define D40_DREG_PCMIS 0x060 #define D40_DREG_PCICR 0x064 #define D40_DREG_PCTIS 0x068 #define D40_DREG_PCEIS 0x06C + +#define D40_DREG_SPCMIS 0x070 +#define D40_DREG_SPCICR 0x074 +#define D40_DREG_SPCTIS 0x078 +#define D40_DREG_SPCEIS 0x07C + #define D40_DREG_LCMIS0 0x080 #define D40_DREG_LCMIS1 0x084 #define D40_DREG_LCMIS2 0x088 @@ -172,6 +189,33 @@ #define D40_DREG_LCEIS1 0x0B4 #define D40_DREG_LCEIS2 0x0B8 #define D40_DREG_LCEIS3 0x0BC + +#define D40_DREG_SLCMIS1 0x0C0 +#define D40_DREG_SLCMIS2 0x0C4 +#define D40_DREG_SLCMIS3 0x0C8 +#define D40_DREG_SLCMIS4 0x0CC + +#define D40_DREG_SLCICR1 0x0D0 +#define D40_DREG_SLCICR2 0x0D4 +#define D40_DREG_SLCICR3 0x0D8 +#define D40_DREG_SLCICR4 0x0DC + +#define D40_DREG_SLCTIS1 0x0E0 +#define D40_DREG_SLCTIS2 0x0E4 +#define D40_DREG_SLCTIS3 0x0E8 +#define D40_DREG_SLCTIS4 0x0EC + +#define D40_DREG_SLCEIS1 0x0F0 +#define D40_DREG_SLCEIS2 0x0F4 +#define D40_DREG_SLCEIS3 0x0F8 +#define D40_DREG_SLCEIS4 0x0FC + +#define D40_DREG_FSESS1 0x100 +#define D40_DREG_FSESS2 0x104 + +#define D40_DREG_FSEBS1 0x108 +#define D40_DREG_FSEBS2 0x10C + #define D40_DREG_PSEG1 0x110 #define D40_DREG_PSEG2 0x114 #define D40_DREG_PSEG3 0x118 @@ -188,6 +232,86 @@ #define D40_DREG_RCEG2 0x144 #define D40_DREG_RCEG3 0x148 #define D40_DREG_RCEG4 0x14C + +#define D40_DREG_PREFOT 0x15C +#define D40_DREG_EXTCFG 0x160 + +#define D40_DREG_CPSEG1 0x200 +#define D40_DREG_CPSEG2 0x204 +#define D40_DREG_CPSEG3 0x208 +#define D40_DREG_CPSEG4 0x20C +#define D40_DREG_CPSEG5 0x210 + +#define D40_DREG_CPCEG1 0x220 +#define D40_DREG_CPCEG2 0x224 +#define D40_DREG_CPCEG3 0x228 +#define D40_DREG_CPCEG4 0x22C +#define D40_DREG_CPCEG5 0x230 + +#define D40_DREG_CRSEG1 0x240 +#define D40_DREG_CRSEG2 0x244 +#define D40_DREG_CRSEG3 0x248 +#define D40_DREG_CRSEG4 0x24C +#define D40_DREG_CRSEG5 0x250 + +#define D40_DREG_CRCEG1 0x260 +#define D40_DREG_CRCEG2 0x264 +#define D40_DREG_CRCEG3 0x268 +#define D40_DREG_CRCEG4 0x26C +#define D40_DREG_CRCEG5 0x270 + +#define D40_DREG_CFSESS1 0x280 +#define D40_DREG_CFSESS2 0x284 +#define D40_DREG_CFSESS3 0x288 + +#define D40_DREG_CFSEBS1 0x290 +#define D40_DREG_CFSEBS2 0x294 +#define D40_DREG_CFSEBS3 0x298 + +#define D40_DREG_CLCMIS1 0x300 +#define D40_DREG_CLCMIS2 0x304 +#define D40_DREG_CLCMIS3 0x308 +#define D40_DREG_CLCMIS4 0x30C +#define D40_DREG_CLCMIS5 0x310 + +#define D40_DREG_CLCICR1 0x320 +#define D40_DREG_CLCICR2 0x324 +#define D40_DREG_CLCICR3 0x328 +#define D40_DREG_CLCICR4 0x32C +#define D40_DREG_CLCICR5 0x330 + +#define D40_DREG_CLCTIS1 0x340 +#define D40_DREG_CLCTIS2 0x344 +#define D40_DREG_CLCTIS3 0x348 +#define D40_DREG_CLCTIS4 0x34C +#define D40_DREG_CLCTIS5 0x350 + +#define D40_DREG_CLCEIS1 0x360 +#define D40_DREG_CLCEIS2 0x364 +#define D40_DREG_CLCEIS3 0x368 +#define D40_DREG_CLCEIS4 0x36C +#define D40_DREG_CLCEIS5 0x370 + +#define D40_DREG_CPCMIS 0x380 +#define D40_DREG_CPCICR 0x384 +#define D40_DREG_CPCTIS 0x388 +#define D40_DREG_CPCEIS 0x38C + +#define D40_DREG_SCCIDA1 0xE80 +#define D40_DREG_SCCIDA2 0xE90 +#define D40_DREG_SCCIDA3 0xEA0 +#define D40_DREG_SCCIDA4 0xEB0 +#define D40_DREG_SCCIDA5 0xEC0 + +#define D40_DREG_SCCIDB1 0xE84 +#define D40_DREG_SCCIDB2 0xE94 +#define D40_DREG_SCCIDB3 0xEA4 +#define D40_DREG_SCCIDB4 0xEB4 +#define D40_DREG_SCCIDB5 0xEC4 + +#define D40_DREG_PRSCCIDA 0xF80 +#define D40_DREG_PRSCCIDB 0xF84 + #define D40_DREG_STFU 0xFC8 #define D40_DREG_ICFG 0xFCC #define D40_DREG_PERIPHID0 0xFE0 -- cgit v0.10.2 From 4226dd86b10ac44f8e98599f6a73e3a1b929f8eb Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 13:46:16 +0100 Subject: dmaengine: ste_dma40: add a done queue for completed descriptors This is to keep the active queue for only those transfers which are actually active in the hardware. Descriptors will be moved to the done queue after they are completed in the hardware (interrupt handler) but before all the cleanup work has been completed (tasklet). Mostly based on a previous patch by Rabin Vincent. Cc: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 67e565b..ff21dcb 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -382,6 +382,7 @@ struct d40_base; * @client: Cliented owned descriptor list. * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. + * @done: Completed jobs * @queue: Queued jobs. * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. @@ -407,6 +408,7 @@ struct d40_chan { struct list_head client; struct list_head pending_queue; struct list_head active; + struct list_head done; struct list_head queue; struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; @@ -754,6 +756,11 @@ static void d40_phy_lli_load(struct d40_chan *chan, struct d40_desc *desc) writel(lli_dst->reg_lnk, base + D40_CHAN_REG_SDLNK); } +static void d40_desc_done(struct d40_chan *d40c, struct d40_desc *desc) +{ + list_add_tail(&desc->node, &d40c->done); +} + static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) { struct d40_lcla_pool *pool = &chan->base->lcla_pool; @@ -914,6 +921,14 @@ static struct d40_desc *d40_first_queued(struct d40_chan *d40c) return d; } +static struct d40_desc *d40_first_done(struct d40_chan *d40c) +{ + if (list_empty(&d40c->done)) + return NULL; + + return list_first_entry(&d40c->done, struct d40_desc, node); +} + static int d40_psize_2_burst_size(bool is_log, int psize) { if (is_log) { @@ -1104,6 +1119,12 @@ static void d40_term_all(struct d40_chan *d40c) struct d40_desc *d40d; struct d40_desc *_d; + /* Release completed descriptors */ + while ((d40d = d40_first_done(d40c))) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } + /* Release active descriptors */ while ((d40d = d40_first_active_get(d40c))) { d40_desc_remove(d40d); @@ -1541,6 +1562,9 @@ static void dma_tc_handle(struct d40_chan *d40c) pm_runtime_put_autosuspend(d40c->base->dev); } + d40_desc_remove(d40d); + d40_desc_done(d40c, d40d); + d40c->pending_tx++; tasklet_schedule(&d40c->tasklet); @@ -1556,10 +1580,14 @@ static void dma_tasklet(unsigned long data) spin_lock_irqsave(&d40c->lock, flags); - /* Get first active entry from list */ - d40d = d40_first_active_get(d40c); - if (d40d == NULL) - goto err; + /* Get first entry from the done list */ + d40d = d40_first_done(d40c); + if (d40d == NULL) { + /* Check if we have reached here for cyclic job */ + d40d = d40_first_active_get(d40c); + if (d40d == NULL || !d40d->cyclic) + goto err; + } if (!d40d->cyclic) dma_cookie_complete(&d40d->txd); @@ -2823,6 +2851,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, d40c->log_num = D40_PHY_CHAN; + INIT_LIST_HEAD(&d40c->done); INIT_LIST_HEAD(&d40c->active); INIT_LIST_HEAD(&d40c->queue); INIT_LIST_HEAD(&d40c->pending_queue); -- cgit v0.10.2 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 diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 833cb95..b99024b 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 v0.10.2 From f26e03ad2b50be50c98f8ecb1fd9dbdf94db91ab Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 17:12:37 +0100 Subject: dmaengine: ste_dma40: minor cosmetic fixes This patch contains various non functional cosmetic fixes. Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ff21dcb..623779e 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1609,13 +1609,11 @@ static void dma_tasklet(unsigned long data) if (async_tx_test_ack(&d40d->txd)) { d40_desc_remove(d40d); d40_desc_free(d40c, d40d); - } else { - if (!d40d->is_in_client_list) { - d40_desc_remove(d40d); - d40_lcla_free_all(d40c, d40d); - list_add_tail(&d40d->node, &d40c->client); - d40d->is_in_client_list = true; - } + } else if (!d40d->is_in_client_list) { + d40_desc_remove(d40d); + d40_lcla_free_all(d40c, d40d); + list_add_tail(&d40d->node, &d40c->client); + d40d->is_in_client_list = true; } } @@ -2123,7 +2121,6 @@ _exit: } - static u32 stedma40_residue(struct dma_chan *chan) { struct d40_chan *d40c = @@ -2199,7 +2196,6 @@ d40_prep_sg_phy(struct d40_chan *chan, struct d40_desc *desc, return ret < 0 ? ret : 0; } - static struct d40_desc * d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, unsigned int sg_len, unsigned long dma_flags) @@ -2225,7 +2221,6 @@ d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, goto err; } - desc->lli_current = 0; desc->txd.flags = dma_flags; desc->txd.tx_submit = d40_tx_submit; @@ -2274,7 +2269,6 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, return NULL; } - spin_lock_irqsave(&chan->lock, flags); desc = d40_prep_desc(chan, sg_src, sg_len, dma_flags); @@ -2432,11 +2426,11 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; + d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; else d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.dst_dev_type * - D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; + d40c->dma_cfg.dst_dev_type * + D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; } dev_dbg(chan2dev(d40c), "allocated %s channel (phy %d%s)\n", @@ -2471,7 +2465,6 @@ static void d40_free_chan_resources(struct dma_chan *chan) return; } - spin_lock_irqsave(&d40c->lock, flags); err = d40_free_dma(d40c); @@ -2514,12 +2507,10 @@ d40_prep_memcpy_sg(struct dma_chan *chan, return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags); } -static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, - struct scatterlist *sgl, - unsigned int sg_len, - enum dma_transfer_direction direction, - unsigned long dma_flags, - void *context) +static struct dma_async_tx_descriptor * +d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long dma_flags, void *context) { if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) return NULL; diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 1cfe7ab..7180e0d 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -251,7 +251,7 @@ d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size, return lli; - err: +err: return NULL; } -- cgit v0.10.2 From 7ce529efbcf6fdcb1854e4634adf7f6a18216e81 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 18 Dec 2012 16:59:09 +0100 Subject: dmaengine: ste_dma40: minor code readability fixes Use internal variables to the cycles to improve code readability, no functional changes. Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 623779e..e317deb 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -639,19 +639,18 @@ static int d40_lcla_alloc_one(struct d40_chan *d40c, unsigned long flags; int i; int ret = -EINVAL; - int p; spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags); - p = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP; - /* * Allocate both src and dst at the same time, therefore the half * start on 1 since 0 can't be used since zero is used as end marker. */ for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { - if (!d40c->base->lcla_pool.alloc_map[p + i]) { - d40c->base->lcla_pool.alloc_map[p + i] = d40d; + int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i; + + if (!d40c->base->lcla_pool.alloc_map[idx]) { + d40c->base->lcla_pool.alloc_map[idx] = d40d; d40d->lcla_alloc++; ret = i; break; @@ -676,10 +675,10 @@ static int d40_lcla_free_all(struct d40_chan *d40c, spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags); for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { - if (d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num * - D40_LCLA_LINK_PER_EVENT_GRP + i] == d40d) { - d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num * - D40_LCLA_LINK_PER_EVENT_GRP + i] = NULL; + int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i; + + if (d40c->base->lcla_pool.alloc_map[idx] == d40d) { + d40c->base->lcla_pool.alloc_map[idx] = NULL; d40d->lcla_alloc--; if (d40d->lcla_alloc == 0) { ret = 0; -- cgit v0.10.2 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 diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index e317deb..2ecefb7 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 b99024b..4b78101 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 v0.10.2 From 53d6d68f3c1792bce0144d8499435468e425a995 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 19 Dec 2012 14:41:56 +0100 Subject: dmaengine: set_dma40: ignore spurious interrupts Some DMA channels may be used by other cores in the SoC. This patch modifies the dma interrupt handler to ignore interrupts from unknown channels. Cc: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2ecefb7..a97bbd3 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1677,13 +1677,22 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data) row = chan / BITS_PER_LONG; idx = chan & (BITS_PER_LONG - 1); - /* ACK interrupt */ - writel(1 << idx, base->virtbase + il[row].clr); - if (il[row].offset == D40_PHY_CHAN) d40c = base->lookup_phy_chans[idx]; else d40c = base->lookup_log_chans[il[row].offset + idx]; + + if (!d40c) { + /* + * No error because this can happen if something else + * in the system is using the channel. + */ + continue; + } + + /* ACK interrupt */ + writel(1 << idx, base->virtbase + il[row].clr); + spin_lock(&d40c->lock); if (!il[row].is_error) -- cgit v0.10.2 From da2ac56a1bc9c6c56244aa9ca990d5c5c7574b5f Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Mon, 7 Jan 2013 10:58:35 +0100 Subject: dmaengine: set_dma40: balance clock in probe fail code Clock code was changed to use clk_prepare_enable in: b707c65 dma/ste_dma40: Fixup clock usage during probe but clk_disable on probe fail path was not updated. This patch fix this by using clk_disable_unprepare in place of clk_disable. Acked-by: Ulf Hansson Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index a97bbd3..d77d41d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3634,7 +3634,7 @@ failure: release_mem_region(base->phy_start, base->phy_size); if (base->clk) { - clk_disable(base->clk); + clk_disable_unprepare(base->clk); clk_put(base->clk); } -- cgit v0.10.2 From 661f7cb55c61fa7491e0caf21e55f59e5bc49abe Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 10 Jan 2013 13:41:04 -0500 Subject: dma: edma: fix slave config dependency on direction The edma_slave_config() implementation depends on the direction field such that it will not properly configure a slave channel when called without direction set. This fixes the implementation so that the slave config is copied as is and prep_slave_sg() handles the direction dependent handling. spi-omap2-mcspi and omap_hsmmc both expose this bug as they configure the slave channel config from a common path with an unconfigured direction field. Signed-off-by: Matt Porter Signed-off-by: Vinod Koul diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 232b458..82c8672 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -69,9 +69,7 @@ struct edma_chan { int ch_num; bool alloced; int slot[EDMA_MAX_SLOTS]; - dma_addr_t addr; - int addr_width; - int maxburst; + struct dma_slave_config cfg; }; struct edma_cc { @@ -178,29 +176,14 @@ static int edma_terminate_all(struct edma_chan *echan) return 0; } - static int edma_slave_config(struct edma_chan *echan, - struct dma_slave_config *config) + struct dma_slave_config *cfg) { - if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) || - (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; - if (config->direction == DMA_MEM_TO_DEV) { - if (config->dst_addr) - echan->addr = config->dst_addr; - if (config->dst_addr_width) - echan->addr_width = config->dst_addr_width; - if (config->dst_maxburst) - echan->maxburst = config->dst_maxburst; - } else if (config->direction == DMA_DEV_TO_MEM) { - if (config->src_addr) - echan->addr = config->src_addr; - if (config->src_addr_width) - echan->addr_width = config->src_addr_width; - if (config->src_maxburst) - echan->maxburst = config->src_maxburst; - } + memcpy(&echan->cfg, cfg, sizeof(echan->cfg)); return 0; } @@ -235,6 +218,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( struct edma_chan *echan = to_edma_chan(chan); struct device *dev = chan->device->dev; struct edma_desc *edesc; + dma_addr_t dev_addr; + enum dma_slave_buswidth dev_width; + u32 burst; struct scatterlist *sg; int i; int acnt, bcnt, ccnt, src, dst, cidx; @@ -243,7 +229,20 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (unlikely(!echan || !sgl || !sg_len)) return NULL; - if (echan->addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { + if (direction == DMA_DEV_TO_MEM) { + dev_addr = echan->cfg.src_addr; + dev_width = echan->cfg.src_addr_width; + burst = echan->cfg.src_maxburst; + } else if (direction == DMA_MEM_TO_DEV) { + dev_addr = echan->cfg.dst_addr; + dev_width = echan->cfg.dst_addr_width; + burst = echan->cfg.dst_maxburst; + } else { + dev_err(dev, "%s: bad direction?\n", __func__); + return NULL; + } + + if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { dev_err(dev, "Undefined slave buswidth\n"); return NULL; } @@ -275,14 +274,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } } - acnt = echan->addr_width; + acnt = dev_width; /* * If the maxburst is equal to the fifo width, use * A-synced transfers. This allows for large contiguous * buffer transfers using only one PaRAM set. */ - if (echan->maxburst == 1) { + if (burst == 1) { edesc->absync = false; ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1); bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1); @@ -302,7 +301,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( */ } else { edesc->absync = true; - bcnt = echan->maxburst; + bcnt = burst; ccnt = sg_dma_len(sg) / (acnt * bcnt); if (ccnt > (SZ_64K - 1)) { dev_err(dev, "Exceeded max SG segment size\n"); @@ -313,13 +312,13 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (direction == DMA_MEM_TO_DEV) { src = sg_dma_address(sg); - dst = echan->addr; + dst = dev_addr; src_bidx = acnt; src_cidx = cidx; dst_bidx = 0; dst_cidx = 0; } else { - src = echan->addr; + src = dev_addr; dst = sg_dma_address(sg); src_bidx = 0; src_cidx = 0; -- cgit v0.10.2 From ed30933e6f3dbeaaab1de91e1bec25f42d5d32df Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 01:19:48 +0100 Subject: dma: remove unnecessary null pointer check in mmp_pdma.c the pointer cfg is dereferenced in line 594, so it's no reason to check null again in line 620. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 13bdf4a..41ad6a6 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -617,10 +617,8 @@ static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, else if (maxburst == 32) chan->dcmd |= DCMD_BURST32; - if (cfg) { - chan->dir = cfg->direction; - chan->drcmr = cfg->slave_id; - } + chan->dir = cfg->direction; + chan->drcmr = cfg->slave_id; chan->dev_addr = addr; break; default: -- cgit v0.10.2 From 855372c013bbad8369223f7c75242bd3c94f9345 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 01:23:48 +0100 Subject: dma: sh/shdma-base.c: remove unnecessary null pointer check the variable chan is dereferenced in line 635, so it is no reason to check null again in line 641. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index f4cd946..4acb85a 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -638,9 +638,6 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long flags; int ret; - if (!chan) - return -EINVAL; - switch (cmd) { case DMA_TERMINATE_ALL: spin_lock_irqsave(&schan->chan_lock, flags); -- cgit v0.10.2 From f8122a82d2eae8ef42de48829deed0ca9d9e1f17 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Jan 2013 15:48:50 +0200 Subject: dw_dmac: allocate dma descriptors from DMA_COHERENT memory Currently descriptors are allocated from normal cacheable memory and that slows down filling the descriptors, as we need to call cache_coherency routines afterwards. It would be better to allocate memory for these descriptors from DMA_COHERENT memory. This would make code much cleaner too. Signed-off-by: Andy Shevchenko Tested-by: Mika Westerberg Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 28d5f01..76301c4 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -91,14 +92,6 @@ static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) /*----------------------------------------------------------------------*/ -/* - * Because we're not relying on writeback from the controller (it may not - * even be configured into the core!) we don't need to use dma_pool. These - * descriptors -- and associated data -- are cacheable. We do need to make - * sure their dcache entries are written back before handing them off to - * the controller, though. - */ - static struct device *chan2dev(struct dma_chan *chan) { return &chan->dev->device; @@ -137,19 +130,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) return ret; } -static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - struct dw_desc *child; - - list_for_each_entry(child, &desc->tx_list, desc_node) - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - child->txd.phys, sizeof(child->lli), - DMA_TO_DEVICE); - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - desc->txd.phys, sizeof(desc->lli), - DMA_TO_DEVICE); -} - /* * Move a descriptor, including any children, to the free list. * `desc' must not be on any lists. @@ -161,8 +141,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) if (desc) { struct dw_desc *child; - dwc_sync_desc_for_cpu(dwc, desc); - spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dwc->chan), @@ -335,8 +313,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, param = txd->callback_param; } - dwc_sync_desc_for_cpu(dwc, desc); - /* async_tx_ack */ list_for_each_entry(child, &desc->tx_list, desc_node) async_tx_ack(&child->txd); @@ -770,25 +746,17 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } - if (flags & DMA_PREP_INTERRUPT) /* Trigger interrupt after last block */ prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->txd.flags = flags; first->len = len; @@ -876,10 +844,6 @@ slave_sg_todev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -938,10 +902,6 @@ slave_sg_fromdev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -961,10 +921,6 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->len = total_len; return &first->txd; @@ -1118,7 +1074,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; - int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1139,21 +1094,21 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); i = dwc->descs_allocated; while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + spin_unlock_irqrestore(&dwc->lock, flags); - desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); if (!desc) goto err_desc_alloc; + memset(desc, 0, sizeof(struct dw_desc)); + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, - sizeof(desc->lli), DMA_TO_DEVICE); - ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); - if (ret) - goto err_desc_alloc; + desc->txd.phys = phys; dwc_desc_put(dwc, desc); @@ -1168,8 +1123,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) return i; err_desc_alloc: - kfree(desc); - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); return i; @@ -1204,9 +1157,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) list_for_each_entry_safe(desc, _desc, &list, desc_node) { dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_unmap_single(chan2parent(chan), desc->txd.phys, - sizeof(desc->lli), DMA_TO_DEVICE); - kfree(desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); } dev_vdbg(chan2dev(chan), "%s: done\n", __func__); @@ -1451,20 +1402,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, desc->lli.ctlhi = (period_len >> reg_width); cdesc->desc[i] = desc; - if (last) { + if (last) last->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - last->txd.phys, sizeof(last->lli), - DMA_TO_DEVICE); - } last = desc; } /* lets make a cyclic list */ last->lli.llp = cdesc->desc[0]->txd.phys; - dma_sync_single_for_device(chan2parent(chan), last->txd.phys, - sizeof(last->lli), DMA_TO_DEVICE); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " "period %zu periods %d\n", (unsigned long long)buf_addr, @@ -1722,6 +1667,14 @@ static int dw_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw); + /* create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); INIT_LIST_HEAD(&dw->dma.channels); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 577f2dd..fef296d 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -235,6 +235,7 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) struct dw_dma { struct dma_device dma; void __iomem *regs; + struct dma_pool *desc_pool; struct tasklet_struct tasklet; struct clk *clk; -- cgit v0.10.2 From 5be10f349bc0a2f3dd2ab6417ffe29746403984c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 17 Jan 2013 10:03:01 +0200 Subject: dw_dmac: don't exceed AHB master number in dwc_get_data_width The driver assumes that hardware has two AHB masters which might not be always true. In such cases we must not exceed number of the AHB masters present in the hardware. In the proposed scheme in this patch, we would choose the master with highest possible number whenever we exceed max AHB masters. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 76301c4..635a4a5 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -47,13 +47,29 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) return slave ? slave->src_master : 1; } +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_master(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + unsigned int m; + + if (master == SRC_MASTER) + m = dwc_get_sms(dws); + else + m = dwc_get_dms(dws); + + return min_t(unsigned int, dw->nr_masters - 1, m); +} + #define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ bool _is_slave = is_slave_direction(_dwc->direction); \ - int _dms = dwc_get_dms(__slave); \ - int _sms = dwc_get_sms(__slave); \ + int _dms = dwc_get_master(_chan, DST_MASTER); \ + int _sms = dwc_get_master(_chan, SRC_MASTER); \ u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ @@ -74,20 +90,11 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 -#define SRC_MASTER 0 -#define DST_MASTER 1 - static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) { struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_dma_slave *dws = chan->private; - - if (master == SRC_MASTER) - return dw->data_width[dwc_get_sms(dws)]; - else if (master == DST_MASTER) - return dw->data_width[dwc_get_dms(dws)]; - return 0; + return dw->data_width[dwc_get_master(chan, master)]; } /*----------------------------------------------------------------------*/ -- cgit v0.10.2 From 77bcc497c60ec62dbb84abc809a6e218d53409e9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 18 Jan 2013 14:14:15 +0200 Subject: dw_dmac: move soft LLP code from tasklet to dwc_scan_descriptors The proper place for the main logic of the soft LLP mode is dwc_scan_descriptors. It prevents to get the transfer unexpectedly aborted in case the user calls dwc_tx_status. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 635a4a5..dc3b955 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -400,6 +400,20 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) if (status_xfer & dwc->mask) { /* Everything we've submitted is done */ dma_writel(dw, CLEAR.XFER, dwc->mask); + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + if (dwc->tx_node_active != dwc->tx_list) { + desc = to_dw_desc(dwc->tx_node_active); + + /* Submit next block */ + dwc_do_single_block(dwc, desc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return; + } + /* We are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + } spin_unlock_irqrestore(&dwc->lock, flags); dwc_complete_all(dw, dwc); @@ -411,6 +425,12 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) return; } + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, (unsigned long long)llp); @@ -596,29 +616,8 @@ static void dw_dma_tasklet(unsigned long data) dwc_handle_cyclic(dw, dwc, status_err, status_xfer); else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); - else if (status_xfer & (1 << i)) { - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - if (dwc->tx_node_active != dwc->tx_list) { - struct dw_desc *desc = - to_dw_desc(dwc->tx_node_active); - - dma_writel(dw, CLEAR.XFER, dwc->mask); - - dwc_do_single_block(dwc, desc); - - spin_unlock_irqrestore(&dwc->lock, flags); - continue; - } - /* we are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - } - spin_unlock_irqrestore(&dwc->lock, flags); - + else if (status_xfer & (1 << i)) dwc_scan_descriptors(dw, dwc); - } } /* -- cgit v0.10.2 From 2518d1d1fc0ba7ef781bac97132bcfd6d7466c4b Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 14 Dec 2012 10:59:22 +0000 Subject: DMAEngine: sirf: add DMA pause/resume support pause/resume are important for users like ALSA sound drivers, this patches make the sirf prima2/marco support DMA commands DMA_PAUSE and DMA_RESUME. Signed-off-by: Barry Song Cc: Russell King Signed-off-by: Vinod Koul diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 3c210ba..7d78cf7 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -313,6 +313,48 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) return 0; } +static int sirfsoc_dma_pause_chan(struct sirfsoc_dma_chan *schan) +{ + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); + int cid = schan->chan.chan_id; + unsigned long flags; + + spin_lock_irqsave(&schan->lock, flags); + + if (!sdma->is_marco) + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) + & ~((1 << cid) | 1 << (cid + 16)), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + else + writel_relaxed((1 << cid) | 1 << (cid + 16), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR); + + spin_unlock_irqrestore(&schan->lock, flags); + + return 0; +} + +static int sirfsoc_dma_resume_chan(struct sirfsoc_dma_chan *schan) +{ + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); + int cid = schan->chan.chan_id; + unsigned long flags; + + spin_lock_irqsave(&schan->lock, flags); + + if (!sdma->is_marco) + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) + | ((1 << cid) | 1 << (cid + 16)), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + else + writel_relaxed((1 << cid) | 1 << (cid + 16), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + + spin_unlock_irqrestore(&schan->lock, flags); + + return 0; +} + static int sirfsoc_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -320,6 +362,10 @@ static int sirfsoc_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); switch (cmd) { + case DMA_PAUSE: + return sirfsoc_dma_pause_chan(schan); + case DMA_RESUME: + return sirfsoc_dma_resume_chan(schan); case DMA_TERMINATE_ALL: return sirfsoc_dma_terminate_all(schan); case DMA_SLAVE_CONFIG: -- cgit v0.10.2 From 2b99c2592167eb1043e16d1e7187afdc7c940a41 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 14 Dec 2012 11:06:58 +0000 Subject: DMAEngine: sirf: lock the shared registers access in sirfsoc_dma_terminate_all Just like Russell pointed out in "DMAEngine: sirf: add DMA pause/resume support" at http://www.spinics.net/lists/arm-kernel/msg212496.html here I find sirfsoc_dma_terminate_all() has same problem, so move the locking to the front of registers access. Signed-off-by: Barry Song Cc: Russell King Signed-off-by: Vinod Koul diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 7d78cf7..fb5790d 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -291,6 +291,8 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) int cid = schan->chan.chan_id; unsigned long flags; + spin_lock_irqsave(&schan->lock, flags); + if (!sdma->is_marco) { writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); @@ -305,9 +307,9 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); - spin_lock_irqsave(&schan->lock, flags); list_splice_tail_init(&schan->active, &schan->free); list_splice_tail_init(&schan->queued, &schan->free); + spin_unlock_irqrestore(&schan->lock, flags); return 0; -- cgit v0.10.2 From 985a6c7dcf309378b91d16cdbdb0909479f99c33 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 18 Jan 2013 17:10:59 +0200 Subject: dw_dmac: print out DW_PARAMS and DWC_PARAMS when debug It's usefull to have the values of the DW_PARAMS and DWC_PARAMS printed when debug mode is enabled. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index dc3b955..ddd3ad2 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1608,6 +1608,8 @@ static int dw_probe(struct platform_device *pdev) dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; + dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params); + pdata = dev_get_platdata(&pdev->dev); if (!pdata) pdata = dw_dma_parse_dt(pdev); @@ -1721,6 +1723,9 @@ static int dw_probe(struct platform_device *pdev) dwc_params = dma_read_byaddr(regs + r * sizeof(u32), DWC_PARAMS); + dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, + dwc_params); + /* Decode maximum block size for given channel. The * stored 4 bit value represents blocks from 0x00 for 3 * up to 0x0a for 4095. */ -- cgit v0.10.2 From fdf475fa40f1468cf43a72b270f74dc6a4a5c905 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:00 +0200 Subject: dw_dmac: remove unnecessary tx_list field in dw_dma_chan The soft LLP mode is working for active descriptor only. So, we do not need to have a copy of its pointer. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index ddd3ad2..721beaf 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -282,9 +282,9 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); - dwc->tx_list = &first->tx_list; dwc->tx_node_active = &first->tx_list; + /* Submit first block */ dwc_do_single_block(dwc, first); return; @@ -402,15 +402,25 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) dma_writel(dw, CLEAR.XFER, dwc->mask); if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - if (dwc->tx_node_active != dwc->tx_list) { - desc = to_dw_desc(dwc->tx_node_active); + struct list_head *head, *active = dwc->tx_node_active; + + /* + * We are inside first active descriptor. + * Otherwise something is really wrong. + */ + desc = dwc_first_active(dwc); + + head = &desc->tx_list; + if (active != head) { + child = to_dw_desc(active); /* Submit next block */ - dwc_do_single_block(dwc, desc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc_do_single_block(dwc, child); + spin_unlock_irqrestore(&dwc->lock, flags); return; } + /* We are done here */ clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index fef296d..13000d2 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -194,7 +194,6 @@ struct dw_dma_chan { bool initialized; /* software emulation of the LLP transfers */ - struct list_head *tx_list; struct list_head *tx_node_active; spinlock_t lock; -- cgit v0.10.2 From 30d38a3286b140ae8cea84a93cde1f112e352aaf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:01 +0200 Subject: dw_dmac: introduce total_len field in struct dw_desc By this new field we distinguish a total length of the chain and the individual length of each descriptor in the chain. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 721beaf..7f9f332 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -333,18 +333,18 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) dma_unmap_single(parent, desc->lli.dar, - desc->len, DMA_FROM_DEVICE); + desc->total_len, DMA_FROM_DEVICE); else dma_unmap_page(parent, desc->lli.dar, - desc->len, DMA_FROM_DEVICE); + desc->total_len, DMA_FROM_DEVICE); } if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) dma_unmap_single(parent, desc->lli.sar, - desc->len, DMA_TO_DEVICE); + desc->total_len, DMA_TO_DEVICE); else dma_unmap_page(parent, desc->lli.sar, - desc->len, DMA_TO_DEVICE); + desc->total_len, DMA_TO_DEVICE); } } @@ -774,7 +774,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, prev->lli.llp = 0; first->txd.flags = flags; - first->len = len; + first->total_len = len; return &first->txd; @@ -937,7 +937,7 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - first->len = total_len; + first->total_len = total_len; return &first->txd; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 13000d2..833b4cf 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -296,6 +296,7 @@ struct dw_desc { struct list_head tx_list; struct dma_async_tx_descriptor txd; size_t len; + size_t total_len; }; #define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) -- cgit v0.10.2 From 176dcec50f3f0bc46f11b983c1a3bbc2dd3514fd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:02 +0200 Subject: dw_dmac: fill individual length of descriptor It will be useful to have the length of the transfer in the descriptor. The cyclic transfer functions remained untouched. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 7f9f332..5816da3 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -757,6 +757,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, desc->lli.dar = dest + offset; desc->lli.ctllo = ctllo; desc->lli.ctlhi = xfer_count; + desc->len = xfer_count << src_width; if (!first) { first = desc; @@ -855,6 +856,7 @@ slave_sg_todev_fill_desc: } desc->lli.ctlhi = dlen >> mem_width; + desc->len = dlen; if (!first) { first = desc; @@ -913,6 +915,7 @@ slave_sg_fromdev_fill_desc: len = 0; } desc->lli.ctlhi = dlen >> reg_width; + desc->len = dlen; if (!first) { first = desc; -- cgit v0.10.2 From 4702d5244ca947263e8b7eb2ba6d8721e80c46e2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:03 +0200 Subject: dw_dmac: return proper residue value Currently the driver returns full length of the active descriptor which is wrong. We have to go throught the active descriptor and substract the length of each sent children in the chain from the total length along with the actual data in the DMA channel registers. The cyclic case is not handled by this patch due to len field in the descriptor structure is left untouched by the original code. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 5816da3..35c8dd5 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -282,6 +282,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); + dwc->residue = first->total_len; dwc->tx_node_active = &first->tx_list; /* Submit first block */ @@ -385,6 +386,15 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_descriptor_complete(dwc, desc, true); } +/* Returns how many bytes were already received from source */ +static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) +{ + u32 ctlhi = channel_readl(dwc, CTL_HI); + u32 ctllo = channel_readl(dwc, CTL_LO); + + return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7)); +} + static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) { dma_addr_t llp; @@ -412,6 +422,12 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) head = &desc->tx_list; if (active != head) { + /* Update desc to reflect last sent one */ + if (active != head->next) + desc = to_dw_desc(active->prev); + + dwc->residue -= desc->len; + child = to_dw_desc(active); /* Submit next block */ @@ -424,6 +440,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) /* We are done here */ clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } + + dwc->residue = 0; + spin_unlock_irqrestore(&dwc->lock, flags); dwc_complete_all(dw, dwc); @@ -431,6 +450,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) } if (list_empty(&dwc->active_list)) { + dwc->residue = 0; spin_unlock_irqrestore(&dwc->lock, flags); return; } @@ -445,6 +465,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) (unsigned long long)llp); list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + /* initial residue value */ + dwc->residue = desc->total_len; + /* check first descriptors addr */ if (desc->txd.phys == llp) { spin_unlock_irqrestore(&dwc->lock, flags); @@ -454,16 +477,21 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) /* check first descriptors llp */ if (desc->lli.llp == llp) { /* This one is currently in progress */ + dwc->residue -= dwc_get_sent(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return; } - list_for_each_entry(child, &desc->tx_list, desc_node) + dwc->residue -= desc->len; + list_for_each_entry(child, &desc->tx_list, desc_node) { if (child->lli.llp == llp) { /* Currently in progress */ + dwc->residue -= dwc_get_sent(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return; } + dwc->residue -= child->len; + } /* * No descriptors so far seem to be in progress, i.e. @@ -1054,6 +1082,21 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, return 0; } +static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) +{ + unsigned long flags; + u32 residue; + + spin_lock_irqsave(&dwc->lock, flags); + + residue = dwc->residue; + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) + residue -= dwc_get_sent(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + return residue; +} + static enum dma_status dwc_tx_status(struct dma_chan *chan, dma_cookie_t cookie, @@ -1070,7 +1113,7 @@ dwc_tx_status(struct dma_chan *chan, } if (ret != DMA_SUCCESS) - dma_set_residue(txstate, dwc_first_active(dwc)->len); + dma_set_residue(txstate, dwc_get_residue(dwc)); if (dwc->paused) return DMA_PAUSED; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 833b4cf..88dd8eb 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -203,6 +203,7 @@ struct dw_dma_chan { struct list_head active_list; struct list_head queue; struct list_head free_list; + u32 residue; struct dw_cyclic_desc *cdesc; unsigned int descs_allocated; -- cgit v0.10.2 From cfdf5b6cc5985014a7ce891093f4fd0ae2d27ca6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 7 Feb 2013 17:36:28 +0200 Subject: dw_dmac: add support for Lynxpoint DMA controllers Intel Lynxpoint PCH Low Power Subsystem has DMA controller to support general purpose serial buses like SPI, I2C, and HSUART. This controller is enumerated from ACPI namespace with ACPI ID INTL9C60. Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 35c8dd5..6694676 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1899,6 +1899,11 @@ static const struct of_device_id dw_dma_id_table[] = { MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif +static const struct platform_device_id dw_dma_ids[] = { + { "INTL9C60", 0 }, + { } +}; + static struct platform_driver dw_driver = { .probe = dw_probe, .remove = dw_remove, @@ -1908,6 +1913,7 @@ static struct platform_driver dw_driver = { .pm = &dw_dev_pm_ops, .of_match_table = of_match_ptr(dw_dma_id_table), }, + .id_table = dw_dma_ids, }; static int __init dw_init(void) -- cgit v0.10.2 From 4dec23d7718e6f1f5e1773698d112025169e7d49 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 7 Feb 2013 14:38:32 -0700 Subject: ioatdma: fix race between updating ioat->head and IOAT_COMPLETION_PENDING There is a race that can hit during __cleanup() when the ioat->head pointer is incremented during descriptor submission. The __cleanup() can clear the PENDING flag when it does not see any active descriptors. This causes new submitted descriptors to be ignored because the COMPLETION_PENDING flag is cleared. This was introduced when code was adapted from ioatdma v1 to ioatdma v2. For v2 and v3, IOAT_COMPLETION_PENDING flag will be abandoned and a new flag IOAT_CHAN_ACTIVE will be utilized. This flag will also be protected under the prep_lock when being modified in order to avoid the race. Signed-off-by: Dave Jiang Reviewed-by: Dan Williams Signed-off-by: Vinod Koul diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 5e8fe01..1020598 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -97,6 +97,7 @@ struct ioat_chan_common { #define IOAT_KOBJ_INIT_FAIL 3 #define IOAT_RESHAPE_PENDING 4 #define IOAT_RUN 5 + #define IOAT_CHAN_ACTIVE 6 struct timer_list timer; #define COMPLETION_TIMEOUT msecs_to_jiffies(100) #define IDLE_TIMEOUT msecs_to_jiffies(2000) diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index b9d6678..e2b2c70 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -269,61 +269,22 @@ static void ioat2_restart_channel(struct ioat2_dma_chan *ioat) __ioat2_restart_chan(ioat); } -void ioat2_timer_event(unsigned long data) +static void check_active(struct ioat2_dma_chan *ioat) { - struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; - if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - dma_addr_t phys_complete; - u64 status; - - status = ioat_chansts(chan); - - /* when halted due to errors check for channel - * programming errors before advancing the completion state - */ - if (is_ioat_halted(status)) { - u32 chanerr; - - chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); - dev_err(to_dev(chan), "%s: Channel halted (%x)\n", - __func__, chanerr); - if (test_bit(IOAT_RUN, &chan->state)) - BUG_ON(is_ioat_bug(chanerr)); - else /* we never got off the ground */ - return; - } - - /* if we haven't made progress and we have already - * acknowledged a pending completion once, then be more - * forceful with a restart - */ - spin_lock_bh(&chan->cleanup_lock); - if (ioat_cleanup_preamble(chan, &phys_complete)) { - __cleanup(ioat, phys_complete); - } else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { - spin_lock_bh(&ioat->prep_lock); - ioat2_restart_channel(ioat); - spin_unlock_bh(&ioat->prep_lock); - } else { - set_bit(IOAT_COMPLETION_ACK, &chan->state); - mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); - } - spin_unlock_bh(&chan->cleanup_lock); - } else { - u16 active; + if (ioat2_ring_active(ioat)) { + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + return; + } + if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &chan->state)) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + else if (ioat->alloc_order > ioat_get_alloc_order()) { /* if the ring is idle, empty, and oversized try to step * down the size */ - spin_lock_bh(&chan->cleanup_lock); - spin_lock_bh(&ioat->prep_lock); - active = ioat2_ring_active(ioat); - if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) - reshape_ring(ioat, ioat->alloc_order-1); - spin_unlock_bh(&ioat->prep_lock); - spin_unlock_bh(&chan->cleanup_lock); + reshape_ring(ioat, ioat->alloc_order - 1); /* keep shrinking until we get back to our minimum * default size @@ -331,6 +292,60 @@ void ioat2_timer_event(unsigned long data) if (ioat->alloc_order > ioat_get_alloc_order()) mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); } + +} + +void ioat2_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; + dma_addr_t phys_complete; + u64 status; + + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + if (test_bit(IOAT_RUN, &chan->state)) + BUG_ON(is_ioat_bug(chanerr)); + else /* we never got off the ground */ + return; + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + spin_lock_bh(&chan->cleanup_lock); + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { + spin_lock_bh(&ioat->prep_lock); + ioat2_restart_channel(ioat); + spin_unlock_bh(&ioat->prep_lock); + spin_unlock_bh(&chan->cleanup_lock); + return; + } else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + + + if (ioat2_ring_active(ioat)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + else { + spin_lock_bh(&ioat->prep_lock); + check_active(ioat); + spin_unlock_bh(&ioat->prep_lock); + } + spin_unlock_bh(&chan->cleanup_lock); } static int ioat2_reset_hw(struct ioat_chan_common *chan) @@ -404,7 +419,7 @@ static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx) cookie = dma_cookie_assign(tx); dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); - if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state)) + if (!test_and_set_bit(IOAT_CHAN_ACTIVE, &chan->state)) mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); /* make descriptor updates visible before advancing ioat->head, diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index e52cf1e..570dd63 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -342,61 +342,22 @@ static void ioat3_restart_channel(struct ioat2_dma_chan *ioat) __ioat2_restart_chan(ioat); } -static void ioat3_timer_event(unsigned long data) +static void check_active(struct ioat2_dma_chan *ioat) { - struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; - if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - dma_addr_t phys_complete; - u64 status; - - status = ioat_chansts(chan); - - /* when halted due to errors check for channel - * programming errors before advancing the completion state - */ - if (is_ioat_halted(status)) { - u32 chanerr; - - chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); - dev_err(to_dev(chan), "%s: Channel halted (%x)\n", - __func__, chanerr); - if (test_bit(IOAT_RUN, &chan->state)) - BUG_ON(is_ioat_bug(chanerr)); - else /* we never got off the ground */ - return; - } - - /* if we haven't made progress and we have already - * acknowledged a pending completion once, then be more - * forceful with a restart - */ - spin_lock_bh(&chan->cleanup_lock); - if (ioat_cleanup_preamble(chan, &phys_complete)) - __cleanup(ioat, phys_complete); - else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { - spin_lock_bh(&ioat->prep_lock); - ioat3_restart_channel(ioat); - spin_unlock_bh(&ioat->prep_lock); - } else { - set_bit(IOAT_COMPLETION_ACK, &chan->state); - mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); - } - spin_unlock_bh(&chan->cleanup_lock); - } else { - u16 active; + if (ioat2_ring_active(ioat)) { + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + return; + } + if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &chan->state)) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + else if (ioat->alloc_order > ioat_get_alloc_order()) { /* if the ring is idle, empty, and oversized try to step * down the size */ - spin_lock_bh(&chan->cleanup_lock); - spin_lock_bh(&ioat->prep_lock); - active = ioat2_ring_active(ioat); - if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) - reshape_ring(ioat, ioat->alloc_order-1); - spin_unlock_bh(&ioat->prep_lock); - spin_unlock_bh(&chan->cleanup_lock); + reshape_ring(ioat, ioat->alloc_order - 1); /* keep shrinking until we get back to our minimum * default size @@ -404,6 +365,60 @@ static void ioat3_timer_event(unsigned long data) if (ioat->alloc_order > ioat_get_alloc_order()) mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); } + +} + +void ioat3_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; + dma_addr_t phys_complete; + u64 status; + + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + if (test_bit(IOAT_RUN, &chan->state)) + BUG_ON(is_ioat_bug(chanerr)); + else /* we never got off the ground */ + return; + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + spin_lock_bh(&chan->cleanup_lock); + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { + spin_lock_bh(&ioat->prep_lock); + ioat3_restart_channel(ioat); + spin_unlock_bh(&ioat->prep_lock); + spin_unlock_bh(&chan->cleanup_lock); + return; + } else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + + + if (ioat2_ring_active(ioat)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + else { + spin_lock_bh(&ioat->prep_lock); + check_active(ioat); + spin_unlock_bh(&ioat->prep_lock); + } + spin_unlock_bh(&chan->cleanup_lock); } static enum dma_status -- cgit v0.10.2 From 5fa422c922c2599dbfd960faf6dfca2411cc3f99 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 12 Feb 2013 09:15:02 -0800 Subject: dmaengine: move drivers/of/dma.c -> drivers/dma/of-dma.c as requested by Rob Suggested-by: Rob Herring Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0b408bb..e92b5f0 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -325,6 +325,10 @@ config DMA_ENGINE config DMA_VIRTUAL_CHANNELS tristate +config DMA_OF + def_bool y + depends on OF + comment "DMA Clients" depends on DMA_ENGINE diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 7428fea..c1ed644 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -3,6 +3,8 @@ ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o +obj-$(CONFIG_DMA_OF) += of-dma.o + obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o obj-$(CONFIG_DMATEST) += dmatest.o diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c new file mode 100644 index 0000000..59631b2 --- /dev/null +++ b/drivers/dma/of-dma.c @@ -0,0 +1,264 @@ +/* + * 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); +static DEFINE_SPINLOCK(of_dma_lock); + +/** + * 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_get_controller(struct of_phandle_args *dma_spec) +{ + struct of_dma *ofdma; + + spin_lock(&of_dma_lock); + + if (list_empty(&of_dma_list)) { + spin_unlock(&of_dma_lock); + return NULL; + } + + 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 + * @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; + ofdma->use_count = 0; + + /* Now queue of_dma controller structure in list */ + list_add_tail(&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. + */ +int of_dma_controller_free(struct device_node *np) +{ + struct of_dma *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); + +/** + * of_dma_match_channel - Check if a DMA specifier matches name + * @np: device node to look for DMA channels + * @name: channel name to be matched + * @index: index of DMA specifier in list of DMA specifiers + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Check if the DMA specifier pointed to by the index in a list of DMA + * specifiers, matches the name provided. Returns 0 if the name matches and + * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. + */ +static int of_dma_match_channel(struct device_node *np, char *name, int index, + struct of_phandle_args *dma_spec) +{ + const char *s; + + if (of_property_read_string_index(np, "dma-names", index, &s)) + return -ENODEV; + + if (strcmp(name, s)) + return -ENODEV; + + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, + dma_spec)) + return -ENODEV; + + return 0; +} + +/** + * 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 count, i; + + if (!np || !name) { + pr_err("%s: not enough information provided\n", __func__); + return NULL; + } + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + pr_err("%s: dma-names property missing or empty\n", __func__); + return NULL; + } + + for (i = 0; i < count; i++) { + if (of_dma_match_channel(np, name, i, &dma_spec)) + continue; + + ofdma = of_dma_get_controller(&dma_spec); + + if (!ofdma) + continue; + + chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + + of_dma_put_controller(ofdma); + + of_node_put(dma_spec.np); + + if (chan) + return chan; + } + + return NULL; +} + +/** + * 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/drivers/of/Makefile b/drivers/of/Makefile index eafa107..e027f44 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,4 @@ -obj-y = base.o dma.o +obj-y = base.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 deleted file mode 100644 index 59631b2..0000000 --- a/drivers/of/dma.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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); -static DEFINE_SPINLOCK(of_dma_lock); - -/** - * 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_get_controller(struct of_phandle_args *dma_spec) -{ - struct of_dma *ofdma; - - spin_lock(&of_dma_lock); - - if (list_empty(&of_dma_list)) { - spin_unlock(&of_dma_lock); - return NULL; - } - - 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 - * @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; - ofdma->use_count = 0; - - /* Now queue of_dma controller structure in list */ - list_add_tail(&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. - */ -int of_dma_controller_free(struct device_node *np) -{ - struct of_dma *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); - -/** - * of_dma_match_channel - Check if a DMA specifier matches name - * @np: device node to look for DMA channels - * @name: channel name to be matched - * @index: index of DMA specifier in list of DMA specifiers - * @dma_spec: pointer to DMA specifier as found in the device tree - * - * Check if the DMA specifier pointed to by the index in a list of DMA - * specifiers, matches the name provided. Returns 0 if the name matches and - * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. - */ -static int of_dma_match_channel(struct device_node *np, char *name, int index, - struct of_phandle_args *dma_spec) -{ - const char *s; - - if (of_property_read_string_index(np, "dma-names", index, &s)) - return -ENODEV; - - if (strcmp(name, s)) - return -ENODEV; - - if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, - dma_spec)) - return -ENODEV; - - return 0; -} - -/** - * 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 count, i; - - if (!np || !name) { - pr_err("%s: not enough information provided\n", __func__); - return NULL; - } - - count = of_property_count_strings(np, "dma-names"); - if (count < 0) { - pr_err("%s: dma-names property missing or empty\n", __func__); - return NULL; - } - - for (i = 0; i < count; i++) { - if (of_dma_match_channel(np, name, i, &dma_spec)) - continue; - - ofdma = of_dma_get_controller(&dma_spec); - - if (!ofdma) - continue; - - chan = ofdma->of_dma_xlate(&dma_spec, ofdma); - - of_dma_put_controller(ofdma); - - of_node_put(dma_spec.np); - - if (chan) - return chan; - } - - return NULL; -} - -/** - * 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); -- cgit v0.10.2 From a20702b8d7c2b54a618e203d94b37b4f1d21bbd4 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 13 Feb 2013 09:40:03 +0800 Subject: dmaengine: ioat - fix spare sparse complain >> drivers/dma/ioat/dma_v3.c:371:6: sparse: symbol 'ioat3_timer_event' was not declared. Reported-by: Fengguang Wu Signed-off-by: Fengguang Wu Acked-by: Dave Jiang Signed-off-by: Vinod Koul diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 570dd63..2acd1ad 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -368,7 +368,7 @@ static void check_active(struct ioat2_dma_chan *ioat) } -void ioat3_timer_event(unsigned long data) +static void ioat3_timer_event(unsigned long data) { struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; -- cgit v0.10.2 From 877e86f28385407f05c5aa4e397d4ccb3233f01a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 10:41:09 +0200 Subject: dw_dmac: apply default dma_mask if needed In some cases we got the device without dma_mask configured. We have to apply the default value to avoid crashes during memory mapping. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6694676..4c83f18 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1661,6 +1661,12 @@ static int dw_probe(struct platform_device *pdev) if (!regs) return -EBUSY; + /* Apply default dma_mask if needed */ + if (!pdev->dev.dma_mask) { + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + } + dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; -- cgit v0.10.2 From e68b1130dfbb834a4a028df01133591aeb59d241 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Thu, 14 Feb 2013 11:16:10 +0100 Subject: dma: of-dma.c: fix memory leakage The memory allocated to ofdma might be a leakage when error occurs. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 59631b2..583e50e3 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -107,6 +107,7 @@ int of_dma_controller_register(struct device_node *np, if (!nbcells) { pr_err("%s: #dma-cells property is missing or invalid\n", __func__); + kfree(ofdma); return -EINVAL; } -- cgit v0.10.2 From 7dd145252574e34d92ad574e5168e4115639c0be Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 14 Feb 2013 10:03:10 +0100 Subject: dmaengine: ste_dma40: do not remove descriptors for cyclic transfers Fix dma_tc_handle() to call d40_desc_remove() and d40_desc_done() only for non-cyclic transfers, as this was breaking ux500_pcm since introduced in: d49278e dmaengine: dma40: Add support to split up large elements Reported-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Vinod Koul diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ad860a2..1734fee 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1570,10 +1570,10 @@ static void dma_tc_handle(struct d40_chan *d40c) d40c->busy = false; pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); - } - d40_desc_remove(d40d); - d40_desc_done(d40c, d40d); + d40_desc_remove(d40d); + d40_desc_done(d40c, d40d); + } d40c->pending_tx++; tasklet_schedule(&d40c->tasklet); -- cgit v0.10.2 From 88b386c0a7c278581d2b8e2c2761d12f6475938d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:15 +0200 Subject: dma: of-dma: protect list write operation by spin_lock It's possible to have an inconsistency in the list due to unprotected operation on it. The patch adds a proper locking on the list operation. Signed-off-by: Andy Shevchenko Acked-by: Rob Herring Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 583e50e3..69d04d2 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -118,7 +118,9 @@ int of_dma_controller_register(struct device_node *np, ofdma->use_count = 0; /* Now queue of_dma controller structure in list */ + spin_lock(&of_dma_lock); list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); + spin_unlock(&of_dma_lock); return 0; } -- cgit v0.10.2 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 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index bfcdecb..f593999 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 v0.10.2 From 4168d0d9d304f184f786b1f00750557b8e09453c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:17 +0200 Subject: dma: coh901318: avoid unbalanced locking In case the len is 0 we must return without trying to unlock the lock that was not locked. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Cc: Linus Walleij Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Vinod Koul diff --git a/drivers/dma/coh901318_lli.c b/drivers/dma/coh901318_lli.c index 780e042..fe9cc5e 100644 --- a/drivers/dma/coh901318_lli.c +++ b/drivers/dma/coh901318_lli.c @@ -61,7 +61,7 @@ coh901318_lli_alloc(struct coh901318_pool *pool, unsigned int len) dma_addr_t phy; if (len == 0) - goto err; + return NULL; spin_lock(&pool->lock); -- cgit v0.10.2 From 9b562639a1dbef847dfc9daa807bd3e7e02ef24f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:18 +0200 Subject: dma: coh901318: set residue only if dma is in progress When status is DMA_SUCCESS the residue should be zero. Otherwise it's a bug. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Cc: Linus Walleij Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Vinod Koul diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index aa384e5..671e759 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1147,7 +1147,9 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, enum dma_status ret; ret = dma_cookie_status(chan, cookie, txstate); - /* FIXME: should be conditional on ret != DMA_SUCCESS? */ + if (ret == DMA_SUCCESS) + return ret; + dma_set_residue(txstate, coh901318_get_bytes_left(chan)); if (ret == DMA_IN_PROGRESS && cohc->stopped) -- cgit v0.10.2 From 373459eee0cd79f775ef9874395bb37638154777 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:19 +0200 Subject: edma: do not waste memory for dma_mask Accordingly to commentary in the platform_device_register_full the memory allocated for dma_mask will not going to be freed. That's why is better to assign dma_mask afterwards. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 82c8672..af44cad 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -620,13 +620,11 @@ static struct platform_device *pdev0, *pdev1; static const struct platform_device_info edma_dev_info0 = { .name = "edma-dma-engine", .id = 0, - .dma_mask = DMA_BIT_MASK(32), }; static const struct platform_device_info edma_dev_info1 = { .name = "edma-dma-engine", .id = 1, - .dma_mask = DMA_BIT_MASK(32), }; static int edma_init(void) @@ -640,6 +638,8 @@ static int edma_init(void) ret = PTR_ERR(pdev0); goto out; } + pdev0->dev.dma_mask = &pdev0->dev.coherent_dma_mask; + pdev0->dev.coherent_dma_mask = DMA_BIT_MASK(32); } if (EDMA_CTLRS == 2) { @@ -649,6 +649,8 @@ static int edma_init(void) platform_device_unregister(pdev0); ret = PTR_ERR(pdev1); } + pdev1->dev.dma_mask = &pdev1->dev.coherent_dma_mask; + pdev1->dev.coherent_dma_mask = DMA_BIT_MASK(32); } out: -- cgit v0.10.2 From a72208733f7866972bcba7995f7e598ad07f1158 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:20 +0200 Subject: dma: tegra20-apb-dma: remove unnecessary assignment There is no need to assign 0 to residue, because dma_cookie_status() does this for us. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Acked-by: Laxman Dewangan Signed-off-by: Vinod Koul diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 6c14481..c71812f 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -767,7 +767,6 @@ static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, ret = dma_cookie_status(dc, cookie, txstate); if (ret == DMA_SUCCESS) { - dma_set_residue(txstate, 0); spin_unlock_irqrestore(&tdc->lock, flags); return ret; } -- cgit v0.10.2 From 34d19355b84adde9eebc1d6771231c15dff891e6 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:05 +0530 Subject: DMA: PL330: Add new pl330 filter for DT case. This patch adds a new pl330_dt_filter for DT case to filter the required channel based on the new filter params and modifies the old filter only for non-DT case as suggested by Arnd Bergmann. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f7edb6f..40e9752 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -606,6 +606,11 @@ struct dma_pl330_desc { struct dma_pl330_chan *pchan; }; +struct dma_pl330_filter_args { + struct dma_pl330_dmac *pdmac; + unsigned int chan_id; +}; + static inline void _callback(struct pl330_req *r, enum pl330_op_err err) { if (r && r->xfer_cb) @@ -2352,6 +2357,16 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err) tasklet_schedule(&pch->task); } +static bool pl330_dt_filter(struct dma_chan *chan, void *param) +{ + struct dma_pl330_filter_args *fargs = param; + + if (chan->device != &fargs->pdmac->ddma) + return false; + + return (chan->chan_id == fargs->chan_id); +} + bool pl330_filter(struct dma_chan *chan, void *param) { u8 *peri_id; @@ -2359,20 +2374,6 @@ bool pl330_filter(struct dma_chan *chan, void *param) if (chan->device->dev->driver != &pl330_driver.drv) return false; -#ifdef CONFIG_OF - if (chan->device->dev->of_node) { - const __be32 *prop_value; - phandle phandle; - struct device_node *node; - - prop_value = ((struct property *)param)->value; - phandle = be32_to_cpup(prop_value++); - node = of_find_node_by_phandle(phandle); - return ((chan->private == node) && - (chan->chan_id == be32_to_cpup(prop_value))); - } -#endif - peri_id = chan->private; return *peri_id == (unsigned)param; } -- cgit v0.10.2 From a80258f9b2ac81e72ff680d273df9544a1307a32 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:06 +0530 Subject: DMA: PL330: Add xlate function Add xlate to translate the device-tree binding information into the appropriate format. The filter function requires the dma controller device and dma channel number as filter_params. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 40e9752..f5d47e6 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "dmaengine.h" #define PL330_MAX_CHAN 8 @@ -2379,6 +2380,30 @@ bool pl330_filter(struct dma_chan *chan, void *param) } EXPORT_SYMBOL(pl330_filter); +static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct dma_pl330_dmac *pdmac = ofdma->of_dma_data; + struct dma_pl330_filter_args fargs; + dma_cap_mask_t cap; + + if (!pdmac) + return NULL; + + if (count != 1) + return NULL; + + fargs.pdmac = pdmac; + fargs.chan_id = dma_spec->args[0]; + + dma_cap_zero(cap); + dma_cap_set(DMA_SLAVE, cap); + dma_cap_set(DMA_CYCLIC, cap); + + return dma_request_channel(cap, pl330_dt_filter, &fargs); +} + static int pl330_alloc_chan_resources(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); -- cgit v0.10.2 From 421da89aadd1b24f4a3bc1d60c9de9825ec2debc Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:07 +0530 Subject: DMA: PL330: Register the DMA controller with the generic DMA helpers This patch registers the pl330 dma controller driver with the generic device tree dma helper functions. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f5d47e6..fc9c800 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2995,6 +2995,14 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->pcfg.data_bus_width / 8, pi->pcfg.num_chan, pi->pcfg.num_peri, pi->pcfg.num_events); + ret = of_dma_controller_register(adev->dev.of_node, + of_dma_pl330_xlate, pdmac); + if (ret) { + dev_err(&adev->dev, + "unable to register DMA to the generic DT DMA helpers\n"); + goto probe_err2; + } + return 0; probe_err2: @@ -3015,6 +3023,8 @@ static int __devexit pl330_remove(struct amba_device *adev) if (!pdmac) return 0; + of_dma_controller_free(adev->dev.of_node); + amba_set_drvdata(adev, NULL); /* Idle the DMAC */ -- cgit v0.10.2 From 42cf20980cded7e0ecdd6dba74592d059c6a8bda Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:08 +0530 Subject: ARM: dts: pl330: Add #dma-cells for generic dma binding support This patch adds #dma-cells property to PL330 DMA controller nodes for supporting generic dma dt bindings on samsung exynos5250 platform. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/arm-pl330.txt b/Documentation/devicetree/bindings/dma/arm-pl330.txt index 36e27d5..2675658 100644 --- a/Documentation/devicetree/bindings/dma/arm-pl330.txt +++ b/Documentation/devicetree/bindings/dma/arm-pl330.txt @@ -10,7 +10,11 @@ Required properties: - interrupts: interrupt number to the cpu. Optional properties: -- dma-coherent : Present if dma operations are coherent + - dma-coherent : Present if dma operations are coherent + - #dma-cells: must be <1>. used to represent the number of integer + cells in the dmas property of client device. + - dma-channels: contains the total number of DMA channels supported by the DMAC + - dma-requests: contains the total number of DMA requests supported by the DMAC Example: @@ -18,16 +22,23 @@ Example: compatible = "arm,pl330", "arm,primecell"; reg = <0x12680000 0x1000>; interrupts = <99>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; }; Client drivers (device nodes requiring dma transfers from dev-to-mem or -mem-to-dev) should specify the DMA channel numbers using a two-value pair +mem-to-dev) should specify the DMA channel numbers and dma channel names as shown below. [property name] = <[phandle of the dma controller] [dma request id]>; + [property name] = <[dma channel name]> where 'dma request id' is the dma request number which is connected - to the client controller. The 'property name' is recommended to be - of the form -dma-channel. + to the client controller. The 'property name' 'dmas' and 'dma-names' + as required by the generic dma device tree binding helpers. The dma + names correspond 1:1 with the dma request ids in the dmas property. - Example: tx-dma-channel = <&pdma0 12>; + Example: dmas = <&pdma0 12 + &pdma1 11>; + dma-names = "tx", "rx"; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 2e3b6ef..c774aae 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -280,24 +280,36 @@ compatible = "arm,pl330", "arm,primecell"; reg = <0x121A0000 0x1000>; interrupts = <0 34 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; }; pdma1: pdma@121B0000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x121B0000 0x1000>; interrupts = <0 35 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; }; mdma0: mdma@10800000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x10800000 0x1000>; interrupts = <0 33 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <1>; }; mdma1: mdma@11C10000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x11C10000 0x1000>; interrupts = <0 124 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <1>; }; }; -- cgit v0.10.2 From 17166a3b6e88b93189e6be5f7e1335a3cc4fa965 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 Feb 2013 19:21:49 +0100 Subject: Revert "ARM: SPEAr13xx: Pass DW DMAC platform data from DT" This reverts commit f9965aa20706860077cfa093d04a6351c0c1e940. Signed-off-by: Arnd Bergmann Acked-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi index b2d41b7..34da11a 100644 --- a/arch/arm/boot/dts/spear1340.dtsi +++ b/arch/arm/boot/dts/spear1340.dtsi @@ -88,25 +88,6 @@ status = "disabled"; }; - dma@ea800000 { - slave_info { - uart1_tx { - bus_id = "uart1_tx"; - cfg_hi = <0x6000>; /* 0xC << 11 */ - cfg_lo = <0>; - src_master = <0>; - dst_master = <1>; - }; - uart1_tx { - bus_id = "uart1_tx"; - cfg_hi = <0x680>; /* 0xD << 7 */ - cfg_lo = <0>; - src_master = <1>; - dst_master = <0>; - }; - }; - }; - spi1: spi@5d400000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x5d400000 0x1000>; diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index 585f641..b4ca60f 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -105,37 +105,6 @@ reg = <0xea800000 0x1000>; interrupts = <0 19 0x4>; status = "disabled"; - - nr_channels = <8>; - chan_allocation_order = <1>; - chan_priority = <1>; - block_size = <0xfff>; - nr_masters = <2>; - data_width = <3 3 0 0>; - - slave_info { - ssp0_tx { - bus_id = "ssp0_tx"; - cfg_hi = <0x2000>; /* 0x4 << 11 */ - cfg_lo = <0>; - src_master = <0>; - dst_master = <0>; - }; - ssp0_rx { - bus_id = "ssp0_rx"; - cfg_hi = <0x280>; /* 0x5 << 7 */ - cfg_lo = <0>; - src_master = <0>; - dst_master = <0>; - }; - cf { - bus_id = "cf"; - cfg_hi = <0>; - cfg_lo = <0>; - src_master = <0>; - dst_master = <0>; - }; - }; }; dma@eb000000 { @@ -143,13 +112,6 @@ reg = <0xeb000000 0x1000>; interrupts = <0 59 0x4>; status = "disabled"; - - nr_channels = <8>; - chan_allocation_order = <1>; - chan_priority = <1>; - block_size = <0xfff>; - nr_masters = <2>; - data_width = <3 3 0 0>; }; fsmc: flash@b0000000 { diff --git a/arch/arm/mach-spear13xx/include/mach/spear.h b/arch/arm/mach-spear13xx/include/mach/spear.h index 972a151..7cfa681 100644 --- a/arch/arm/mach-spear13xx/include/mach/spear.h +++ b/arch/arm/mach-spear13xx/include/mach/spear.h @@ -43,6 +43,8 @@ #define VA_L2CC_BASE IOMEM(UL(0xFB000000)) /* others */ +#define DMAC0_BASE UL(0xEA800000) +#define DMAC1_BASE UL(0xEB000000) #define MCIF_CF_BASE UL(0xB2800000) /* Debug uart for linux, will be used for debug and uncompress messages */ diff --git a/arch/arm/mach-spear13xx/spear1310.c b/arch/arm/mach-spear13xx/spear1310.c index ec72c47..02f4724 100644 --- a/arch/arm/mach-spear13xx/spear1310.c +++ b/arch/arm/mach-spear13xx/spear1310.c @@ -36,7 +36,7 @@ static struct arasan_cf_pdata cf_pdata = { .cf_if_clk = CF_IF_CLK_166M, .quirk = CF_BROKEN_UDMA, - .dma_priv = "cf", + .dma_priv = &cf_dma_priv, }; /* ssp device registration */ @@ -47,7 +47,10 @@ static struct pl022_ssp_controller ssp1_plat_data = { /* Add SPEAr1310 auxdata to pass platform data */ static struct of_dev_auxdata spear1310_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata), + OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data), + OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data), OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data), + OF_DEV_AUXDATA("arm,pl022", SPEAR1310_SSP1_BASE, NULL, &ssp1_plat_data), {} }; diff --git a/arch/arm/mach-spear13xx/spear1340.c b/arch/arm/mach-spear13xx/spear1340.c index 69c8f72..081014f 100644 --- a/arch/arm/mach-spear13xx/spear1340.c +++ b/arch/arm/mach-spear13xx/spear1340.c @@ -18,9 +18,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -78,16 +78,26 @@ (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) -static struct amba_pl011_data uart1_data = { - .dma_filter = dw_dma_generic_filter, - .dma_tx_param = "uart1_tx", - .dma_rx_param = "uart1_rx", +static struct dw_dma_slave uart1_dma_param[] = { + { + /* Tx */ + .cfg_hi = DWC_CFGH_DST_PER(SPEAR1340_DMA_REQ_UART1_TX), + .cfg_lo = 0, + .src_master = DMA_MASTER_MEMORY, + .dst_master = SPEAR1340_DMA_MASTER_UART1, + }, { + /* Rx */ + .cfg_hi = DWC_CFGH_SRC_PER(SPEAR1340_DMA_REQ_UART1_RX), + .cfg_lo = 0, + .src_master = SPEAR1340_DMA_MASTER_UART1, + .dst_master = DMA_MASTER_MEMORY, + } }; -static struct arasan_cf_pdata cf_pdata = { - .cf_if_clk = CF_IF_CLK_166M, - .quirk = CF_BROKEN_UDMA, - .dma_priv = "cf", +static struct amba_pl011_data uart1_data = { + .dma_filter = dw_dma_filter, + .dma_tx_param = &uart1_dma_param[0], + .dma_rx_param = &uart1_dma_param[1], }; /* SATA device registration */ @@ -148,8 +158,11 @@ static struct ahci_platform_data sata_pdata = { /* Add SPEAr1340 auxdata to pass platform data */ static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata), + OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_dma_priv), + OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data), + OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data), OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data), + OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL, &sata_pdata), OF_DEV_AUXDATA("arm,pl011", SPEAR1340_UART1_BASE, NULL, &uart1_data), diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c index b074db8..c4af775 100644 --- a/arch/arm/mach-spear13xx/spear13xx.c +++ b/arch/arm/mach-spear13xx/spear13xx.c @@ -22,16 +22,63 @@ #include #include #include +#include #include #include +/* common dw_dma filter routine to be used by peripherals */ +bool dw_dma_filter(struct dma_chan *chan, void *slave) +{ + struct dw_dma_slave *dws = (struct dw_dma_slave *)slave; + + if (chan->device->dev == dws->dma_dev) { + chan->private = slave; + return true; + } else { + return false; + } +} + /* ssp device registration */ +static struct dw_dma_slave ssp_dma_param[] = { + { + /* Tx */ + .cfg_hi = DWC_CFGH_DST_PER(DMA_REQ_SSP0_TX), + .cfg_lo = 0, + .src_master = DMA_MASTER_MEMORY, + .dst_master = DMA_MASTER_SSP0, + }, { + /* Rx */ + .cfg_hi = DWC_CFGH_SRC_PER(DMA_REQ_SSP0_RX), + .cfg_lo = 0, + .src_master = DMA_MASTER_SSP0, + .dst_master = DMA_MASTER_MEMORY, + } +}; + struct pl022_ssp_controller pl022_plat_data = { .enable_dma = 1, - .dma_filter = dw_dma_generic_filter, - .dma_rx_param = "ssp0_rx", - .dma_tx_param = "ssp0_tx", - .num_chipselect = 3, + .dma_filter = dw_dma_filter, + .dma_rx_param = &ssp_dma_param[1], + .dma_tx_param = &ssp_dma_param[0], +}; + +/* CF device registration */ +struct dw_dma_slave cf_dma_priv = { + .cfg_hi = 0, + .cfg_lo = 0, + .src_master = 0, + .dst_master = 0, +}; + +/* dmac device registeration */ +struct dw_dma_platform_data dmac_plat_data = { + .nr_channels = 8, + .chan_allocation_order = CHAN_ALLOCATION_DESCENDING, + .chan_priority = CHAN_PRIORITY_DESCENDING, + .block_size = 4095U, + .nr_masters = 2, + .data_width = { 3, 3, 0, 0 }, }; void __init spear13xx_l2x0_init(void) -- cgit v0.10.2