summaryrefslogtreecommitdiffstats
path: root/target/linux/apm821xx
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/apm821xx')
-rw-r--r--target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch143
-rw-r--r--target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch345
-rw-r--r--target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch293
-rw-r--r--target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch1522
4 files changed, 2303 insertions, 0 deletions
diff --git a/target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch b/target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch
new file mode 100644
index 0000000000..924f7970a8
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch
@@ -0,0 +1,143 @@
+From 7bd903c5ca47fde5ad52370a47776491813c772e Mon Sep 17 00:00:00 2001
+From: Peter Ujfalusi <peter.ujfalusi@ti.com>
+Date: Mon, 14 Dec 2015 22:47:39 +0200
+Subject: [PATCH 1/3] dmaengine: core: Move and merge the code paths using
+ private_candidate
+
+Channel matching with private_candidate() is used in two paths, the error
+checking is slightly different in them and they are duplicating code also.
+Move the code under find_candidate() to provide consistent execution and
+going to allow us to reuse this mode of channel lookup later.
+
+Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Reviewed-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ drivers/dma/dmaengine.c | 81 +++++++++++++++++++++++++------------------------
+ 1 file changed, 42 insertions(+), 39 deletions(-)
+
+diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
+index f2cbff9..81a36fc 100644
+--- a/drivers/dma/dmaengine.c
++++ b/drivers/dma/dmaengine.c
+@@ -542,6 +542,42 @@ static struct dma_chan *private_candidate(const dma_cap_mask_t *mask,
+ return NULL;
+ }
+
++static struct dma_chan *find_candidate(struct dma_device *device,
++ const dma_cap_mask_t *mask,
++ dma_filter_fn fn, void *fn_param)
++{
++ struct dma_chan *chan = private_candidate(mask, device, fn, fn_param);
++ int err;
++
++ if (chan) {
++ /* Found a suitable channel, try to grab, prep, and return it.
++ * We first set DMA_PRIVATE to disable balance_ref_count as this
++ * channel will not be published in the general-purpose
++ * allocator
++ */
++ dma_cap_set(DMA_PRIVATE, device->cap_mask);
++ device->privatecnt++;
++ err = dma_chan_get(chan);
++
++ if (err) {
++ if (err == -ENODEV) {
++ pr_debug("%s: %s module removed\n", __func__,
++ dma_chan_name(chan));
++ list_del_rcu(&device->global_node);
++ } else
++ pr_debug("%s: failed to get %s: (%d)\n",
++ __func__, dma_chan_name(chan), err);
++
++ if (--device->privatecnt == 0)
++ dma_cap_clear(DMA_PRIVATE, device->cap_mask);
++
++ chan = ERR_PTR(err);
++ }
++ }
++
++ return chan ? chan : ERR_PTR(-EPROBE_DEFER);
++}
++
+ /**
+ * dma_get_slave_channel - try to get specific channel exclusively
+ * @chan: target channel
+@@ -580,7 +616,6 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
+ {
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+- int err;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+@@ -588,23 +623,11 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
+ /* lock against __dma_request_channel */
+ mutex_lock(&dma_list_mutex);
+
+- chan = private_candidate(&mask, device, NULL, NULL);
+- if (chan) {
+- dma_cap_set(DMA_PRIVATE, device->cap_mask);
+- device->privatecnt++;
+- err = dma_chan_get(chan);
+- if (err) {
+- pr_debug("%s: failed to get %s: (%d)\n",
+- __func__, dma_chan_name(chan), err);
+- chan = NULL;
+- if (--device->privatecnt == 0)
+- dma_cap_clear(DMA_PRIVATE, device->cap_mask);
+- }
+- }
++ chan = find_candidate(device, &mask, NULL, NULL);
+
+ mutex_unlock(&dma_list_mutex);
+
+- return chan;
++ return IS_ERR(chan) ? NULL : chan;
+ }
+ EXPORT_SYMBOL_GPL(dma_get_any_slave_channel);
+
+@@ -621,35 +644,15 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
+ {
+ struct dma_device *device, *_d;
+ struct dma_chan *chan = NULL;
+- int err;
+
+ /* Find a channel */
+ mutex_lock(&dma_list_mutex);
+ list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
+- chan = private_candidate(mask, device, fn, fn_param);
+- if (chan) {
+- /* Found a suitable channel, try to grab, prep, and
+- * return it. We first set DMA_PRIVATE to disable
+- * balance_ref_count as this channel will not be
+- * published in the general-purpose allocator
+- */
+- dma_cap_set(DMA_PRIVATE, device->cap_mask);
+- device->privatecnt++;
+- err = dma_chan_get(chan);
++ chan = find_candidate(device, mask, fn, fn_param);
++ if (!IS_ERR(chan))
++ break;
+
+- if (err == -ENODEV) {
+- pr_debug("%s: %s module removed\n",
+- __func__, dma_chan_name(chan));
+- list_del_rcu(&device->global_node);
+- } else if (err)
+- pr_debug("%s: failed to get %s: (%d)\n",
+- __func__, dma_chan_name(chan), err);
+- else
+- break;
+- if (--device->privatecnt == 0)
+- dma_cap_clear(DMA_PRIVATE, device->cap_mask);
+- chan = NULL;
+- }
++ chan = NULL;
+ }
+ mutex_unlock(&dma_list_mutex);
+
+--
+2.8.1
+
diff --git a/target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch b/target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch
new file mode 100644
index 0000000000..0296714639
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch
@@ -0,0 +1,345 @@
+From a8135d0d79e9d0ad3a4ff494fceeaae838becf38 Mon Sep 17 00:00:00 2001
+From: Peter Ujfalusi <peter.ujfalusi@ti.com>
+Date: Mon, 14 Dec 2015 22:47:40 +0200
+Subject: [PATCH 2/3] dmaengine: core: Introduce new, universal API to request
+ a channel
+
+The two API function can cover most, if not all current APIs used to
+request a channel. With minimal effort dmaengine drivers, platforms and
+dmaengine user drivers can be converted to use the two function.
+
+struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask);
+
+To request any channel matching with the requested capabilities, can be
+used to request channel for memcpy, memset, xor, etc where no hardware
+synchronization is needed.
+
+struct dma_chan *dma_request_chan(struct device *dev, const char *name);
+To request a slave channel. The dma_request_chan() will try to find the
+channel via DT, ACPI or in case if the kernel booted in non DT/ACPI mode
+it will use a filter lookup table and retrieves the needed information from
+the dma_slave_map provided by the DMA drivers.
+This legacy mode needs changes in platform code, in dmaengine drivers and
+finally the dmaengine user drivers can be converted:
+
+For each dmaengine driver an array of DMA device, slave and the parameter
+for the filter function needs to be added:
+
+static const struct dma_slave_map da830_edma_map[] = {
+ { "davinci-mcasp.0", "rx", EDMA_FILTER_PARAM(0, 0) },
+ { "davinci-mcasp.0", "tx", EDMA_FILTER_PARAM(0, 1) },
+ { "davinci-mcasp.1", "rx", EDMA_FILTER_PARAM(0, 2) },
+ { "davinci-mcasp.1", "tx", EDMA_FILTER_PARAM(0, 3) },
+ { "davinci-mcasp.2", "rx", EDMA_FILTER_PARAM(0, 4) },
+ { "davinci-mcasp.2", "tx", EDMA_FILTER_PARAM(0, 5) },
+ { "spi_davinci.0", "rx", EDMA_FILTER_PARAM(0, 14) },
+ { "spi_davinci.0", "tx", EDMA_FILTER_PARAM(0, 15) },
+ { "da830-mmc.0", "rx", EDMA_FILTER_PARAM(0, 16) },
+ { "da830-mmc.0", "tx", EDMA_FILTER_PARAM(0, 17) },
+ { "spi_davinci.1", "rx", EDMA_FILTER_PARAM(0, 18) },
+ { "spi_davinci.1", "tx", EDMA_FILTER_PARAM(0, 19) },
+};
+
+This information is going to be needed by the dmaengine driver, so
+modification to the platform_data is needed, and the driver map should be
+added to the pdata of the DMA driver:
+
+da8xx_edma0_pdata.slave_map = da830_edma_map;
+da8xx_edma0_pdata.slavecnt = ARRAY_SIZE(da830_edma_map);
+
+The DMA driver then needs to configure the needed device -> filter_fn
+mapping before it registers with dma_async_device_register() :
+
+ecc->dma_slave.filter_map.map = info->slave_map;
+ecc->dma_slave.filter_map.mapcnt = info->slavecnt;
+ecc->dma_slave.filter_map.fn = edma_filter_fn;
+
+When neither DT or ACPI lookup is available the dma_request_chan() will
+try to match the requester's device name with the filter_map's list of
+device names, when a match found it will use the information from the
+dma_slave_map to get the channel with the dma_get_channel() internal
+function.
+
+Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
+Reviewed-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ Documentation/dmaengine/client.txt | 23 +++-------
+ drivers/dma/dmaengine.c | 89 +++++++++++++++++++++++++++++++++-----
+ include/linux/dmaengine.h | 51 +++++++++++++++++++---
+ 3 files changed, 127 insertions(+), 36 deletions(-)
+
+diff --git a/Documentation/dmaengine/client.txt b/Documentation/dmaengine/client.txt
+index 11fb87f..4b04d89 100644
+--- a/Documentation/dmaengine/client.txt
++++ b/Documentation/dmaengine/client.txt
+@@ -22,25 +22,14 @@ The slave DMA usage consists of following steps:
+ Channel allocation is slightly different in the slave DMA context,
+ client drivers typically need a channel from a particular DMA
+ controller only and even in some cases a specific channel is desired.
+- To request a channel dma_request_channel() API is used.
++ To request a channel dma_request_chan() API is used.
+
+ Interface:
+- struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
+- dma_filter_fn filter_fn,
+- void *filter_param);
+- where dma_filter_fn is defined as:
+- typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
+-
+- The 'filter_fn' parameter is optional, but highly recommended for
+- slave and cyclic channels as they typically need to obtain a specific
+- DMA channel.
+-
+- When the optional 'filter_fn' parameter is NULL, dma_request_channel()
+- simply returns the first channel that satisfies the capability mask.
+-
+- Otherwise, the 'filter_fn' routine will be called once for each free
+- channel which has a capability in 'mask'. 'filter_fn' is expected to
+- return 'true' when the desired DMA channel is found.
++ struct dma_chan *dma_request_chan(struct device *dev, const char *name);
++
++ Which will find and return the 'name' DMA channel associated with the 'dev'
++ device. The association is done via DT, ACPI or board file based
++ dma_slave_map matching table.
+
+ A channel allocated via this interface is exclusive to the caller,
+ until dma_release_channel() is called.
+diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
+index 81a36fc..a094dbb 100644
+--- a/drivers/dma/dmaengine.c
++++ b/drivers/dma/dmaengine.c
+@@ -43,6 +43,7 @@
+
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
++#include <linux/platform_device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/init.h>
+ #include <linux/module.h>
+@@ -665,27 +666,73 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
+ }
+ EXPORT_SYMBOL_GPL(__dma_request_channel);
+
++static const struct dma_slave_map *dma_filter_match(struct dma_device *device,
++ const char *name,
++ struct device *dev)
++{
++ int i;
++
++ if (!device->filter.mapcnt)
++ return NULL;
++
++ for (i = 0; i < device->filter.mapcnt; i++) {
++ const struct dma_slave_map *map = &device->filter.map[i];
++
++ if (!strcmp(map->devname, dev_name(dev)) &&
++ !strcmp(map->slave, name))
++ return map;
++ }
++
++ return NULL;
++}
++
+ /**
+- * dma_request_slave_channel_reason - try to allocate an exclusive slave channel
++ * dma_request_chan - try to allocate an exclusive slave channel
+ * @dev: pointer to client device structure
+ * @name: slave channel name
+ *
+ * Returns pointer to appropriate DMA channel on success or an error pointer.
+ */
+-struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
+- const char *name)
++struct dma_chan *dma_request_chan(struct device *dev, const char *name)
+ {
++ struct dma_device *d, *_d;
++ struct dma_chan *chan = NULL;
++
+ /* If device-tree is present get slave info from here */
+ if (dev->of_node)
+- return of_dma_request_slave_channel(dev->of_node, name);
++ chan = of_dma_request_slave_channel(dev->of_node, name);
+
+ /* If device was enumerated by ACPI get slave info from here */
+- if (ACPI_HANDLE(dev))
+- return acpi_dma_request_slave_chan_by_name(dev, name);
++ if (has_acpi_companion(dev) && !chan)
++ chan = acpi_dma_request_slave_chan_by_name(dev, name);
++
++ if (chan) {
++ /* Valid channel found or requester need to be deferred */
++ if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
++ return chan;
++ }
++
++ /* Try to find the channel via the DMA filter map(s) */
++ mutex_lock(&dma_list_mutex);
++ list_for_each_entry_safe(d, _d, &dma_device_list, global_node) {
++ dma_cap_mask_t mask;
++ const struct dma_slave_map *map = dma_filter_match(d, name, dev);
++
++ if (!map)
++ continue;
++
++ dma_cap_zero(mask);
++ dma_cap_set(DMA_SLAVE, mask);
+
+- return ERR_PTR(-ENODEV);
++ chan = find_candidate(d, &mask, d->filter.fn, map->param);
++ if (!IS_ERR(chan))
++ break;
++ }
++ mutex_unlock(&dma_list_mutex);
++
++ return chan ? chan : ERR_PTR(-EPROBE_DEFER);
+ }
+-EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason);
++EXPORT_SYMBOL_GPL(dma_request_chan);
+
+ /**
+ * dma_request_slave_channel - try to allocate an exclusive slave channel
+@@ -697,17 +744,35 @@ EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason);
+ struct dma_chan *dma_request_slave_channel(struct device *dev,
+ const char *name)
+ {
+- struct dma_chan *ch = dma_request_slave_channel_reason(dev, name);
++ struct dma_chan *ch = dma_request_chan(dev, name);
+ if (IS_ERR(ch))
+ return NULL;
+
+- dma_cap_set(DMA_PRIVATE, ch->device->cap_mask);
+- ch->device->privatecnt++;
+-
+ return ch;
+ }
+ EXPORT_SYMBOL_GPL(dma_request_slave_channel);
+
++/**
++ * dma_request_chan_by_mask - allocate a channel satisfying certain capabilities
++ * @mask: capabilities that the channel must satisfy
++ *
++ * Returns pointer to appropriate DMA channel on success or an error pointer.
++ */
++struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask)
++{
++ struct dma_chan *chan;
++
++ if (!mask)
++ return ERR_PTR(-ENODEV);
++
++ chan = __dma_request_channel(mask, NULL, NULL);
++ if (!chan)
++ chan = ERR_PTR(-ENODEV);
++
++ return chan;
++}
++EXPORT_SYMBOL_GPL(dma_request_chan_by_mask);
++
+ 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 c47c68e..d50a6b51 100644
+--- a/include/linux/dmaengine.h
++++ b/include/linux/dmaengine.h
+@@ -607,11 +607,38 @@ enum dmaengine_alignment {
+ };
+
+ /**
++ * struct dma_slave_map - associates slave device and it's slave channel with
++ * parameter to be used by a filter function
++ * @devname: name of the device
++ * @slave: slave channel name
++ * @param: opaque parameter to pass to struct dma_filter.fn
++ */
++struct dma_slave_map {
++ const char *devname;
++ const char *slave;
++ void *param;
++};
++
++/**
++ * struct dma_filter - information for slave device/channel to filter_fn/param
++ * mapping
++ * @fn: filter function callback
++ * @mapcnt: number of slave device/channel in the map
++ * @map: array of channel to filter mapping data
++ */
++struct dma_filter {
++ dma_filter_fn fn;
++ int mapcnt;
++ const struct dma_slave_map *map;
++};
++
++/**
+ * struct dma_device - info on the entity supplying DMA services
+ * @chancnt: how many DMA channels are supported
+ * @privatecnt: how many DMA channels are requested by dma_request_channel
+ * @channels: the list of struct dma_chan
+ * @global_node: list_head for global dma_device_list
++ * @filter: information for device/slave to filter function/param mapping
+ * @cap_mask: one or more dma_capability flags
+ * @max_xor: maximum number of xor sources, 0 if no capability
+ * @max_pq: maximum number of PQ sources and PQ-continue capability
+@@ -666,6 +693,7 @@ struct dma_device {
+ unsigned int privatecnt;
+ struct list_head channels;
+ struct list_head global_node;
++ struct dma_filter filter;
+ dma_cap_mask_t cap_mask;
+ unsigned short max_xor;
+ unsigned short max_pq;
+@@ -1140,9 +1168,11 @@ 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(const dma_cap_mask_t *mask,
+ dma_filter_fn fn, void *fn_param);
+-struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
+- const char *name);
+ struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name);
++
++struct dma_chan *dma_request_chan(struct device *dev, const char *name);
++struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask);
++
+ void dma_release_channel(struct dma_chan *chan);
+ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps);
+ #else
+@@ -1166,16 +1196,21 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
+ {
+ return NULL;
+ }
+-static inline struct dma_chan *dma_request_slave_channel_reason(
+- struct device *dev, const char *name)
+-{
+- return ERR_PTR(-ENODEV);
+-}
+ static inline struct dma_chan *dma_request_slave_channel(struct device *dev,
+ const char *name)
+ {
+ return NULL;
+ }
++static inline struct dma_chan *dma_request_chan(struct device *dev,
++ const char *name)
++{
++ return ERR_PTR(-ENODEV);
++}
++static inline struct dma_chan *dma_request_chan_by_mask(
++ const dma_cap_mask_t *mask)
++{
++ return ERR_PTR(-ENODEV);
++}
+ static inline void dma_release_channel(struct dma_chan *chan)
+ {
+ }
+@@ -1186,6 +1221,8 @@ static inline int dma_get_slave_caps(struct dma_chan *chan,
+ }
+ #endif
+
++#define dma_request_slave_channel_reason(dev, name) dma_request_chan(dev, name)
++
+ static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx)
+ {
+ struct dma_slave_caps caps;
+--
+2.8.1
+
diff --git a/target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch b/target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch
new file mode 100644
index 0000000000..8fcf8caa8a
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch
@@ -0,0 +1,293 @@
+From b36f09c3c441a6e59eab9315032e7d546571de3f Mon Sep 17 00:00:00 2001
+From: Lars-Peter Clausen <lars@metafoo.de>
+Date: Tue, 20 Oct 2015 11:46:28 +0200
+Subject: [PATCH] dmaengine: Add transfer termination synchronization support
+
+The DMAengine API has a long standing race condition that is inherent to
+the API itself. Calling dmaengine_terminate_all() is supposed to stop and
+abort any pending or active transfers that have previously been submitted.
+Unfortunately it is possible that this operation races against a currently
+running (or with some drivers also scheduled) completion callback.
+
+Since the API allows dmaengine_terminate_all() to be called from atomic
+context as well as from within a completion callback it is not possible to
+synchronize to the execution of the completion callback from within
+dmaengine_terminate_all() itself.
+
+This means that a user of the DMAengine API does not know when it is safe
+to free resources used in the completion callback, which can result in a
+use-after-free race condition.
+
+This patch addresses the issue by introducing an explicit synchronization
+primitive to the DMAengine API called dmaengine_synchronize().
+
+The existing dmaengine_terminate_all() is deprecated in favor of
+dmaengine_terminate_sync() and dmaengine_terminate_async(). The former
+aborts all pending and active transfers and synchronizes to the current
+context, meaning it will wait until all running completion callbacks have
+finished. This means it is only possible to call this function from
+non-atomic context. The later function does not synchronize, but can still
+be used in atomic context or from within a complete callback. It has to be
+followed up by dmaengine_synchronize() before a client can free the
+resources used in a completion callback.
+
+In addition to this the semantics of the device_terminate_all() callback
+are slightly relaxed by this patch. It is now OK for a driver to only
+schedule the termination of the active transfer, but does not necessarily
+have to wait until the DMA controller has completely stopped. The driver
+must ensure though that the controller has stopped and no longer accesses
+any memory when the device_synchronize() callback returns.
+
+This was in part done since most drivers do not pay attention to this
+anyway at the moment and to emphasize that this needs to be done when the
+device_synchronize() callback is implemented. But it also helps with
+implementing support for devices where stopping the controller can require
+operations that may sleep.
+
+Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ Documentation/dmaengine/client.txt | 38 ++++++++++++++-
+ Documentation/dmaengine/provider.txt | 20 +++++++-
+ drivers/dma/dmaengine.c | 5 +-
+ include/linux/dmaengine.h | 90 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 148 insertions(+), 5 deletions(-)
+
+diff --git a/Documentation/dmaengine/client.txt b/Documentation/dmaengine/client.txt
+index 11fb87f..d9f9f46 100644
+--- a/Documentation/dmaengine/client.txt
++++ b/Documentation/dmaengine/client.txt
+@@ -128,7 +128,7 @@ The slave DMA usage consists of following steps:
+ transaction.
+
+ For cyclic DMA, a callback function may wish to terminate the
+- DMA via dmaengine_terminate_all().
++ DMA via dmaengine_terminate_async().
+
+ Therefore, it is important that DMA engine drivers drop any
+ locks before calling the callback function which may cause a
+@@ -166,12 +166,29 @@ The slave DMA usage consists of following steps:
+
+ Further APIs:
+
+-1. int dmaengine_terminate_all(struct dma_chan *chan)
++1. int dmaengine_terminate_sync(struct dma_chan *chan)
++ int dmaengine_terminate_async(struct dma_chan *chan)
++ int dmaengine_terminate_all(struct dma_chan *chan) /* DEPRECATED */
+
+ This causes all activity for the DMA channel to be stopped, and may
+ discard data in the DMA FIFO which hasn't been fully transferred.
+ No callback functions will be called for any incomplete transfers.
+
++ Two variants of this function are available.
++
++ dmaengine_terminate_async() might not wait until the DMA has been fully
++ stopped or until any running complete callbacks have finished. But it is
++ possible to call dmaengine_terminate_async() from atomic context or from
++ within a complete callback. dmaengine_synchronize() must be called before it
++ is safe to free the memory accessed by the DMA transfer or free resources
++ accessed from within the complete callback.
++
++ dmaengine_terminate_sync() will wait for the transfer and any running
++ complete callbacks to finish before it returns. But the function must not be
++ called from atomic context or from within a complete callback.
++
++ dmaengine_terminate_all() is deprecated and should not be used in new code.
++
+ 2. int dmaengine_pause(struct dma_chan *chan)
+
+ This pauses activity on the DMA channel without data loss.
+@@ -197,3 +214,20 @@ Further APIs:
+ a running DMA channel. It is recommended that DMA engine users
+ pause or stop (via dmaengine_terminate_all()) the channel before
+ using this API.
++
++5. void dmaengine_synchronize(struct dma_chan *chan)
++
++ Synchronize the termination of the DMA channel to the current context.
++
++ This function should be used after dmaengine_terminate_async() to synchronize
++ the termination of the DMA channel to the current context. The function will
++ wait for the transfer and any running complete callbacks to finish before it
++ returns.
++
++ If dmaengine_terminate_async() is used to stop the DMA channel this function
++ must be called before it is safe to free memory accessed by previously
++ submitted descriptors or to free any resources accessed within the complete
++ callback of previously submitted descriptors.
++
++ The behavior of this function is undefined if dma_async_issue_pending() has
++ been called between dmaengine_terminate_async() and this function.
+diff --git a/Documentation/dmaengine/provider.txt b/Documentation/dmaengine/provider.txt
+index 67d4ce4..122b7f4 100644
+--- a/Documentation/dmaengine/provider.txt
++++ b/Documentation/dmaengine/provider.txt
+@@ -327,8 +327,24 @@ supported.
+
+ * device_terminate_all
+ - Aborts all the pending and ongoing transfers on the channel
+- - This command should operate synchronously on the channel,
+- terminating right away all the channels
++ - For aborted transfers the complete callback should not be called
++ - Can be called from atomic context or from within a complete
++ callback of a descriptor. Must not sleep. Drivers must be able
++ to handle this correctly.
++ - Termination may be asynchronous. The driver does not have to
++ wait until the currently active transfer has completely stopped.
++ See device_synchronize.
++
++ * device_synchronize
++ - Must synchronize the termination of a channel to the current
++ context.
++ - Must make sure that memory for previously submitted
++ descriptors is no longer accessed by the DMA controller.
++ - Must make sure that all complete callbacks for previously
++ submitted descriptors have finished running and none are
++ scheduled to run.
++ - May sleep.
++
+
+ Misc notes (stuff that should be documented, but don't really know
+ where to put them)
+diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
+index 3ecec14..d6fc82e 100644
+--- a/drivers/dma/dmaengine.c
++++ b/drivers/dma/dmaengine.c
+@@ -265,8 +265,11 @@ static void dma_chan_put(struct dma_chan *chan)
+ module_put(dma_chan_to_owner(chan));
+
+ /* This channel is not in use anymore, free it */
+- if (!chan->client_count && chan->device->device_free_chan_resources)
++ if (!chan->client_count && chan->device->device_free_chan_resources) {
++ /* Make sure all operations have completed */
++ dmaengine_synchronize(chan);
+ chan->device->device_free_chan_resources(chan);
++ }
+
+ /* If the channel is used via a DMA request router, free the mapping */
+ if (chan->router && chan->router->route_free) {
+diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
+index c47c68e..4662d9a 100644
+--- a/include/linux/dmaengine.h
++++ b/include/linux/dmaengine.h
+@@ -654,6 +654,8 @@ enum dmaengine_alignment {
+ * paused. Returns 0 or an error code
+ * @device_terminate_all: Aborts all transfers on a channel. Returns 0
+ * or an error code
++ * @device_synchronize: Synchronizes the termination of a transfers to the
++ * current context.
+ * @device_tx_status: poll for transaction completion, the optional
+ * txstate parameter can be supplied with a pointer to get a
+ * struct with auxiliary transfer status information, otherwise the call
+@@ -737,6 +739,7 @@ struct dma_device {
+ int (*device_pause)(struct dma_chan *chan);
+ int (*device_resume)(struct dma_chan *chan);
+ int (*device_terminate_all)(struct dma_chan *chan);
++ void (*device_synchronize)(struct dma_chan *chan);
+
+ enum dma_status (*device_tx_status)(struct dma_chan *chan,
+ dma_cookie_t cookie,
+@@ -828,6 +831,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg(
+ src_sg, src_nents, flags);
+ }
+
++/**
++ * dmaengine_terminate_all() - Terminate all active DMA transfers
++ * @chan: The channel for which to terminate the transfers
++ *
++ * This function is DEPRECATED use either dmaengine_terminate_sync() or
++ * dmaengine_terminate_async() instead.
++ */
+ static inline int dmaengine_terminate_all(struct dma_chan *chan)
+ {
+ if (chan->device->device_terminate_all)
+@@ -836,6 +846,86 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan)
+ return -ENOSYS;
+ }
+
++/**
++ * dmaengine_terminate_async() - Terminate all active DMA transfers
++ * @chan: The channel for which to terminate the transfers
++ *
++ * Calling this function will terminate all active and pending descriptors
++ * that have previously been submitted to the channel. It is not guaranteed
++ * though that the transfer for the active descriptor has stopped when the
++ * function returns. Furthermore it is possible the complete callback of a
++ * submitted transfer is still running when this function returns.
++ *
++ * dmaengine_synchronize() needs to be called before it is safe to free
++ * any memory that is accessed by previously submitted descriptors or before
++ * freeing any resources accessed from within the completion callback of any
++ * perviously submitted descriptors.
++ *
++ * This function can be called from atomic context as well as from within a
++ * complete callback of a descriptor submitted on the same channel.
++ *
++ * If none of the two conditions above apply consider using
++ * dmaengine_terminate_sync() instead.
++ */
++static inline int dmaengine_terminate_async(struct dma_chan *chan)
++{
++ if (chan->device->device_terminate_all)
++ return chan->device->device_terminate_all(chan);
++
++ return -EINVAL;
++}
++
++/**
++ * dmaengine_synchronize() - Synchronize DMA channel termination
++ * @chan: The channel to synchronize
++ *
++ * Synchronizes to the DMA channel termination to the current context. When this
++ * function returns it is guaranteed that all transfers for previously issued
++ * descriptors have stopped and and it is safe to free the memory assoicated
++ * with them. Furthermore it is guaranteed that all complete callback functions
++ * for a previously submitted descriptor have finished running and it is safe to
++ * free resources accessed from within the complete callbacks.
++ *
++ * The behavior of this function is undefined if dma_async_issue_pending() has
++ * been called between dmaengine_terminate_async() and this function.
++ *
++ * This function must only be called from non-atomic context and must not be
++ * called from within a complete callback of a descriptor submitted on the same
++ * channel.
++ */
++static inline void dmaengine_synchronize(struct dma_chan *chan)
++{
++ if (chan->device->device_synchronize)
++ chan->device->device_synchronize(chan);
++}
++
++/**
++ * dmaengine_terminate_sync() - Terminate all active DMA transfers
++ * @chan: The channel for which to terminate the transfers
++ *
++ * Calling this function will terminate all active and pending transfers
++ * that have previously been submitted to the channel. It is similar to
++ * dmaengine_terminate_async() but guarantees that the DMA transfer has actually
++ * stopped and that all complete callbacks have finished running when the
++ * function returns.
++ *
++ * This function must only be called from non-atomic context and must not be
++ * called from within a complete callback of a descriptor submitted on the same
++ * channel.
++ */
++static inline int dmaengine_terminate_sync(struct dma_chan *chan)
++{
++ int ret;
++
++ ret = dmaengine_terminate_async(chan);
++ if (ret)
++ return ret;
++
++ dmaengine_synchronize(chan);
++
++ return 0;
++}
++
+ static inline int dmaengine_pause(struct dma_chan *chan)
+ {
+ if (chan->device->device_pause)
+--
+2.8.1
+
diff --git a/target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch b/target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch
new file mode 100644
index 0000000000..96b11a82b6
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch
@@ -0,0 +1,1522 @@
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Subject: [PATCH v6 0/4] Fixes / cleanups in dw_dmac (affects on few subsystems)
+Date: Mon, 25 Apr 2016 15:35:05 +0300
+
+This patch series (v3: http://www.spinics.net/lists/kernel/msg2215303.html)
+contains a number of mostly minor fixes and cleanups for the DW DMA driver. A
+couple of them affect the DT binding so these may need to be updated to
+maintain compatibility (old format is still supported though). The rest should
+be relatively straight-forward.
+
+This version has been tested on the following bare metal platforms:
+- ATNGW100 (avr32 based platform) with dmatest
+- Sam460ex (powerpc 44x based platform) with SATA
+- Intel Braswell with UART
+- Intel Galileo (Intel Quark based platform) with UART
+
+(SATA driver and Intel Galileo UART support are based on this series and just
+ published recently for a review)
+
+Vinod, there are few patch sets developed on top of this one, so, the idea is
+to keep this in an immuutable branch / tag.
+
+Changes since v5:
+- fixed an issue found by kbuildbot
+
+Changes since v4:
+- send proper set of patches
+- add changelog
+
+Changes since v3:
+- add patch 1 to check value of dma-masters property
+- drop the upstreamed patches
+- update patch 2 to keep an array for data-width property as well
+
+Changes since v2:
+- add patch 1 to fix master selection which was broken for long time
+- remove "use field-by-field initialization" patch since like Mans metioned in
+ has mostly no value and even might increase error prone
+- rebase on top of recent linux-next
+- wide testing on several platforms
+
+Changes since v1:
+- zeroing struct dw_dma_slave before use
+- fall back to old data_width property if data-width is not found
+- append tags for few patches
+- correct title of cover letter
+- rebase on top of recent linux-next
+
+Andy Shevchenko (4):
+ dmaengine: dw: platform: check nr_masters to be non-zero
+ dmaengine: dw: revisit data_width property
+ dmaengine: dw: keep entire platform data in struct dw_dma
+ dmaengine: dw: pass platform data via struct dw_dma_chip
+
+ Documentation/devicetree/bindings/dma/snps-dma.txt | 6 +-
+ arch/arc/boot/dts/abilis_tb10x.dtsi | 2 +-
+ arch/arm/boot/dts/spear13xx.dtsi | 4 +-
+ drivers/ata/sata_dwc_460ex.c | 2 +-
+ drivers/dma/dw/core.c | 75 ++++++++--------------
+ drivers/dma/dw/pci.c | 5 +-
+ drivers/dma/dw/platform.c | 32 +++++----
+ drivers/dma/dw/regs.h | 5 +-
+ include/linux/dma/dw.h | 5 +-
+ include/linux/platform_data/dma-dw.h | 4 +-
+ sound/soc/intel/common/sst-firmware.c | 2 +-
+ 11 files changed, 64 insertions(+), 78 deletions(-)
+
+--- a/drivers/dma/dw/core.c 2016-05-21 23:13:19.964478443 +0200
++++ b/drivers/dma/dw/core.c 2016-05-21 22:47:08.665465180 +0200
+@@ -45,22 +45,19 @@
+ DW_DMA_MSIZE_16; \
+ u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \
+ DW_DMA_MSIZE_16; \
++ u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \
++ _dwc->p_master : _dwc->m_master; \
++ u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \
++ _dwc->p_master : _dwc->m_master; \
+ \
+ (DWC_CTLL_DST_MSIZE(_dmsize) \
+ | DWC_CTLL_SRC_MSIZE(_smsize) \
+ | DWC_CTLL_LLP_D_EN \
+ | DWC_CTLL_LLP_S_EN \
+- | DWC_CTLL_DMS(_dwc->dst_master) \
+- | DWC_CTLL_SMS(_dwc->src_master)); \
++ | DWC_CTLL_DMS(_dms) \
++ | DWC_CTLL_SMS(_sms)); \
+ })
+
+-/*
+- * Number of descriptors to allocate for each channel. This should be
+- * made configurable somehow; preferably, the clients (at least the
+- * ones using slave transfers) should be able to give us a hint.
+- */
+-#define NR_DESCS_PER_CHANNEL 64
+-
+ /* The set of bus widths supported by the DMA controller */
+ #define DW_DMA_BUSWIDTHS \
+ BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \
+@@ -80,51 +77,65 @@ static struct dw_desc *dwc_first_active(
+ return to_dw_desc(dwc->active_list.next);
+ }
+
+-static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
++static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
+ {
+- struct dw_desc *desc, *_desc;
+- struct dw_desc *ret = NULL;
+- unsigned int i = 0;
+- unsigned long flags;
++ struct dw_desc *desc = txd_to_dw_desc(tx);
++ struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan);
++ dma_cookie_t cookie;
++ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+- list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) {
+- i++;
+- if (async_tx_test_ack(&desc->txd)) {
+- list_del(&desc->desc_node);
+- ret = desc;
+- break;
+- }
+- dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc);
+- }
++ cookie = dma_cookie_assign(tx);
++
++ /*
++ * REVISIT: We should attempt to chain as many descriptors as
++ * possible, perhaps even appending to those already submitted
++ * for DMA. But this is hard to do in a race-free manner.
++ */
++
++ list_add_tail(&desc->desc_node, &dwc->queue);
+ spin_unlock_irqrestore(&dwc->lock, flags);
++ dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n",
++ __func__, desc->txd.cookie);
+
+- dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i);
++ return cookie;
++}
+
+- return ret;
++static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
++{
++ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
++ struct dw_desc *desc;
++ dma_addr_t phys;
++
++ desc = dma_pool_zalloc(dw->desc_pool, GFP_ATOMIC, &phys);
++ if (!desc)
++ return NULL;
++
++ dwc->descs_allocated++;
++ INIT_LIST_HEAD(&desc->tx_list);
++ dma_async_tx_descriptor_init(&desc->txd, &dwc->chan);
++ desc->txd.tx_submit = dwc_tx_submit;
++ desc->txd.flags = DMA_CTRL_ACK;
++ desc->txd.phys = phys;
++ return desc;
+ }
+
+-/*
+- * Move a descriptor, including any children, to the free list.
+- * `desc' must not be on any lists.
+- */
+ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
+ {
+- unsigned long flags;
++ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
++ struct dw_desc *child, *_next;
+
+- if (desc) {
+- struct dw_desc *child;
++ if (unlikely(!desc))
++ return;
+
+- spin_lock_irqsave(&dwc->lock, flags);
+- list_for_each_entry(child, &desc->tx_list, desc_node)
+- dev_vdbg(chan2dev(&dwc->chan),
+- "moving child desc %p to freelist\n",
+- child);
+- list_splice_init(&desc->tx_list, &dwc->free_list);
+- dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc);
+- list_add(&desc->desc_node, &dwc->free_list);
+- spin_unlock_irqrestore(&dwc->lock, flags);
++ list_for_each_entry_safe(child, _next, &desc->tx_list, desc_node) {
++ list_del(&child->desc_node);
++ dma_pool_free(dw->desc_pool, child, child->txd.phys);
++ dwc->descs_allocated--;
+ }
++
++ dma_pool_free(dw->desc_pool, desc, desc->txd.phys);
++ dwc->descs_allocated--;
+ }
+
+ static void dwc_initialize(struct dw_dma_chan *dwc)
+@@ -133,7 +144,7 @@ static void dwc_initialize(struct dw_dma
+ u32 cfghi = DWC_CFGH_FIFO_MODE;
+ u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
+
+- if (dwc->initialized == true)
++ if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags))
+ return;
+
+ cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
+@@ -146,26 +157,11 @@ static void dwc_initialize(struct dw_dma
+ channel_set_bit(dw, MASK.XFER, dwc->mask);
+ channel_set_bit(dw, MASK.ERROR, dwc->mask);
+
+- dwc->initialized = true;
++ set_bit(DW_DMA_IS_INITIALIZED, &dwc->flags);
+ }
+
+ /*----------------------------------------------------------------------*/
+
+-static inline unsigned int dwc_fast_ffs(unsigned long long v)
+-{
+- /*
+- * We can be a lot more clever here, but this should take care
+- * of the most common optimization.
+- */
+- if (!(v & 7))
+- return 3;
+- else if (!(v & 3))
+- return 2;
+- else if (!(v & 1))
+- return 1;
+- return 0;
+-}
+-
+ static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc)
+ {
+ dev_err(chan2dev(&dwc->chan),
+@@ -197,12 +193,12 @@ static inline void dwc_do_single_block(s
+ * Software emulation of LLP mode relies on interrupts to continue
+ * multi block transfer.
+ */
+- ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN;
++ ctllo = lli_read(desc, ctllo) | DWC_CTLL_INT_EN;
+
+- channel_writel(dwc, SAR, desc->lli.sar);
+- channel_writel(dwc, DAR, desc->lli.dar);
++ channel_writel(dwc, SAR, lli_read(desc, sar));
++ channel_writel(dwc, DAR, lli_read(desc, dar));
+ channel_writel(dwc, CTL_LO, ctllo);
+- channel_writel(dwc, CTL_HI, desc->lli.ctlhi);
++ channel_writel(dwc, CTL_HI, lli_read(desc, ctlhi));
+ channel_set_bit(dw, CH_EN, dwc->mask);
+
+ /* Move pointer to next descriptor */
+@@ -213,6 +209,7 @@ static inline void dwc_do_single_block(s
+ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
+ {
+ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
++ u8 lms = DWC_LLP_LMS(dwc->m_master);
+ unsigned long was_soft_llp;
+
+ /* ASSERT: channel is idle */
+@@ -237,7 +234,7 @@ static void dwc_dostart(struct dw_dma_ch
+
+ dwc_initialize(dwc);
+
+- dwc->residue = first->total_len;
++ first->residue = first->total_len;
+ dwc->tx_node_active = &first->tx_list;
+
+ /* Submit first block */
+@@ -248,9 +245,8 @@ static void dwc_dostart(struct dw_dma_ch
+
+ dwc_initialize(dwc);
+
+- channel_writel(dwc, LLP, first->txd.phys);
+- channel_writel(dwc, CTL_LO,
+- DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN);
++ channel_writel(dwc, LLP, first->txd.phys | lms);
++ channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN);
+ channel_writel(dwc, CTL_HI, 0);
+ channel_set_bit(dw, CH_EN, dwc->mask);
+ }
+@@ -293,11 +289,7 @@ dwc_descriptor_complete(struct dw_dma_ch
+ list_for_each_entry(child, &desc->tx_list, desc_node)
+ async_tx_ack(&child->txd);
+ async_tx_ack(&desc->txd);
+-
+- list_splice_init(&desc->tx_list, &dwc->free_list);
+- list_move(&desc->desc_node, &dwc->free_list);
+-
+- dma_descriptor_unmap(txd);
++ dwc_desc_put(dwc, desc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (callback)
+@@ -368,11 +360,11 @@ static void dwc_scan_descriptors(struct
+
+ 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;
++ /* Update residue to reflect last sent descriptor */
++ if (active == head->next)
++ desc->residue -= desc->len;
++ else
++ desc->residue -= to_dw_desc(active->prev)->len;
+
+ child = to_dw_desc(active);
+
+@@ -387,8 +379,6 @@ static void dwc_scan_descriptors(struct
+ clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);
+ }
+
+- dwc->residue = 0;
+-
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ dwc_complete_all(dw, dwc);
+@@ -396,7 +386,6 @@ static void dwc_scan_descriptors(struct
+ }
+
+ if (list_empty(&dwc->active_list)) {
+- dwc->residue = 0;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+@@ -411,31 +400,31 @@ static void dwc_scan_descriptors(struct
+
+ list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
+ /* Initial residue value */
+- dwc->residue = desc->total_len;
++ desc->residue = desc->total_len;
+
+ /* Check first descriptors addr */
+- if (desc->txd.phys == llp) {
++ if (desc->txd.phys == DWC_LLP_LOC(llp)) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+
+ /* Check first descriptors llp */
+- if (desc->lli.llp == llp) {
++ if (lli_read(desc, llp) == llp) {
+ /* This one is currently in progress */
+- dwc->residue -= dwc_get_sent(dwc);
++ desc->residue -= dwc_get_sent(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+
+- dwc->residue -= desc->len;
++ desc->residue -= desc->len;
+ list_for_each_entry(child, &desc->tx_list, desc_node) {
+- if (child->lli.llp == llp) {
++ if (lli_read(child, llp) == llp) {
+ /* Currently in progress */
+- dwc->residue -= dwc_get_sent(dwc);
++ desc->residue -= dwc_get_sent(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+- dwc->residue -= child->len;
++ desc->residue -= child->len;
+ }
+
+ /*
+@@ -457,10 +446,14 @@ static void dwc_scan_descriptors(struct
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+-static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli)
++static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_desc *desc)
+ {
+ 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);
++ lli_read(desc, sar),
++ lli_read(desc, dar),
++ lli_read(desc, llp),
++ lli_read(desc, ctlhi),
++ lli_read(desc, ctllo));
+ }
+
+ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
+@@ -496,9 +489,9 @@ static void dwc_handle_error(struct dw_d
+ */
+ 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);
++ dwc_dump_lli(dwc, bad_desc);
+ list_for_each_entry(child, &bad_desc->tx_list, desc_node)
+- dwc_dump_lli(dwc, &child->lli);
++ dwc_dump_lli(dwc, child);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+@@ -549,7 +542,7 @@ static void dwc_handle_cyclic(struct dw_
+ */
+ if (unlikely(status_err & dwc->mask) ||
+ unlikely(status_xfer & dwc->mask)) {
+- int i;
++ unsigned int i;
+
+ dev_err(chan2dev(&dwc->chan),
+ "cyclic DMA unexpected %s interrupt, stopping DMA transfer\n",
+@@ -571,7 +564,7 @@ static void dwc_handle_cyclic(struct dw_
+ dma_writel(dw, CLEAR.XFER, dwc->mask);
+
+ for (i = 0; i < dwc->cdesc->periods; i++)
+- dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli);
++ dwc_dump_lli(dwc, dwc->cdesc->desc[i]);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+@@ -589,7 +582,7 @@ static void dw_dma_tasklet(unsigned long
+ u32 status_block;
+ u32 status_xfer;
+ u32 status_err;
+- int i;
++ unsigned int i;
+
+ status_block = dma_readl(dw, RAW.BLOCK);
+ status_xfer = dma_readl(dw, RAW.XFER);
+@@ -616,12 +609,17 @@ static void dw_dma_tasklet(unsigned long
+ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id)
+ {
+ struct dw_dma *dw = dev_id;
+- u32 status = dma_readl(dw, STATUS_INT);
++ u32 status;
++
++ /* Check if we have any interrupt from the DMAC which is not in use */
++ if (!dw->in_use)
++ return IRQ_NONE;
+
++ status = dma_readl(dw, STATUS_INT);
+ dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, status);
+
+ /* Check if we have any interrupt from the DMAC */
+- if (!status || !dw->in_use)
++ if (!status)
+ return IRQ_NONE;
+
+ /*
+@@ -653,30 +651,6 @@ static irqreturn_t dw_dma_interrupt(int
+
+ /*----------------------------------------------------------------------*/
+
+-static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
+-{
+- struct dw_desc *desc = txd_to_dw_desc(tx);
+- struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan);
+- dma_cookie_t cookie;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&dwc->lock, flags);
+- cookie = dma_cookie_assign(tx);
+-
+- /*
+- * REVISIT: We should attempt to chain as many descriptors as
+- * possible, perhaps even appending to those already submitted
+- * for DMA. But this is hard to do in a race-free manner.
+- */
+-
+- dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, desc->txd.cookie);
+- list_add_tail(&desc->desc_node, &dwc->queue);
+-
+- spin_unlock_irqrestore(&dwc->lock, flags);
+-
+- return cookie;
+-}
+-
+ static struct dma_async_tx_descriptor *
+ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+@@ -688,10 +662,12 @@ dwc_prep_dma_memcpy(struct dma_chan *cha
+ struct dw_desc *prev;
+ size_t xfer_count;
+ size_t offset;
++ u8 m_master = dwc->m_master;
+ unsigned int src_width;
+ unsigned int dst_width;
+- unsigned int data_width;
++ unsigned int data_width = dw->pdata->data_width[m_master];
+ u32 ctllo;
++ u8 lms = DWC_LLP_LMS(m_master);
+
+ dev_vdbg(chan2dev(chan),
+ "%s: d%pad s%pad l0x%zx f0x%lx\n", __func__,
+@@ -704,11 +680,7 @@ dwc_prep_dma_memcpy(struct dma_chan *cha
+
+ dwc->direction = DMA_MEM_TO_MEM;
+
+- data_width = min_t(unsigned int, dw->data_width[dwc->src_master],
+- dw->data_width[dwc->dst_master]);
+-
+- src_width = dst_width = min_t(unsigned int, data_width,
+- dwc_fast_ffs(src | dest | len));
++ src_width = dst_width = __ffs(data_width | src | dest | len);
+
+ ctllo = DWC_DEFAULT_CTLLO(chan)
+ | DWC_CTLL_DST_WIDTH(dst_width)
+@@ -726,27 +698,27 @@ dwc_prep_dma_memcpy(struct dma_chan *cha
+ if (!desc)
+ goto err_desc_get;
+
+- desc->lli.sar = src + offset;
+- desc->lli.dar = dest + offset;
+- desc->lli.ctllo = ctllo;
+- desc->lli.ctlhi = xfer_count;
++ lli_write(desc, sar, src + offset);
++ lli_write(desc, dar, dest + offset);
++ lli_write(desc, ctllo, ctllo);
++ lli_write(desc, ctlhi, xfer_count);
+ desc->len = xfer_count << src_width;
+
+ if (!first) {
+ first = desc;
+ } else {
+- prev->lli.llp = desc->txd.phys;
+- list_add_tail(&desc->desc_node,
+- &first->tx_list);
++ lli_write(prev, llp, desc->txd.phys | lms);
++ 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;
++ lli_set(prev, ctllo, DWC_CTLL_INT_EN);
+
+ prev->lli.llp = 0;
++ lli_clear(prev, ctllo, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN);
+ first->txd.flags = flags;
+ first->total_len = len;
+
+@@ -768,10 +740,12 @@ dwc_prep_slave_sg(struct dma_chan *chan,
+ struct dw_desc *prev;
+ struct dw_desc *first;
+ u32 ctllo;
++ u8 m_master = dwc->m_master;
++ u8 lms = DWC_LLP_LMS(m_master);
+ dma_addr_t reg;
+ unsigned int reg_width;
+ unsigned int mem_width;
+- unsigned int data_width;
++ unsigned int data_width = dw->pdata->data_width[m_master];
+ unsigned int i;
+ struct scatterlist *sg;
+ size_t total_len = 0;
+@@ -797,8 +771,6 @@ dwc_prep_slave_sg(struct dma_chan *chan,
+ ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
+ DWC_CTLL_FC(DW_DMA_FC_D_M2P);
+
+- data_width = dw->data_width[dwc->src_master];
+-
+ for_each_sg(sgl, sg, sg_len, i) {
+ struct dw_desc *desc;
+ u32 len, dlen, mem;
+@@ -806,17 +778,16 @@ dwc_prep_slave_sg(struct dma_chan *chan,
+ mem = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+
+- mem_width = min_t(unsigned int,
+- data_width, dwc_fast_ffs(mem | len));
++ mem_width = __ffs(data_width | mem | len);
+
+ slave_sg_todev_fill_desc:
+ desc = dwc_desc_get(dwc);
+ if (!desc)
+ goto err_desc_get;
+
+- desc->lli.sar = mem;
+- desc->lli.dar = reg;
+- desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width);
++ lli_write(desc, sar, mem);
++ lli_write(desc, dar, reg);
++ lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width));
+ if ((len >> mem_width) > dwc->block_size) {
+ dlen = dwc->block_size << mem_width;
+ mem += dlen;
+@@ -826,15 +797,14 @@ slave_sg_todev_fill_desc:
+ len = 0;
+ }
+
+- desc->lli.ctlhi = dlen >> mem_width;
++ lli_write(desc, ctlhi, dlen >> mem_width);
+ desc->len = dlen;
+
+ if (!first) {
+ first = desc;
+ } else {
+- prev->lli.llp = desc->txd.phys;
+- list_add_tail(&desc->desc_node,
+- &first->tx_list);
++ lli_write(prev, llp, desc->txd.phys | lms);
++ list_add_tail(&desc->desc_node, &first->tx_list);
+ }
+ prev = desc;
+ total_len += dlen;
+@@ -854,8 +824,6 @@ 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 = dw->data_width[dwc->dst_master];
+-
+ for_each_sg(sgl, sg, sg_len, i) {
+ struct dw_desc *desc;
+ u32 len, dlen, mem;
+@@ -863,17 +831,16 @@ slave_sg_todev_fill_desc:
+ mem = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+
+- mem_width = min_t(unsigned int,
+- data_width, dwc_fast_ffs(mem | len));
++ mem_width = __ffs(data_width | mem | len);
+
+ slave_sg_fromdev_fill_desc:
+ desc = dwc_desc_get(dwc);
+ if (!desc)
+ goto err_desc_get;
+
+- desc->lli.sar = reg;
+- desc->lli.dar = mem;
+- desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width);
++ lli_write(desc, sar, reg);
++ lli_write(desc, dar, mem);
++ lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width));
+ if ((len >> reg_width) > dwc->block_size) {
+ dlen = dwc->block_size << reg_width;
+ mem += dlen;
+@@ -882,15 +849,14 @@ slave_sg_fromdev_fill_desc:
+ dlen = len;
+ len = 0;
+ }
+- desc->lli.ctlhi = dlen >> reg_width;
++ lli_write(desc, ctlhi, dlen >> reg_width);
+ desc->len = dlen;
+
+ if (!first) {
+ first = desc;
+ } else {
+- prev->lli.llp = desc->txd.phys;
+- list_add_tail(&desc->desc_node,
+- &first->tx_list);
++ lli_write(prev, llp, desc->txd.phys | lms);
++ list_add_tail(&desc->desc_node, &first->tx_list);
+ }
+ prev = desc;
+ total_len += dlen;
+@@ -905,9 +871,10 @@ slave_sg_fromdev_fill_desc:
+
+ if (flags & DMA_PREP_INTERRUPT)
+ /* Trigger interrupt after last block */
+- prev->lli.ctllo |= DWC_CTLL_INT_EN;
++ lli_set(prev, ctllo, DWC_CTLL_INT_EN);
+
+ prev->lli.llp = 0;
++ lli_clear(prev, ctllo, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN);
+ first->total_len = total_len;
+
+ return &first->txd;
+@@ -932,8 +899,8 @@ bool dw_dma_filter(struct dma_chan *chan
+ dwc->src_id = dws->src_id;
+ dwc->dst_id = dws->dst_id;
+
+- dwc->src_master = dws->src_master;
+- dwc->dst_master = dws->dst_master;
++ dwc->m_master = dws->m_master;
++ dwc->p_master = dws->p_master;
+
+ return true;
+ }
+@@ -986,7 +953,7 @@ static int dwc_pause(struct dma_chan *ch
+ while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--)
+ udelay(2);
+
+- dwc->paused = true;
++ set_bit(DW_DMA_IS_PAUSED, &dwc->flags);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+@@ -999,7 +966,7 @@ static inline void dwc_chan_resume(struc
+
+ channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
+
+- dwc->paused = false;
++ clear_bit(DW_DMA_IS_PAUSED, &dwc->flags);
+ }
+
+ static int dwc_resume(struct dma_chan *chan)
+@@ -1007,12 +974,10 @@ static int dwc_resume(struct dma_chan *c
+ struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+ unsigned long flags;
+
+- if (!dwc->paused)
+- return 0;
+-
+ spin_lock_irqsave(&dwc->lock, flags);
+
+- dwc_chan_resume(dwc);
++ if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags))
++ dwc_chan_resume(dwc);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+@@ -1048,16 +1013,37 @@ static int dwc_terminate_all(struct dma_
+ return 0;
+ }
+
+-static inline u32 dwc_get_residue(struct dw_dma_chan *dwc)
++static struct dw_desc *dwc_find_desc(struct dw_dma_chan *dwc, dma_cookie_t c)
++{
++ struct dw_desc *desc;
++
++ list_for_each_entry(desc, &dwc->active_list, desc_node)
++ if (desc->txd.cookie == c)
++ return desc;
++
++ return NULL;
++}
++
++static u32 dwc_get_residue(struct dw_dma_chan *dwc, dma_cookie_t cookie)
+ {
++ struct dw_desc *desc;
+ 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);
++ desc = dwc_find_desc(dwc, cookie);
++ if (desc) {
++ if (desc == dwc_first_active(dwc)) {
++ residue = desc->residue;
++ if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue)
++ residue -= dwc_get_sent(dwc);
++ } else {
++ residue = desc->total_len;
++ }
++ } else {
++ residue = 0;
++ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return residue;
+@@ -1078,10 +1064,12 @@ dwc_tx_status(struct dma_chan *chan,
+ dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+- if (ret != DMA_COMPLETE)
+- dma_set_residue(txstate, dwc_get_residue(dwc));
++ if (ret == DMA_COMPLETE)
++ return ret;
++
++ dma_set_residue(txstate, dwc_get_residue(dwc, cookie));
+
+- if (dwc->paused && ret == DMA_IN_PROGRESS)
++ if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags) && ret == DMA_IN_PROGRESS)
+ return DMA_PAUSED;
+
+ return ret;
+@@ -1102,7 +1090,7 @@ static void dwc_issue_pending(struct dma
+
+ static void dw_dma_off(struct dw_dma *dw)
+ {
+- int i;
++ unsigned int i;
+
+ dma_writel(dw, CFG, 0);
+
+@@ -1116,7 +1104,7 @@ static void dw_dma_off(struct dw_dma *dw
+ cpu_relax();
+
+ for (i = 0; i < dw->dma.chancnt; i++)
+- dw->chan[i].initialized = false;
++ clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags);
+ }
+
+ static void dw_dma_on(struct dw_dma *dw)
+@@ -1128,9 +1116,6 @@ static int dwc_alloc_chan_resources(stru
+ {
+ struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+ struct dw_dma *dw = to_dw_dma(chan->device);
+- struct dw_desc *desc;
+- int i;
+- unsigned long flags;
+
+ dev_vdbg(chan2dev(chan), "%s\n", __func__);
+
+@@ -1161,48 +1146,13 @@ static int dwc_alloc_chan_resources(stru
+ dw_dma_on(dw);
+ dw->in_use |= dwc->mask;
+
+- 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 = 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 = phys;
+-
+- dwc_desc_put(dwc, desc);
+-
+- spin_lock_irqsave(&dwc->lock, flags);
+- i = ++dwc->descs_allocated;
+- }
+-
+- spin_unlock_irqrestore(&dwc->lock, flags);
+-
+- dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i);
+-
+- return i;
+-
+-err_desc_alloc:
+- dev_info(chan2dev(chan), "only allocated %d descriptors\n", i);
+-
+- return i;
++ return 0;
+ }
+
+ static void dwc_free_chan_resources(struct dma_chan *chan)
+ {
+ struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+ struct dw_dma *dw = to_dw_dma(chan->device);
+- struct dw_desc *desc, *_desc;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+@@ -1215,17 +1165,15 @@ static void dwc_free_chan_resources(stru
+ BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+- list_splice_init(&dwc->free_list, &list);
+- dwc->descs_allocated = 0;
+
+ /* Clear custom channel configuration */
+ dwc->src_id = 0;
+ dwc->dst_id = 0;
+
+- dwc->src_master = 0;
+- dwc->dst_master = 0;
++ dwc->m_master = 0;
++ dwc->p_master = 0;
+
+- dwc->initialized = false;
++ clear_bit(DW_DMA_IS_INITIALIZED, &dwc->flags);
+
+ /* Disable interrupts */
+ channel_clear_bit(dw, MASK.XFER, dwc->mask);
+@@ -1239,11 +1187,6 @@ static void dwc_free_chan_resources(stru
+ if (!dw->in_use)
+ dw_dma_off(dw);
+
+- list_for_each_entry_safe(desc, _desc, &list, desc_node) {
+- dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc);
+- dma_pool_free(dw->desc_pool, desc, desc->txd.phys);
+- }
+-
+ dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
+ }
+
+@@ -1321,6 +1264,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_pre
+ struct dw_cyclic_desc *retval = NULL;
+ struct dw_desc *desc;
+ struct dw_desc *last = NULL;
++ u8 lms = DWC_LLP_LMS(dwc->m_master);
+ unsigned long was_cyclic;
+ unsigned int reg_width;
+ unsigned int periods;
+@@ -1374,9 +1318,6 @@ struct dw_cyclic_desc *dw_dma_cyclic_pre
+
+ retval = ERR_PTR(-ENOMEM);
+
+- if (periods > NR_DESCS_PER_CHANNEL)
+- goto out_err;
+-
+ cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL);
+ if (!cdesc)
+ goto out_err;
+@@ -1392,50 +1333,50 @@ struct dw_cyclic_desc *dw_dma_cyclic_pre
+
+ switch (direction) {
+ case DMA_MEM_TO_DEV:
+- desc->lli.dar = sconfig->dst_addr;
+- desc->lli.sar = buf_addr + (period_len * i);
+- desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
+- | DWC_CTLL_DST_WIDTH(reg_width)
+- | DWC_CTLL_SRC_WIDTH(reg_width)
+- | DWC_CTLL_DST_FIX
+- | DWC_CTLL_SRC_INC
+- | DWC_CTLL_INT_EN);
+-
+- desc->lli.ctllo |= sconfig->device_fc ?
+- DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
+- DWC_CTLL_FC(DW_DMA_FC_D_M2P);
++ lli_write(desc, dar, sconfig->dst_addr);
++ lli_write(desc, sar, buf_addr + period_len * i);
++ lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan)
++ | DWC_CTLL_DST_WIDTH(reg_width)
++ | DWC_CTLL_SRC_WIDTH(reg_width)
++ | DWC_CTLL_DST_FIX
++ | DWC_CTLL_SRC_INC
++ | DWC_CTLL_INT_EN));
++
++ lli_set(desc, ctllo, sconfig->device_fc ?
++ DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
++ DWC_CTLL_FC(DW_DMA_FC_D_M2P));
+
+ break;
+ case DMA_DEV_TO_MEM:
+- desc->lli.dar = buf_addr + (period_len * i);
+- desc->lli.sar = sconfig->src_addr;
+- desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
+- | DWC_CTLL_SRC_WIDTH(reg_width)
+- | DWC_CTLL_DST_WIDTH(reg_width)
+- | DWC_CTLL_DST_INC
+- | DWC_CTLL_SRC_FIX
+- | DWC_CTLL_INT_EN);
+-
+- desc->lli.ctllo |= sconfig->device_fc ?
+- DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
+- DWC_CTLL_FC(DW_DMA_FC_D_P2M);
++ lli_write(desc, dar, buf_addr + period_len * i);
++ lli_write(desc, sar, sconfig->src_addr);
++ lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan)
++ | DWC_CTLL_SRC_WIDTH(reg_width)
++ | DWC_CTLL_DST_WIDTH(reg_width)
++ | DWC_CTLL_DST_INC
++ | DWC_CTLL_SRC_FIX
++ | DWC_CTLL_INT_EN));
++
++ lli_set(desc, ctllo, sconfig->device_fc ?
++ DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
++ DWC_CTLL_FC(DW_DMA_FC_D_P2M));
+
+ break;
+ default:
+ break;
+ }
+
+- desc->lli.ctlhi = (period_len >> reg_width);
++ lli_write(desc, ctlhi, period_len >> reg_width);
+ cdesc->desc[i] = desc;
+
+ if (last)
+- last->lli.llp = desc->txd.phys;
++ lli_write(last, llp, desc->txd.phys | lms);
+
+ last = desc;
+ }
+
+ /* Let's make a cyclic list */
+- last->lli.llp = cdesc->desc[0]->txd.phys;
++ lli_write(last, llp, cdesc->desc[0]->txd.phys | lms);
+
+ dev_dbg(chan2dev(&dwc->chan),
+ "cyclic prepared buf %pad len %zu period %zu periods %d\n",
+@@ -1466,7 +1407,7 @@ void dw_dma_cyclic_free(struct dma_chan
+ struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+ struct dw_dma *dw = to_dw_dma(dwc->chan.device);
+ struct dw_cyclic_desc *cdesc = dwc->cdesc;
+- int i;
++ unsigned int i;
+ unsigned long flags;
+
+ dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__);
+@@ -1490,32 +1431,38 @@ void dw_dma_cyclic_free(struct dma_chan
+ kfree(cdesc->desc);
+ kfree(cdesc);
+
++ dwc->cdesc = NULL;
++
+ clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags);
+ }
+ EXPORT_SYMBOL(dw_dma_cyclic_free);
+
+ /*----------------------------------------------------------------------*/
+
+-int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
++int dw_dma_probe(struct dw_dma_chip *chip)
+ {
++ struct dw_dma_platform_data *pdata;
+ struct dw_dma *dw;
+ bool autocfg = false;
+ unsigned int dw_params;
+- unsigned int max_blk_size = 0;
++ unsigned int i;
+ int err;
+- int i;
+
+ dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
++ dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL);
++ if (!dw->pdata)
++ return -ENOMEM;
++
+ dw->regs = chip->regs;
+ chip->dw = dw;
+
+ pm_runtime_get_sync(chip->dev);
+
+- if (!pdata) {
+- dw_params = dma_read_byaddr(chip->regs, DW_PARAMS);
++ if (!chip->pdata) {
++ dw_params = dma_readl(dw, DW_PARAMS);
+ dev_dbg(chip->dev, "DW_PARAMS: 0x%08x\n", dw_params);
+
+ autocfg = dw_params >> DW_PARAMS_EN & 1;
+@@ -1524,29 +1471,31 @@ int dw_dma_probe(struct dw_dma_chip *chi
+ goto err_pdata;
+ }
+
+- pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
+- if (!pdata) {
+- err = -ENOMEM;
+- goto err_pdata;
+- }
++ /* Reassign the platform data pointer */
++ pdata = dw->pdata;
+
+ /* Get hardware configuration parameters */
+ pdata->nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 7) + 1;
+ pdata->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1;
+ for (i = 0; i < pdata->nr_masters; i++) {
+ pdata->data_width[i] =
+- (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2;
++ 4 << (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3);
+ }
+- max_blk_size = dma_readl(dw, MAX_BLK_SIZE);
++ pdata->block_size = dma_readl(dw, MAX_BLK_SIZE);
+
+ /* Fill platform data with the default values */
+ pdata->is_private = true;
+ pdata->is_memcpy = true;
+ pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING;
+ pdata->chan_priority = CHAN_PRIORITY_ASCENDING;
+- } else if (pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) {
++ } else if (chip->pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) {
+ err = -EINVAL;
+ goto err_pdata;
++ } else {
++ memcpy(dw->pdata, chip->pdata, sizeof(*dw->pdata));
++
++ /* Reassign the platform data pointer */
++ pdata = dw->pdata;
+ }
+
+ dw->chan = devm_kcalloc(chip->dev, pdata->nr_channels, sizeof(*dw->chan),
+@@ -1556,11 +1505,6 @@ int dw_dma_probe(struct dw_dma_chip *chi
+ goto err_pdata;
+ }
+
+- /* Get hardware configuration parameters */
+- dw->nr_masters = pdata->nr_masters;
+- for (i = 0; i < dw->nr_masters; i++)
+- dw->data_width[i] = pdata->data_width[i];
+-
+ /* Calculate all channel mask before DMA setup */
+ dw->all_chan_mask = (1 << pdata->nr_channels) - 1;
+
+@@ -1607,7 +1551,6 @@ int dw_dma_probe(struct dw_dma_chip *chi
+
+ INIT_LIST_HEAD(&dwc->active_list);
+ INIT_LIST_HEAD(&dwc->queue);
+- INIT_LIST_HEAD(&dwc->free_list);
+
+ channel_clear_bit(dw, CH_EN, dwc->mask);
+
+@@ -1615,11 +1558,9 @@ int dw_dma_probe(struct dw_dma_chip *chi
+
+ /* Hardware configuration */
+ if (autocfg) {
+- unsigned int dwc_params;
+ unsigned int r = DW_DMA_MAX_NR_CHANNELS - i - 1;
+- void __iomem *addr = chip->regs + r * sizeof(u32);
+-
+- dwc_params = dma_read_byaddr(addr, DWC_PARAMS);
++ void __iomem *addr = &__dw_regs(dw)->DWC_PARAMS[r];
++ unsigned int dwc_params = dma_readl_native(addr);
+
+ dev_dbg(chip->dev, "DWC_PARAMS[%d]: 0x%08x\n", i,
+ dwc_params);
+@@ -1630,16 +1571,15 @@ int dw_dma_probe(struct dw_dma_chip *chi
+ * up to 0x0a for 4095.
+ */
+ dwc->block_size =
+- (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1;
++ (4 << ((pdata->block_size >> 4 * i) & 0xf)) - 1;
+ dwc->nollp =
+ (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0;
+ } else {
+ dwc->block_size = pdata->block_size;
+
+ /* Check if channel supports multi block transfer */
+- channel_writel(dwc, LLP, 0xfffffffc);
+- dwc->nollp =
+- (channel_readl(dwc, LLP) & 0xfffffffc) == 0;
++ channel_writel(dwc, LLP, DWC_LLP_LOC(0xffffffff));
++ dwc->nollp = DWC_LLP_LOC(channel_readl(dwc, LLP)) == 0;
+ channel_writel(dwc, LLP, 0);
+ }
+ }
+--- a/drivers/dma/dw/pci.c 2016-05-21 23:13:19.964478443 +0200
++++ b/drivers/dma/dw/pci.c 2016-05-21 22:47:08.665465180 +0200
+@@ -17,8 +17,8 @@
+
+ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
+ {
++ const struct dw_dma_platform_data *pdata = (void *)pid->driver_data;
+ struct dw_dma_chip *chip;
+- struct dw_dma_platform_data *pdata = (void *)pid->driver_data;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+@@ -49,8 +49,9 @@ static int dw_pci_probe(struct pci_dev *
+ chip->dev = &pdev->dev;
+ chip->regs = pcim_iomap_table(pdev)[0];
+ chip->irq = pdev->irq;
++ chip->pdata = pdata;
+
+- ret = dw_dma_probe(chip, pdata);
++ ret = dw_dma_probe(chip);
+ if (ret)
+ return ret;
+
+@@ -108,6 +109,10 @@ static const struct pci_device_id dw_pci
+
+ /* Haswell */
+ { PCI_VDEVICE(INTEL, 0x9c60) },
++
++ /* Broadwell */
++ { PCI_VDEVICE(INTEL, 0x9ce0) },
++
+ { }
+ };
+ MODULE_DEVICE_TABLE(pci, dw_pci_id_table);
+--- a/drivers/dma/dw/platform.c 2016-05-21 23:13:19.964478443 +0200
++++ b/drivers/dma/dw/platform.c 2016-05-21 22:47:08.665465180 +0200
+@@ -42,13 +42,13 @@ static struct dma_chan *dw_dma_of_xlate(
+
+ slave.src_id = dma_spec->args[0];
+ slave.dst_id = dma_spec->args[0];
+- slave.src_master = dma_spec->args[1];
+- slave.dst_master = dma_spec->args[2];
++ slave.m_master = dma_spec->args[1];
++ slave.p_master = dma_spec->args[2];
+
+ if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS ||
+ slave.dst_id >= DW_DMA_MAX_NR_REQUESTS ||
+- slave.src_master >= dw->nr_masters ||
+- slave.dst_master >= dw->nr_masters))
++ slave.m_master >= dw->pdata->nr_masters ||
++ slave.p_master >= dw->pdata->nr_masters))
+ return NULL;
+
+ dma_cap_zero(cap);
+@@ -66,8 +66,8 @@ static bool dw_dma_acpi_filter(struct dm
+ .dma_dev = dma_spec->dev,
+ .src_id = dma_spec->slave_id,
+ .dst_id = dma_spec->slave_id,
+- .src_master = 1,
+- .dst_master = 0,
++ .m_master = 0,
++ .p_master = 1,
+ };
+
+ return dw_dma_filter(chan, &slave);
+@@ -103,18 +103,28 @@ dw_dma_parse_dt(struct platform_device *
+ struct device_node *np = pdev->dev.of_node;
+ struct dw_dma_platform_data *pdata;
+ u32 tmp, arr[DW_DMA_MAX_NR_MASTERS];
++ u32 nr_masters;
++ u32 nr_channels;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Missing DT data\n");
+ return NULL;
+ }
+
++ if (of_property_read_u32(np, "dma-masters", &nr_masters))
++ return NULL;
++ if (nr_masters < 1 || nr_masters > DW_DMA_MAX_NR_MASTERS)
++ return NULL;
++
++ if (of_property_read_u32(np, "dma-channels", &nr_channels))
++ return NULL;
++
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+- if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
+- return NULL;
++ pdata->nr_masters = nr_masters;
++ pdata->nr_channels = nr_channels;
+
+ if (of_property_read_bool(np, "is_private"))
+ pdata->is_private = true;
+@@ -128,17 +138,13 @@ dw_dma_parse_dt(struct platform_device *
+ if (!of_property_read_u32(np, "block_size", &tmp))
+ pdata->block_size = tmp;
+
+- if (!of_property_read_u32(np, "dma-masters", &tmp)) {
+- if (tmp > DW_DMA_MAX_NR_MASTERS)
+- 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++)
++ if (!of_property_read_u32_array(np, "data-width", arr, nr_masters)) {
++ for (tmp = 0; tmp < nr_masters; tmp++)
+ pdata->data_width[tmp] = arr[tmp];
++ } else if (!of_property_read_u32_array(np, "data_width", arr, nr_masters)) {
++ for (tmp = 0; tmp < nr_masters; tmp++)
++ pdata->data_width[tmp] = BIT(arr[tmp] & 0x07);
++ }
+
+ return pdata;
+ }
+@@ -155,8 +161,7 @@ static int dw_probe(struct platform_devi
+ struct dw_dma_chip *chip;
+ struct device *dev = &pdev->dev;
+ struct resource *mem;
+- const struct acpi_device_id *id;
+- struct dw_dma_platform_data *pdata;
++ const struct dw_dma_platform_data *pdata;
+ int err;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+@@ -179,13 +184,9 @@ static int dw_probe(struct platform_devi
+ pdata = dev_get_platdata(dev);
+ if (!pdata)
+ pdata = dw_dma_parse_dt(pdev);
+- if (!pdata && has_acpi_companion(dev)) {
+- id = acpi_match_device(dev->driver->acpi_match_table, dev);
+- if (id)
+- pdata = (struct dw_dma_platform_data *)id->driver_data;
+- }
+
+ chip->dev = dev;
++ chip->pdata = pdata;
+
+ chip->clk = devm_clk_get(chip->dev, "hclk");
+ if (IS_ERR(chip->clk))
+@@ -196,7 +197,7 @@ static int dw_probe(struct platform_devi
+
+ pm_runtime_enable(&pdev->dev);
+
+- err = dw_dma_probe(chip, pdata);
++ err = dw_dma_probe(chip);
+ if (err)
+ goto err_dw_dma_probe;
+
+@@ -239,7 +240,19 @@ static void dw_shutdown(struct platform_
+ {
+ struct dw_dma_chip *chip = platform_get_drvdata(pdev);
+
++ /*
++ * We have to call dw_dma_disable() to stop any ongoing transfer. On
++ * some platforms we can't do that since DMA device is powered off.
++ * Moreover we have no possibility to check if the platform is affected
++ * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
++ * unconditionally. On the other hand we can't use
++ * pm_runtime_suspended() because runtime PM framework is not fully
++ * used by the driver.
++ */
++ pm_runtime_get_sync(chip->dev);
+ dw_dma_disable(chip);
++ pm_runtime_put_sync_suspend(chip->dev);
++
+ clk_disable_unprepare(chip->clk);
+ }
+
+@@ -252,17 +265,8 @@ MODULE_DEVICE_TABLE(of, dw_dma_of_id_tab
+ #endif
+
+ #ifdef CONFIG_ACPI
+-static struct dw_dma_platform_data dw_dma_acpi_pdata = {
+- .nr_channels = 8,
+- .is_private = true,
+- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+- .chan_priority = CHAN_PRIORITY_ASCENDING,
+- .block_size = 4095,
+- .nr_masters = 2,
+-};
+-
+ static const struct acpi_device_id dw_dma_acpi_id_table[] = {
+- { "INTL9C60", (kernel_ulong_t)&dw_dma_acpi_pdata },
++ { "INTL9C60", 0 },
+ { }
+ };
+ MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
+--- a/drivers/dma/dw/regs.h 2016-05-21 23:13:19.964478443 +0200
++++ b/drivers/dma/dw/regs.h 2016-05-21 22:47:08.665465180 +0200
+@@ -114,10 +114,6 @@ struct dw_dma_regs {
+ #define dma_writel_native writel
+ #endif
+
+-/* To access the registers in early stage of probe */
+-#define dma_read_byaddr(addr, name) \
+- dma_readl_native((addr) + offsetof(struct dw_dma_regs, name))
+-
+ /* Bitfields in DW_PARAMS */
+ #define DW_PARAMS_NR_CHAN 8 /* number of channels */
+ #define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */
+@@ -143,6 +139,10 @@ enum dw_dma_msize {
+ DW_DMA_MSIZE_256,
+ };
+
++/* Bitfields in LLP */
++#define DWC_LLP_LMS(x) ((x) & 3) /* list master select */
++#define DWC_LLP_LOC(x) ((x) & ~3) /* next lli */
++
+ /* Bitfields in CTL_LO */
+ #define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */
+ #define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */
+@@ -150,7 +150,7 @@ enum dw_dma_msize {
+ #define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */
+ #define DWC_CTLL_DST_DEC (1<<7)
+ #define DWC_CTLL_DST_FIX (2<<7)
+-#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */
++#define DWC_CTLL_SRC_INC (0<<9) /* SAR update/not */
+ #define DWC_CTLL_SRC_DEC (1<<9)
+ #define DWC_CTLL_SRC_FIX (2<<9)
+ #define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */
+@@ -216,6 +216,8 @@ enum dw_dma_msize {
+ enum dw_dmac_flags {
+ DW_DMA_IS_CYCLIC = 0,
+ DW_DMA_IS_SOFT_LLP = 1,
++ DW_DMA_IS_PAUSED = 2,
++ DW_DMA_IS_INITIALIZED = 3,
+ };
+
+ struct dw_dma_chan {
+@@ -224,8 +226,6 @@ struct dw_dma_chan {
+ u8 mask;
+ u8 priority;
+ enum dma_transfer_direction direction;
+- bool paused;
+- bool initialized;
+
+ /* software emulation of the LLP transfers */
+ struct list_head *tx_node_active;
+@@ -236,8 +236,6 @@ struct dw_dma_chan {
+ unsigned long flags;
+ 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;
+@@ -249,8 +247,8 @@ struct dw_dma_chan {
+ /* custom slave configuration */
+ u8 src_id;
+ u8 dst_id;
+- u8 src_master;
+- u8 dst_master;
++ u8 m_master;
++ u8 p_master;
+
+ /* configuration passed via .device_config */
+ struct dma_slave_config dma_sconfig;
+@@ -283,9 +281,8 @@ struct dw_dma {
+ u8 all_chan_mask;
+ u8 in_use;
+
+- /* hardware configuration */
+- unsigned char nr_masters;
+- unsigned char data_width[DW_DMA_MAX_NR_MASTERS];
++ /* platform data */
++ struct dw_dma_platform_data *pdata;
+ };
+
+ static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw)
+@@ -308,32 +305,51 @@ static inline struct dw_dma *to_dw_dma(s
+ return container_of(ddev, struct dw_dma, dma);
+ }
+
++#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
++typedef __be32 __dw32;
++#else
++typedef __le32 __dw32;
++#endif
++
+ /* LLI == Linked List Item; a.k.a. DMA block descriptor */
+ struct dw_lli {
+ /* values that are not changed by hardware */
+- u32 sar;
+- u32 dar;
+- u32 llp; /* chain to next lli */
+- u32 ctllo;
++ __dw32 sar;
++ __dw32 dar;
++ __dw32 llp; /* chain to next lli */
++ __dw32 ctllo;
+ /* values that may get written back: */
+- u32 ctlhi;
++ __dw32 ctlhi;
+ /* sstat and dstat can snapshot peripheral register state.
+ * silicon config may discard either or both...
+ */
+- u32 sstat;
+- u32 dstat;
++ __dw32 sstat;
++ __dw32 dstat;
+ };
+
+ struct dw_desc {
+ /* FIRST values the hardware uses */
+ struct dw_lli lli;
+
++#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
++#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_be32(v))
++#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_be32(v))
++#define lli_read(d, reg) be32_to_cpu((d)->lli.reg)
++#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_be32(v))
++#else
++#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_le32(v))
++#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_le32(v))
++#define lli_read(d, reg) le32_to_cpu((d)->lli.reg)
++#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_le32(v))
++#endif
++
+ /* THEN values for driver housekeeping */
+ struct list_head desc_node;
+ struct list_head tx_list;
+ struct dma_async_tx_descriptor txd;
+ size_t len;
+ size_t total_len;
++ u32 residue;
+ };
+
+ #define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node)
+--- a/include/linux/dma/dw.h
++++ b/include/linux/dma/dw.h
+@@ -27,6 +27,7 @@ struct dw_dma;
+ * @regs: memory mapped I/O space
+ * @clk: hclk clock
+ * @dw: struct dw_dma that is filed by dw_dma_probe()
++ * @pdata: pointer to platform data
+ */
+ struct dw_dma_chip {
+ struct device *dev;
+@@ -34,10 +35,12 @@ struct dw_dma_chip {
+ void __iomem *regs;
+ struct clk *clk;
+ struct dw_dma *dw;
++
++ const struct dw_dma_platform_data *pdata;
+ };
+
+ /* Export to the platform drivers */
+-int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata);
++int dw_dma_probe(struct dw_dma_chip *chip);
+ int dw_dma_remove(struct dw_dma_chip *chip);
+
+ /* DMA API extensions */
+diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h
+index 03b6095..d15d8ba 100644
+--- a/include/linux/platform_data/dma-dw.h
++++ b/include/linux/platform_data/dma-dw.h
+@@ -21,15 +21,15 @@
+ * @dma_dev: required DMA master device
+ * @src_id: src request line
+ * @dst_id: dst request line
+- * @src_master: src master for transfers on allocated channel.
+- * @dst_master: dest master for transfers on allocated channel.
++ * @m_master: memory master for transfers on allocated channel
++ * @p_master: peripheral master for transfers on allocated channel
+ */
+ struct dw_dma_slave {
+ struct device *dma_dev;
+ u8 src_id;
+ u8 dst_id;
+- u8 src_master;
+- u8 dst_master;
++ u8 m_master;
++ u8 p_master;
+ };
+
+ /**
+@@ -43,7 +43,7 @@ struct dw_dma_slave {
+ * @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)
++ * (in bytes, power of 2)
+ */
+ struct dw_dma_platform_data {
+ unsigned int nr_channels;
+@@ -55,7 +55,7 @@ struct dw_dma_platform_data {
+ #define CHAN_PRIORITY_ASCENDING 0 /* chan0 highest */
+ #define CHAN_PRIORITY_DESCENDING 1 /* chan7 highest */
+ unsigned char chan_priority;
+- unsigned short block_size;
++ unsigned int block_size;
+ unsigned char nr_masters;
+ unsigned char data_width[DW_DMA_MAX_NR_MASTERS];
+ };
+--
+2.8.1
+