DMAEngine support for sun4i, sun5i & sun7i
Subsystems (4)
v3 cover DMAEngine support for sun4i, sun5i & sun7i
Hi everyone,
As part of Google Summer of Code, I've tasked myself with implementing DMA support for the earlier Allwinner platforms. This third round of patches is the result of said effort.
The first patch is the actual driver to support these platforms. Patches three, four and five add the corresponding DMA node to the sun4i, sun5i and sun7i device trees.
The second patch, involving the sunxi SPI driver, lets users do SPI transfers of >=64 bytes by using DMA. Patches six, seven and eight add the DMA properties to SPI so the first two patches can be used.
All comments and reviews are welcome :)
Thanks!
Emilio
Emilio L?pez (8):
dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
spi: sun4i: add DMA support
ARM: sun4i: dt: Add node to represent the DMA controller
ARM: sun5i: dt: Add nodes to represent the DMA controllers
ARM: sun7i: dt: Add node to represent the DMA controller
ARM: sun4i: dt: enable DMA on SPI
ARM: sun5i: dt: enable DMA on SPI
ARM: sun7i: dt: enable DMA on SPI
.../devicetree/bindings/dma/sun4i-dma.txt | 46 + arch/arm/boot/dts/sun4i-a10.dtsi | 16 + arch/arm/boot/dts/sun5i-a10s.dtsi | 14 + arch/arm/boot/dts/sun5i-a13.dtsi | 14 + arch/arm/boot/dts/sun7i-a20.dtsi | 16 + drivers/dma/Kconfig | 11 + drivers/dma/Makefile | 1 + drivers/dma/sun4i-dma.c | 1238 ++++++++++++++++++++ drivers/spi/spi-sun4i.c | 155 ++- 9 files changed, 1500 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/sun4i-dma.txt create mode 100644 drivers/dma/sun4i-dma.c
Signature · 1 line
2.0.4
PATCH v3 1/8 dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
This patch adds support for the DMA engine present on Allwinner A10, A13, A10S and A20 SoCs. This engine has two kinds of channels: normal and dedicated. The main difference is in the mode of operation; while a single normal channel may be operating at any given time, dedicated channels may operate simultaneously provided there is no overlap of source or destination.
Hardware documentation can be found on A10 User Manual (section 12), A13 User Manual (section 14) and A20 User Manual (section 1.12)
---
Changes from v2:
* Faster memcpy
* Quicker cyclic transfers
* Address some stylistic and locking comments from Maxime
* probably some more stuff I'm forgetting
Changes from v1:
* address comments from Chen-Yu and Maxime
* fix issue converting bus width
* switch to using a threaded IRQ instead of a tasklet on recommendation from Maxime
* fix issue setting magic timing parameter for SPI transfers
* fix an issue with list handling reported by the kbuild 0-DAY robot (thanks!)
* drop a lot of unused #define
* probably some more stuff I'm forgetting
.../devicetree/bindings/dma/sun4i-dma.txt | 46 + drivers/dma/Kconfig | 11 + drivers/dma/Makefile | 1 + drivers/dma/sun4i-dma.c | 1238 ++++++++++++++++++++ 4 files changed, 1296 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/sun4i-dma.txt create mode 100644 drivers/dma/sun4i-dma.c
Documentation/devicetree/bindings/dma/sun4i-dma.txt new file +46 -0
@@ -0,0 +1,46 @@ +Allwinner A10 DMA Controller + +This driver follows the generic DMA bindings defined in dma.txt. + +Required properties: + +- compatible: Must be "allwinner,sun4i-a10-dma" +- reg: Should contain the registers base address and length +- interrupts: Should contain a reference to the interrupt used by this device +- clocks: Should contain a reference to the parent AHB clock +- #dma-cells : Should be 2, first cell denoting normal or dedicated dma, + second cell holding the request line number. + +Example: + dma: dma-controller at 01c02000 { + compatible = "allwinner,sun4i-a10-dma"; + reg = <0x01c02000 0x1000>; + interrupts = <27>; + clocks = <&ahb_gates 6>; + #dma-cells = <2>; + }; + +Clients: + +DMA clients connected to the Allwinner A10 DMA controller must use the +format described in the dma.txt file, using a three-cell specifier for +each channel: a phandle plus two integer cells. +The three cells in order are: + +1. A phandle pointing to the DMA controller. +2. Whether it is using normal (0) or dedicated (1) channels +3. The port ID as specified in the datasheet + +Example: + spi2: spi at 01c17000 { + compatible = "allwinner,sun4i-a10-spi"; + reg = <0x01c17000 0x1000>; + interrupts = <0 12 4>; + clocks = <&ahb_gates 22>, <&spi2_clk>; + clock-names = "ahb", "mod"; + dmas = <&dma 1 29>, <&dma 1 28>; + dma-names = "rx", "tx"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + };
drivers/dma/Kconfig +11 -0
@@ -375,6 +375,17 @@ config XILINX_VDMA channels, Memory Mapped to Stream (MM2S) and Stream to Memory Mapped (S2MM) for the data transfers. +config SUN4I_DMA + tristate "Allwinner A10 DMA support" + depends on (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || (COMPILE_TEST && OF && ARM)) + default (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I) + select DMA_ENGINE + select DMA_OF + select DMA_VIRTUAL_CHANNELS + help + Enable support for the DMA controller present in the sun4i, + sun5i and sun7i Allwinner ARM SoCs. + config DMA_ENGINE bool
drivers/dma/Makefile +1 -0
@@ -47,3 +47,4 @@ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o obj-y += xilinx/ +obj-$(CONFIG_SUN4I_DMA) += sun4i-dma.o
drivers/dma/sun4i-dma.c new file +1238 -0
1,238 lines not on this page · raw message
Signature · 1 line
2.0.4
PATCH v3 1/8 dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
Hi,
Overall it looks very nice, thanks.
Quoted 15 lines · Emilio L?pez
drivers/dma/sun4i-dma.c new file +15
-0+static struct sun4i_dma_pchan *find_and_use_pchan (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_pchan *pchan =NULL , *pchans = priv->pchans; +unsigned long flags; +int i, max; + +/* +* pchans 0-NDMA_NR_MAX_CHANNELS are normal, and +* NDMA_NR_MAX_CHANNELS+ are dedicated ones +*/ +if (vchan->is_dedicated) { + i =NDMA_NR_MAX_CHANNELS ; + max =DMA_NR_MAX_CHANNELS ; + }else {
drivers/dma/sun4i-dma.c line 262
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ i =0 ; + max =NDMA_NR_MAX_CHANNELS ; + } + +spin_lock_irqsave (&priv->lock, flags); +for_each_clear_bit_from (i, &priv->pchans_used, max) { + pchan = &pchans[i]; + pchan->vchan = vchan; +set_bit (i, priv->pchans_used); +break ;
ffz instead?
drivers/dma/sun4i-dma.c line 340
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+static int execute_vchan_pending (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_promise *promise =NULL ; +struct sun4i_dma_contract *contract =NULL ; +struct sun4i_dma_pchan *pchan; +struct virt_dma_desc *vd; +int ret; + +lockdep_assert_held (&vchan->vc.lock);
So this has to be called with a lock taken?
You should mention that somewhere.
Usually, such fonctions are also prefixed by "__"
Quoted 24 lines
drivers/dma/sun4i-dma.c new file +24
-0+/** +* Generate a promise, to be used in a normal DMA contract. +* +* A NDMA promise contains all the information required to program the +* normal part of the DMA Engine and get data copied. A non-executed +* promise will live in the demands list on a contract. Once it has been +* completed, it will be moved to the completed demands list for later freeing. +* All linked promises will be freed when the corresponding contract is freed +*/ +static struct sun4i_dma_promise * +generate_ndma_promise (struct dma_chan *chan,dma_addr_t src,dma_addr_t dest, +size_t len,struct dma_slave_config *sconfig) +{ +struct sun4i_dma_promise *promise; +int ret; + + promise =kzalloc (sizeof (*promise),GFP_NOWAIT ); +if (!promise) +return NULL ; + + promise->src = src; + promise->dst = dest; + promise->len = len; + promise->cfg =NDMA_CFG_LOADING |NDMA_CFG_BYTE_COUNT_MODE_REMAIN ;
drivers/dma/sun4i-dma.c line 431
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +/* Use sensible default values if client is using undefined ones */ +if (sconfig->src_addr_width ==DMA_SLAVE_BUSWIDTH_UNDEFINED ) + sconfig->src_addr_width = sconfig->dst_addr_width; +if (sconfig->dst_addr_width ==DMA_SLAVE_BUSWIDTH_UNDEFINED ) + sconfig->dst_addr_width = sconfig->src_addr_width; +if (sconfig->src_maxburst ==0 ) + sconfig->src_maxburst = sconfig->dst_maxburst; +if (sconfig->dst_maxburst ==0 ) + sconfig->dst_maxburst = sconfig->src_maxburst;
I'm not sure what's the default policy on that. Vinod?
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+static irqreturn_t sun4i_dma_submit_work (int irq,void *dev_id) +{ +struct sun4i_dma_dev *priv = dev_id; +struct sun4i_dma_vchan *vchan; +unsigned long flags; +int i;
drivers/dma/sun4i-dma.c line 1067
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +for (i =0 ; i <DMA_NR_MAX_VCHANS ; i++) { + vchan = &priv->vchans[i]; +spin_lock_irqsave (&vchan->vc.lock, flags); +execute_vchan_pending (priv, vchan); +spin_unlock_irqrestore (&vchan->vc.lock, flags); + } + +return IRQ_HANDLED ; +}
Judging from Russell's comment here, that should be in the interrupt handler
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-August/277737.html
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140805/cf899a3b/attachment.sig>
PATCH v3 1/8 dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
Hi,
El 05/08/14 a las 17:00, Maxime Ripard escibi?:
Quoted 24 lines
Hi,
Overall it looks very nice, thanks.
Quoted 19 lines · Emilio L?pez
drivers/dma/sun4i-dma.c new file +19
-0+static struct sun4i_dma_pchan *find_and_use_pchan (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_pchan *pchan =NULL , *pchans = priv->pchans; +unsigned long flags; +int i, max; + +/* +* pchans 0-NDMA_NR_MAX_CHANNELS are normal, and +* NDMA_NR_MAX_CHANNELS+ are dedicated ones +*/ +if (vchan->is_dedicated) { + i =NDMA_NR_MAX_CHANNELS ; + max =DMA_NR_MAX_CHANNELS ; + }else { + i =0 ; + max =NDMA_NR_MAX_CHANNELS ; + } +
Quoted 8 lines
drivers/dma/sun4i-dma.c line 262
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+spin_lock_irqsave (&priv->lock, flags); +for_each_clear_bit_from (i, &priv->pchans_used, max) { + pchan = &pchans[i]; + pchan->vchan = vchan; +set_bit (i, priv->pchans_used); +break ;ffz instead?
I think it'd look way more messy with ffz, as it only works on unsigned longs and it's undefined on the all-1s case.
We could have something like this, but I don't see much point in what's basically expanding the macro
spin_lock_irqsave(&priv->lock, flags);
i = find_next_zero_bit(&priv->pchans_used, max, i);
if (i < max) {
pchan = &pchans[i];
pchan->vchan = vchan;
set_bit(i, priv->pchans_used);
}
spin_unlock_irqrestore(&priv->lock, flags);Quoted 13 lines
drivers/dma/sun4i-dma.c line 340
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+static int execute_vchan_pending (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_promise *promise =NULL ; +struct sun4i_dma_contract *contract =NULL ; +struct sun4i_dma_pchan *pchan; +struct virt_dma_desc *vd; +int ret; + +lockdep_assert_held (&vchan->vc.lock);So this has to be called with a lock taken?
Quoted 2 lines
You should mention that somewhere.
Usually, such fonctions are also prefixed by "__"
Ok, I added a comment and the prefix now.
Quoted 57 lines
Quoted 24 lines
drivers/dma/sun4i-dma.c new file +24
-0+/** +* Generate a promise, to be used in a normal DMA contract. +* +* A NDMA promise contains all the information required to program the +* normal part of the DMA Engine and get data copied. A non-executed +* promise will live in the demands list on a contract. Once it has been +* completed, it will be moved to the completed demands list for later freeing. +* All linked promises will be freed when the corresponding contract is freed +*/ +static struct sun4i_dma_promise * +generate_ndma_promise (struct dma_chan *chan,dma_addr_t src,dma_addr_t dest, +size_t len,struct dma_slave_config *sconfig) +{ +struct sun4i_dma_promise *promise; +int ret; + + promise =kzalloc (sizeof (*promise),GFP_NOWAIT ); +if (!promise) +return NULL ; + + promise->src = src; + promise->dst = dest; + promise->len = len; + promise->cfg =NDMA_CFG_LOADING |NDMA_CFG_BYTE_COUNT_MODE_REMAIN ;drivers/dma/sun4i-dma.c line 431
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +/* Use sensible default values if client is using undefined ones */ +if (sconfig->src_addr_width ==DMA_SLAVE_BUSWIDTH_UNDEFINED ) + sconfig->src_addr_width = sconfig->dst_addr_width; +if (sconfig->dst_addr_width ==DMA_SLAVE_BUSWIDTH_UNDEFINED ) + sconfig->dst_addr_width = sconfig->src_addr_width; +if (sconfig->src_maxburst ==0 ) + sconfig->src_maxburst = sconfig->dst_maxburst; +if (sconfig->dst_maxburst ==0 ) + sconfig->dst_maxburst = sconfig->src_maxburst;I'm not sure what's the default policy on that. Vinod?
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+static irqreturn_t sun4i_dma_submit_work (int irq,void *dev_id) +{ +struct sun4i_dma_dev *priv = dev_id; +struct sun4i_dma_vchan *vchan; +unsigned long flags; +int i;drivers/dma/sun4i-dma.c line 1067
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +for (i =0 ; i <DMA_NR_MAX_VCHANS ; i++) { + vchan = &priv->vchans[i]; +spin_lock_irqsave (&vchan->vc.lock, flags); +execute_vchan_pending (priv, vchan); +spin_unlock_irqrestore (&vchan->vc.lock, flags); + } + +return IRQ_HANDLED ; +}Judging from Russell's comment here, that should be in the interrupt handler
Wouldn't that make the interrupt handler quite big time wise? I was under the impression they should remain as short as possible. LDD3 says "The programmer should be careful to write a routine that executes in a minimum amount of time (...)" on chapter 10
I did try something mid-way a bit ago; I added code to see if we could issue the next DMA operation from the current set, but it was just extra complexity for a noop in performance, as they're sets of one on all cases I tried.
It's worth noting that the very latency sensitive DMA users we will have (ie, audio) are already completely managed in the IRQ handler thanks to the less flexible concept of cyclic transfers.
Cheers,
Emilio
PATCH v3 1/8 dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
Quoted 152 lines · Emilio L?pez
Hi,
El 05/08/14 a las 17:00, Maxime Ripard escibi?:
Quoted 28 lines
Hi,
Overall it looks very nice, thanks.
Quoted 23 lines · Emilio L?pez
+static struct
sun4i_dma_pchan*find_and_use_pchan(structsun4i_dma_dev*priv,
+ structsun4i_dma_vchan
*vchan)drivers/dma/sun4i-dma.c new file +18
-0+{ +struct sun4i_dma_pchan *pchan =NULL , *pchans = priv->pchans; +unsigned long flags; +int i, max; + +/* +* pchans 0-NDMA_NR_MAX_CHANNELS are normal, and +* NDMA_NR_MAX_CHANNELS+ are dedicated ones +*/ +if (vchan->is_dedicated) { + i =NDMA_NR_MAX_CHANNELS ; + max =DMA_NR_MAX_CHANNELS ; + }else { + i =0 ; + max =NDMA_NR_MAX_CHANNELS ; + } + +spin_lock_irqsave (&priv->lock, flags);Quoted 8 lines
drivers/dma/sun4i-dma.c line 262
Quoted 5 lines
drivers/dma/sun4i-dma.c new file +5
-0+for_each_clear_bit_from (i, &priv->pchans_used, max) { + pchan = &pchans[i]; + pchan->vchan = vchan; +set_bit (i, priv->pchans_used); +break ;ffz instead?
I think it'd look way more messy with ffz, as it only works on unsigned longs and it's undefined on the all-1s case.
We could have something like this, but I don't see much point in what's basically expanding the macro
spin_lock_irqsave(&priv->lock, flags); i = find_next_zero_bit(&priv->pchans_used, max, i); if (i < max) { pchan = &pchans[i]; pchan->vchan = vchan; set_bit(i, priv->pchans_used); } spin_unlock_irqrestore(&priv->lock, flags);Quoted 14 lines
drivers/dma/sun4i-dma.c line 340
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+static int execute_vchan_pending (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_promise *promise =NULL ; +struct sun4i_dma_contract *contract =NULL ; +struct sun4i_dma_pchan *pchan; +struct virt_dma_desc *vd; +int ret; + +lockdep_assert_held (&vchan->vc.lock);So this has to be called with a lock taken?
Quoted 2 lines
You should mention that somewhere.
Usually, such fonctions are also prefixed by "__"Ok, I added a comment and the prefix now.
Quoted 65 lines
Quoted 31 lines
+/**
+ * Generate a promise, to be used in a normal DMA contract.
+ *
+ * A NDMA promise contains all the information required to program the
+ * normal part of the DMA Engine and get data copied. A non-executed
+ * promise will live in the demands list on a contract. Once it has been
+ * completed, it will be moved to the completed demands list for later freeing.
+ * All linked promises will be freed when the corresponding contract is freed
+ */(quoted diff) +2
-0+static struct sun4i_dma_promise * +generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_tdest,
drivers/dma/sun4i-dma.c new file +13
-0+ size_t len, struct dma_slave_config *sconfig) +{ + struct sun4i_dma_promise *promise; +int ret; + + promise =kzalloc (sizeof (*promise),GFP_NOWAIT ); +if (!promise) +return NULL ; + + promise->src = src; + promise->dst = dest; + promise->len = len; + promise->cfg = NDMA_CFG_LOADING |
NDMA_CFG_BYTE_COUNT_MODE_REMAIN;(quoted diff) +2
-0+ + /* Use sensible default values if client is using undefined ones*/
drivers/dma/sun4i-dma.c line 431
Quoted 8 lines
drivers/dma/sun4i-dma.c new file +8
-0+if (sconfig->src_addr_width ==DMA_SLAVE_BUSWIDTH_UNDEFINED ) + sconfig->src_addr_width = sconfig->dst_addr_width; +if (sconfig->dst_addr_width ==DMA_SLAVE_BUSWIDTH_UNDEFINED ) + sconfig->dst_addr_width = sconfig->src_addr_width; +if (sconfig->src_maxburst ==0 ) + sconfig->src_maxburst = sconfig->dst_maxburst; +if (sconfig->dst_maxburst ==0 ) + sconfig->dst_maxburst = sconfig->src_maxburst;I'm not sure what's the default policy on that. Vinod?
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+static irqreturn_t sun4i_dma_submit_work (int irq,void *dev_id) +{ +struct sun4i_dma_dev *priv = dev_id; +struct sun4i_dma_vchan *vchan; +unsigned long flags; +int i;drivers/dma/sun4i-dma.c line 1067
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +for (i =0 ; i <DMA_NR_MAX_VCHANS ; i++) { + vchan = &priv->vchans[i]; +spin_lock_irqsave (&vchan->vc.lock, flags); +execute_vchan_pending (priv, vchan); +spin_unlock_irqrestore (&vchan->vc.lock, flags); + } + +return IRQ_HANDLED ; +}Judging from Russell's comment here, that should be in the interrupt handler
Wouldn't that make the interrupt handler quite big time wise? I was under the impression they should remain as short as possible. LDD3 says "The programmer should be careful to write a routine that executes in a minimum amount of time (...)" on chapter 10
Quoted 8 lines
I did try something mid-way a bit ago; I added code to see if we could issue the next DMA operation from the current set, but it was just extra complexity for a noop in performance, as they're sets of one on all cases I tried.
It's worth noting that the very latency sensitive DMA users we will have (ie, audio) are already completely managed in the IRQ handler thanks to the less flexible concept of cyclic transfers.
Other things are latency sensitive like ADC inputs. Plus adding latency lowers the throughput of SPI and whoever else is using the channels.
Running that code is probably just a few microseconds, not enough to mess up overall system latency. Things that mess up system latency are in the millisecond range.
Another thing I do in my IRQ handlers is re-read the interrupt status register right before returning. Then loop if any new IRQs have
asserted. The network drivers all do this, they call it IRQ
mitigation. Consider multiple Gbe interfaces getting hit by billions of tiny packets. But doing IRQ mitigation you can eliminate millions of pointless interrupts.
Quoted 10 lines
Cheers,
Emilio
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Signature · 2 lines
Jon Smirl jonsmirl at gmail.com
PATCH v3 1/8 dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
In reply to Emilio LópezQuoted 42 lines · Emilio L?pez
Hi,
El 05/08/14 a las 17:00, Maxime Ripard escibi?:
Quoted 24 lines
Hi,
Overall it looks very nice, thanks.
Quoted 19 lines · Emilio L?pez
drivers/dma/sun4i-dma.c new file +19
-0+static struct sun4i_dma_pchan *find_and_use_pchan (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_pchan *pchan =NULL , *pchans = priv->pchans; +unsigned long flags; +int i, max; + +/* +* pchans 0-NDMA_NR_MAX_CHANNELS are normal, and +* NDMA_NR_MAX_CHANNELS+ are dedicated ones +*/ +if (vchan->is_dedicated) { + i =NDMA_NR_MAX_CHANNELS ; + max =DMA_NR_MAX_CHANNELS ; + }else { + i =0 ; + max =NDMA_NR_MAX_CHANNELS ; + } +Quoted 8 lines
drivers/dma/sun4i-dma.c line 262
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+spin_lock_irqsave (&priv->lock, flags); +for_each_clear_bit_from (i, &priv->pchans_used, max) { + pchan = &pchans[i]; + pchan->vchan = vchan; +set_bit (i, priv->pchans_used); +break ;ffz instead?
I think it'd look way more messy with ffz, as it only works on unsigned longs and it's undefined on the all-1s case.
We could have something like this, but I don't see much point in what's basically expanding the macro
Quoted 8 lines
spin_lock_irqsave(&priv->lock, flags);
i = find_next_zero_bit(&priv->pchans_used, max, i);
if (i < max) {
pchan = &pchans[i];
pchan->vchan = vchan;
set_bit(i, priv->pchans_used);
}
spin_unlock_irqrestore(&priv->lock, flags);What about
spin_lock_irqsave(&priv->lock, flags);
i = ffz(&priv->pchans_used);
if (!i)
return NULL;
pchan = &pchans[i];pchan->vchan = vchan;set_bit(i, priv->pchans_used);
spin_unlock_irqrestore(&priv->lock, flags);
It looks quite clean to me.
Quoted 20 lines
Quoted 20 lines
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+static irqreturn_t sun4i_dma_submit_work (int irq,void *dev_id) +{ +struct sun4i_dma_dev *priv = dev_id; +struct sun4i_dma_vchan *vchan; +unsigned long flags; +int i;drivers/dma/sun4i-dma.c line 1067
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +for (i =0 ; i <DMA_NR_MAX_VCHANS ; i++) { + vchan = &priv->vchans[i]; +spin_lock_irqsave (&vchan->vc.lock, flags); +execute_vchan_pending (priv, vchan); +spin_unlock_irqrestore (&vchan->vc.lock, flags); + } + +return IRQ_HANDLED ; +}Judging from Russell's comment here, that should be in the interrupt handler
Quoted 6 lines
Wouldn't that make the interrupt handler quite big time wise? I was under the impression they should remain as short as possible. LDD3 says "The programmer should be careful to write a routine that executes in a minimum amount of time (...)" on chapter 10
Yeah, but if that involves delaying a lot a task that should have been performed asap, it would make sense.
Plus, besides the loop in the contract's demands, execute_vchan_pending is actually rather small. Did you timed it to have such concerns?
Quoted 8 lines
I did try something mid-way a bit ago; I added code to see if we could issue the next DMA operation from the current set, but it was just extra complexity for a noop in performance, as they're sets of one on all cases I tried.
It's worth noting that the very latency sensitive DMA users we will have (ie, audio) are already completely managed in the IRQ handler thanks to the less flexible concept of cyclic transfers.
It's not just a matter of latency, but also of throughput. If you wait for the tasklet softirq to be executed to schedule a new vchan, you aren't going to do anything during that time, while you could have.
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140811/4803c8aa/attachment-0001.sig>
PATCH v3 1/8 dma: sun4i: Add support for the DMA engine on sun[457]i SoCs
Quoted 106 lines · Maxime Ripard
Quoted 42 lines · Emilio L?pez
Hi,
El 05/08/14 a las 17:00, Maxime Ripard escibi?:
Quoted 24 lines
Hi,
Overall it looks very nice, thanks.
Quoted 19 lines · Emilio L?pez
drivers/dma/sun4i-dma.c new file +19
-0+static struct sun4i_dma_pchan *find_and_use_pchan (struct sun4i_dma_dev *priv, +struct sun4i_dma_vchan *vchan) +{ +struct sun4i_dma_pchan *pchan =NULL , *pchans = priv->pchans; +unsigned long flags; +int i, max; + +/* +* pchans 0-NDMA_NR_MAX_CHANNELS are normal, and +* NDMA_NR_MAX_CHANNELS+ are dedicated ones +*/ +if (vchan->is_dedicated) { + i =NDMA_NR_MAX_CHANNELS ; + max =DMA_NR_MAX_CHANNELS ; + }else { + i =0 ; + max =NDMA_NR_MAX_CHANNELS ; + } +Quoted 8 lines
drivers/dma/sun4i-dma.c line 262
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+spin_lock_irqsave (&priv->lock, flags); +for_each_clear_bit_from (i, &priv->pchans_used, max) { + pchan = &pchans[i]; + pchan->vchan = vchan; +set_bit (i, priv->pchans_used); +break ;ffz instead?
I think it'd look way more messy with ffz, as it only works on unsigned longs and it's undefined on the all-1s case.
We could have something like this, but I don't see much point in what's basically expanding the macro
Quoted 8 lines
spin_lock_irqsave(&priv->lock, flags); i = find_next_zero_bit(&priv->pchans_used, max, i); if (i < max) { pchan = &pchans[i]; pchan->vchan = vchan; set_bit(i, priv->pchans_used); } spin_unlock_irqrestore(&priv->lock, flags);What about
spin_lock_irqsave(&priv->lock, flags);
i =ffz(&priv->pchans_used);
if (!i)
return NULL;pchan = &pchans[i];
pchan->vchan= vchan;set_bit(i, priv->pchans_used);
spin_unlock_irqrestore(&priv->lock, flags);It looks quite clean to me.
Quoted 20 lines
Quoted 20 lines
Quoted 6 lines
drivers/dma/sun4i-dma.c new file +6
-0+static irqreturn_t sun4i_dma_submit_work (int irq,void *dev_id) +{ +struct sun4i_dma_dev *priv = dev_id; +struct sun4i_dma_vchan *vchan; +unsigned long flags; +int i;drivers/dma/sun4i-dma.c line 1067
Quoted 10 lines
drivers/dma/sun4i-dma.c new file +10
-0+ +for (i =0 ; i <DMA_NR_MAX_VCHANS ; i++) { + vchan = &priv->vchans[i]; +spin_lock_irqsave (&vchan->vc.lock, flags); +execute_vchan_pending (priv, vchan); +spin_unlock_irqrestore (&vchan->vc.lock, flags); + } + +return IRQ_HANDLED ; +}Judging from Russell's comment here, that should be in the interrupt handler
Quoted 6 lines
Wouldn't that make the interrupt handler quite big time wise? I was under the impression they should remain as short as possible. LDD3 says "The programmer should be careful to write a routine that executes in a minimum amount of time (...)" on chapter 10
Yeah, but if that involves delaying a lot a task that should have been performed asap, it would make sense.
Plus, besides the loop in the contract's demands,
execute_vchan_pendingis actually rather small. Did you timed it to have such concerns?Quoted 5 lines
I did try something mid-way a bit ago; I added code to see if we could issue the next DMA operation from the current set, but it was just extra complexity for a noop in performance, as they're sets of one on all cases I tried.
Quoted 7 lines
Quoted 3 lines
It's worth noting that the very latency sensitive DMA users we will have (ie, audio) are already completely managed in the IRQ handler thanks to the less flexible concept of cyclic transfers.
It's not just a matter of latency, but also of throughput. If you wait for the tasklet softirq to be executed to schedule a new vchan, you aren't going to do anything during that time, while you could have.
This throughput issue is an important point. It can be so important that I have worked on some CPUs that address it in hardware by having shadow DMA registers. You pre-load the shadow register set and then the DMA engine can instantly go onto the next request in hardware.
For example if the transport stream (video input) hardware on this CPU is turned on it is going to need high throughput DMA.
Quoted 13 lines
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Signature · 2 lines
Jon Smirl jonsmirl at gmail.com
PATCH v3 2/8 spi: sun4i: add DMA support
In reply to Emilio LópezThis patch adds support for 64 byte or bigger transfers on the sun4i SPI controller. Said transfers will be performed via DMA.
---
Changes from v2:
* Fix commit message
Changes from v1:
* remove 64 byte limitation on transfers
* wrap stuff so it fits better in 80 cols
drivers/spi/spi-sun4i.c | 155 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 144 insertions(+), 11 deletions(-)
drivers/spi/spi-sun4i.c +144 -11
@@ -14,6 +14,8 @@#include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h>
@@ -34,6 +36,7 @@#define SUN4I_CTL_CPHA BIT(2)#define SUN4I_CTL_CPOL BIT(3)#define SUN4I_CTL_CS_ACTIVE_LOW BIT(4) +#define SUN4I_CTL_DMAMC_DEDICATED BIT(5)#define SUN4I_CTL_LMTF BIT(6)#define SUN4I_CTL_TF_RST BIT(8)#define SUN4I_CTL_RF_RST BIT(9)
@@ -51,6 +54,8 @@#define SUN4I_INT_STA_REG 0x10#define SUN4I_DMA_CTL_REG 0x14 +#define SUN4I_DMA_CTL_RF_READY BIT(0) +#define SUN4I_DMA_CTL_TF_NOT_FULL BIT(10)#define SUN4I_WAIT_REG 0x18
@@ -84,6 +89,9 @@ struct sun4i_spi {const u8 *tx_buf;u8 *rx_buf;int len; + +struct dma_chan *rx_dma_chan; +struct dma_chan *tx_dma_chan; };static inline u32 sun4i_spi_read (struct sun4i_spi *sspi,u32 reg)
@@ -130,6 +138,24 @@ static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) } } +static bool sun4i_spi_can_dma (struct spi_master *master, +struct spi_device *spi, +struct spi_transfer *tfr) +{ +return tfr->len >=SUN4I_FIFO_DEPTH ; +} + +static int sun4i_spi_prepare_message (struct spi_master *master, +struct spi_message *msg) +{ +struct sun4i_spi *sspi =spi_master_get_devdata (master); + + master->dma_rx = sspi->rx_dma_chan; + master->dma_tx = sspi->tx_dma_chan; + +return 0 ; +} +static void sun4i_spi_set_cs (struct spi_device *spi,bool enable) {struct sun4i_spi *sspi =spi_master_get_devdata (spi->master);
@@ -169,15 +195,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, struct spi_transfer *tfr) {struct sun4i_spi *sspi =spi_master_get_devdata (master); +struct dma_async_tx_descriptor *desc_tx =NULL , *desc_rx =NULL ;unsigned int mclk_rate, div, timeout;unsigned int tx_len =0 ;int ret =0 ;u32 reg; -/* We don't support transfer larger than the FIFO */ -if (tfr->len >SUN4I_FIFO_DEPTH ) -return -EINVAL ; -reinit_completion (&sspi->done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf;
@@ -186,7 +209,6 @@ static int sun4i_spi_transfer_one(struct spi_master *master,/* Clear pending interrupts */ sun4i_spi_write (sspi,SUN4I_INT_STA_REG , ~0 ); - reg =sun4i_spi_read (sspi,SUN4I_CTL_REG );/* Reset FIFOs */
@@ -269,12 +291,63 @@ static int sun4i_spi_transfer_one(struct spi_master *master,sun4i_spi_write (sspi,SUN4I_BURST_CNT_REG ,SUN4I_BURST_CNT (tfr->len));sun4i_spi_write (sspi,SUN4I_XMIT_CNT_REG ,SUN4I_XMIT_CNT (tx_len)); -/* Fill the TX FIFO */ -sun4i_spi_fill_fifo (sspi,SUN4I_FIFO_DEPTH ); -/* Enable the interrupts */ sun4i_spi_write (sspi,SUN4I_INT_CTL_REG ,SUN4I_INT_CTL_TC ); +if (sun4i_spi_can_dma (master, spi, tfr)) { +dev_dbg (&sspi->master->dev,"Using DMA mode for transfer\n" ); + +if (sspi->tx_buf) { + desc_tx =dmaengine_prep_slave_sg (sspi->tx_dma_chan, + tfr->tx_sg.sgl, tfr->tx_sg.nents, +DMA_TO_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc_tx) { +dev_err (&sspi->master->dev, +"Couldn't prepare dma slave\n" ); +return -EIO ; + } + +dmaengine_submit (desc_tx); + } + +if (sspi->rx_buf) { + desc_rx =dmaengine_prep_slave_sg (sspi->rx_dma_chan, + tfr->rx_sg.sgl, tfr->rx_sg.nents, +DMA_FROM_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc_rx) { +dev_err (&sspi->master->dev, +"Couldn't prepare dma slave\n" ); +return -EIO ; + } + +dmaengine_submit (desc_rx); + } + +/* Enable DMA requests */ + reg =sun4i_spi_read (sspi,SUN4I_CTL_REG ); +sun4i_spi_write (sspi,SUN4I_CTL_REG , + reg |SUN4I_CTL_DMAMC_DEDICATED ); +sun4i_spi_write (sspi,SUN4I_DMA_CTL_REG , +SUN4I_DMA_CTL_TF_NOT_FULL | +SUN4I_DMA_CTL_RF_READY ); + +dma_async_issue_pending (sspi->rx_dma_chan); +dma_async_issue_pending (sspi->tx_dma_chan); + }else { +dev_dbg (&sspi->master->dev,"Using PIO mode for transfer\n" ); + +/* Disable DMA requests */ + reg =sun4i_spi_read (sspi,SUN4I_CTL_REG ); +sun4i_spi_write (sspi,SUN4I_CTL_REG , + reg & ~SUN4I_CTL_DMAMC_DEDICATED ); +sun4i_spi_write (sspi,SUN4I_DMA_CTL_REG ,0 ); + +/* Fill the TX FIFO */ +sun4i_spi_fill_fifo (sspi,SUN4I_FIFO_DEPTH ); + } +/* Start the transfer */ reg =sun4i_spi_read (sspi,SUN4I_CTL_REG );sun4i_spi_write (sspi,SUN4I_CTL_REG , reg |SUN4I_CTL_XCH );
@@ -286,7 +359,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, goto out; } -sun4i_spi_drain_fifo (sspi,SUN4I_FIFO_DEPTH ); +if (sun4i_spi_can_dma (master, spi, tfr) && desc_rx) { +/* The receive transfer should be the last one to finish */ +dma_wait_for_async_tx (desc_rx); + }else { +sun4i_spi_drain_fifo (sspi,SUN4I_FIFO_DEPTH ); + } out:sun4i_spi_write (sspi,SUN4I_INT_CTL_REG ,0 );
@@ -351,6 +429,7 @@ static int sun4i_spi_runtime_suspend(struct device *dev)static int sun4i_spi_probe (struct platform_device *pdev) { +struct dma_slave_config dma_sconfig;struct spi_master *master;struct sun4i_spi *sspi;struct resource *res;
@@ -386,7 +465,10 @@ static int sun4i_spi_probe(struct platform_device *pdev) goto err_free_master; } +init_completion (&sspi->done); sspi->master = master; + master->can_dma = sun4i_spi_can_dma; + master->prepare_message = sun4i_spi_prepare_message; master->set_cs = sun4i_spi_set_cs; master->transfer_one = sun4i_spi_transfer_one; master->num_chipselect =4 ;
@@ -409,7 +491,45 @@ static int sun4i_spi_probe(struct platform_device *pdev) goto err_free_master; } -init_completion (&sspi->done); + sspi->tx_dma_chan =dma_request_slave_channel_reason (&pdev->dev,"tx" ); +if (IS_ERR (sspi->tx_dma_chan)) { +dev_err (&pdev->dev,"Unable to acquire DMA channel TX\n" ); + ret =PTR_ERR (sspi->tx_dma_chan); + goto err_free_master; + } + + dma_sconfig.direction =DMA_MEM_TO_DEV ; + dma_sconfig.src_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr = res->start +SUN4I_TXDATA_REG ; + dma_sconfig.src_maxburst =1 ; + dma_sconfig.dst_maxburst =1 ; + + ret =dmaengine_slave_config (sspi->tx_dma_chan, &dma_sconfig); +if (ret) { +dev_err (&pdev->dev,"Unable to configure TX DMA slave\n" ); + goto err_tx_dma_release; + } + + sspi->rx_dma_chan =dma_request_slave_channel_reason (&pdev->dev,"rx" ); +if (IS_ERR (sspi->rx_dma_chan)) { +dev_err (&pdev->dev,"Unable to acquire DMA channel RX\n" ); + ret =PTR_ERR (sspi->rx_dma_chan); + goto err_tx_dma_release; + } + + dma_sconfig.direction =DMA_DEV_TO_MEM ; + dma_sconfig.src_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.src_addr = res->start +SUN4I_RXDATA_REG ; + dma_sconfig.src_maxburst =1 ; + dma_sconfig.dst_maxburst =1 ; + + ret =dmaengine_slave_config (sspi->rx_dma_chan, &dma_sconfig); +if (ret) { +dev_err (&pdev->dev,"Unable to configure RX DMA slave\n" ); + goto err_rx_dma_release; + }/* * This wake-up/shutdown pattern is to be able to have the
@@ -418,7 +538,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) ret =sun4i_spi_runtime_resume (&pdev->dev);if (ret) {dev_err (&pdev->dev,"Couldn't resume the device\n" ); - goto err_free_master; + goto err_rx_dma_release; }pm_runtime_set_active (&pdev->dev);
@@ -436,6 +556,10 @@ static int sun4i_spi_probe(struct platform_device *pdev) err_pm_disable:pm_runtime_disable (&pdev->dev);sun4i_spi_runtime_suspend (&pdev->dev); +err_rx_dma_release: +dma_release_channel (sspi->rx_dma_chan); +err_tx_dma_release: +dma_release_channel (sspi->tx_dma_chan); err_free_master:spi_master_put (master);return ret;
@@ -443,8 +567,17 @@ err_free_master:static int sun4i_spi_remove (struct platform_device *pdev) { +struct spi_master *master =platform_get_drvdata (pdev); +struct sun4i_spi *sspi =spi_master_get_devdata (master); + +if (pm_runtime_active (&pdev->dev)) +sun4i_spi_runtime_suspend (&pdev->dev); +pm_runtime_disable (&pdev->dev); +dma_release_channel (sspi->rx_dma_chan); +dma_release_channel (sspi->tx_dma_chan); +return 0 ; }
Signature · 1 line
2.0.4
PATCH v3 2/8 spi: sun4i: add DMA support
Hi,
Quoted 31 lines · Emilio L?pez
drivers/spi/spi-sun4i.c +31
-0+if (sun4i_spi_can_dma (master, spi, tfr)) { +dev_dbg (&sspi->master->dev,"Using DMA mode for transfer\n" ); + +if (sspi->tx_buf) { + desc_tx =dmaengine_prep_slave_sg (sspi->tx_dma_chan, + tfr->tx_sg.sgl, tfr->tx_sg.nents, +DMA_TO_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc_tx) { +dev_err (&sspi->master->dev, +"Couldn't prepare dma slave\n" ); +return -EIO ; + } + +dmaengine_submit (desc_tx); + } + +if (sspi->rx_buf) { + desc_rx =dmaengine_prep_slave_sg (sspi->rx_dma_chan, + tfr->rx_sg.sgl, tfr->rx_sg.nents, +DMA_FROM_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc_rx) { +dev_err (&sspi->master->dev, +"Couldn't prepare dma slave\n" ); +return -EIO ; + } + +dmaengine_submit (desc_rx); + } +
drivers/spi/spi-sun4i.c static int sun4i_spi_transfer_one(struct spi_master *master, · line 337
Quoted 10 lines
drivers/spi/spi-sun4i.c +10
-0+/* Enable DMA requests */ + reg =sun4i_spi_read (sspi,SUN4I_CTL_REG ); +sun4i_spi_write (sspi,SUN4I_CTL_REG , + reg |SUN4I_CTL_DMAMC_DEDICATED ); +sun4i_spi_write (sspi,SUN4I_DMA_CTL_REG , +SUN4I_DMA_CTL_TF_NOT_FULL | +SUN4I_DMA_CTL_RF_READY ); + +dma_async_issue_pending (sspi->rx_dma_chan); +dma_async_issue_pending (sspi->tx_dma_chan);
I guess the DMA levels setup and the issue pending calls could be moved to the if statements above about wether the RX and TX buffers are here or not.
Quoted 117 lines
drivers/spi/spi-sun4i.c +67
-3+ }else { +dev_dbg (&sspi->master->dev,"Using PIO mode for transfer\n" ); + +/* Disable DMA requests */ + reg =sun4i_spi_read (sspi,SUN4I_CTL_REG ); +sun4i_spi_write (sspi,SUN4I_CTL_REG , + reg & ~SUN4I_CTL_DMAMC_DEDICATED ); +sun4i_spi_write (sspi,SUN4I_DMA_CTL_REG ,0 ); + +/* Fill the TX FIFO */ +sun4i_spi_fill_fifo (sspi,SUN4I_FIFO_DEPTH ); + } +/* Start the transfer */ reg =sun4i_spi_read (sspi,SUN4I_CTL_REG );sun4i_spi_write (sspi,SUN4I_CTL_REG , reg |SUN4I_CTL_XCH );@@ -286,7 +359,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, goto out; } -sun4i_spi_drain_fifo (sspi,SUN4I_FIFO_DEPTH ); +if (sun4i_spi_can_dma (master, spi, tfr) && desc_rx) { +/* The receive transfer should be the last one to finish */ +dma_wait_for_async_tx (desc_rx); + }else { +sun4i_spi_drain_fifo (sspi,SUN4I_FIFO_DEPTH ); + } out:sun4i_spi_write (sspi,SUN4I_INT_CTL_REG ,0 );@@ -351,6 +429,7 @@ static int sun4i_spi_runtime_suspend(struct device *dev)static int sun4i_spi_probe (struct platform_device *pdev) { +struct dma_slave_config dma_sconfig;struct spi_master *master;struct sun4i_spi *sspi;struct resource *res;@@ -386,7 +465,10 @@ static int sun4i_spi_probe(struct platform_device *pdev) goto err_free_master; } +init_completion (&sspi->done); sspi->master = master; + master->can_dma = sun4i_spi_can_dma; + master->prepare_message = sun4i_spi_prepare_message; master->set_cs = sun4i_spi_set_cs; master->transfer_one = sun4i_spi_transfer_one; master->num_chipselect =4 ;@@ -409,7 +491,45 @@ static int sun4i_spi_probe(struct platform_device *pdev) goto err_free_master; } -init_completion (&sspi->done); + sspi->tx_dma_chan =dma_request_slave_channel_reason (&pdev->dev,"tx" ); +if (IS_ERR (sspi->tx_dma_chan)) { +dev_err (&pdev->dev,"Unable to acquire DMA channel TX\n" ); + ret =PTR_ERR (sspi->tx_dma_chan); + goto err_free_master; + } + + dma_sconfig.direction =DMA_MEM_TO_DEV ; + dma_sconfig.src_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr = res->start +SUN4I_TXDATA_REG ; + dma_sconfig.src_maxburst =1 ; + dma_sconfig.dst_maxburst =1 ; + + ret =dmaengine_slave_config (sspi->tx_dma_chan, &dma_sconfig); +if (ret) { +dev_err (&pdev->dev,"Unable to configure TX DMA slave\n" ); + goto err_tx_dma_release; + } + + sspi->rx_dma_chan =dma_request_slave_channel_reason (&pdev->dev,"rx" ); +if (IS_ERR (sspi->rx_dma_chan)) { +dev_err (&pdev->dev,"Unable to acquire DMA channel RX\n" ); + ret =PTR_ERR (sspi->rx_dma_chan); + goto err_tx_dma_release; + } + + dma_sconfig.direction =DMA_DEV_TO_MEM ; + dma_sconfig.src_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.src_addr = res->start +SUN4I_RXDATA_REG ; + dma_sconfig.src_maxburst =1 ; + dma_sconfig.dst_maxburst =1 ; + + ret =dmaengine_slave_config (sspi->rx_dma_chan, &dma_sconfig); +if (ret) { +dev_err (&pdev->dev,"Unable to configure RX DMA slave\n" ); + goto err_rx_dma_release; + }/* * This wake-up/shutdown pattern is to be able to have the @@ -418,7 +538,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) ret =sun4i_spi_runtime_resume (&pdev->dev);if (ret) {dev_err (&pdev->dev,"Couldn't resume the device\n" ); - goto err_free_master; + goto err_rx_dma_release; }pm_runtime_set_active (&pdev->dev);@@ -436,6 +556,10 @@ static int sun4i_spi_probe(struct platform_device *pdev) err_pm_disable:pm_runtime_disable (&pdev->dev);sun4i_spi_runtime_suspend (&pdev->dev); +err_rx_dma_release: +dma_release_channel (sspi->rx_dma_chan); +err_tx_dma_release: +dma_release_channel (sspi->tx_dma_chan); err_free_master:spi_master_put (master);return ret;
drivers/spi/spi-sun4i.c err_free_master: · line 575
Quoted 9 lines
drivers/spi/spi-sun4i.c +6
-0@@ -443,8 +567,17 @@ err_free_master:static int sun4i_spi_remove (struct platform_device *pdev) { +struct spi_master *master =platform_get_drvdata (pdev); +struct sun4i_spi *sspi =spi_master_get_devdata (master); + +if (pm_runtime_active (&pdev->dev)) +sun4i_spi_runtime_suspend (&pdev->dev); +
This doesn't really belong in this patch, please send a separate one.
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140805/523c6850/attachment.sig>
PATCH v3 2/8 spi: sun4i: add DMA support
Hi Maxime,
El 05/08/14 a las 17:23, Maxime Ripard escibi?:
Quoted 40 lines
Hi,
Quoted 37 lines · Emilio L?pez
drivers/spi/spi-sun4i.c +37
-0+if (sun4i_spi_can_dma (master, spi, tfr)) { +dev_dbg (&sspi->master->dev,"Using DMA mode for transfer\n" ); + +if (sspi->tx_buf) { + desc_tx =dmaengine_prep_slave_sg (sspi->tx_dma_chan, + tfr->tx_sg.sgl, tfr->tx_sg.nents, +DMA_TO_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc_tx) { +dev_err (&sspi->master->dev, +"Couldn't prepare dma slave\n" ); +return -EIO ; + } + +dmaengine_submit (desc_tx); + } + +if (sspi->rx_buf) { + desc_rx =dmaengine_prep_slave_sg (sspi->rx_dma_chan, + tfr->rx_sg.sgl, tfr->rx_sg.nents, +DMA_FROM_DEVICE , +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); +if (!desc_rx) { +dev_err (&sspi->master->dev, +"Couldn't prepare dma slave\n" ); +return -EIO ; + } + +dmaengine_submit (desc_rx); + } + +/* Enable DMA requests */ + reg =sun4i_spi_read (sspi,SUN4I_CTL_REG ); +sun4i_spi_write (sspi,SUN4I_CTL_REG , + reg |SUN4I_CTL_DMAMC_DEDICATED ); + sun4i_spi_write(sspi, SUN4I_DMA_CTL_REG, + SUN4I_DMA_CTL_TF_NOT_FULL |
Quoted 8 lines
drivers/spi/spi-sun4i.c static int sun4i_spi_transfer_one(struct spi_master *master, · line 337
Quoted 4 lines
drivers/spi/spi-sun4i.c +4
-0+ SUN4I_DMA_CTL_RF_READY); + +dma_async_issue_pending (sspi->rx_dma_chan); +dma_async_issue_pending (sspi->tx_dma_chan);I guess the DMA levels setup and the issue pending calls could be moved to the if statements above about wether the RX and TX buffers are here or not.
Ok, I'll do that.
Quoted 118 lines
Quoted 118 lines
drivers/spi/spi-sun4i.c +67
-3+ }else { +dev_dbg (&sspi->master->dev,"Using PIO mode for transfer\n" ); + +/* Disable DMA requests */ + reg =sun4i_spi_read (sspi,SUN4I_CTL_REG ); +sun4i_spi_write (sspi,SUN4I_CTL_REG , + reg & ~SUN4I_CTL_DMAMC_DEDICATED ); +sun4i_spi_write (sspi,SUN4I_DMA_CTL_REG ,0 ); + +/* Fill the TX FIFO */ +sun4i_spi_fill_fifo (sspi,SUN4I_FIFO_DEPTH ); + } +/* Start the transfer */ reg =sun4i_spi_read (sspi,SUN4I_CTL_REG );sun4i_spi_write (sspi,SUN4I_CTL_REG , reg |SUN4I_CTL_XCH );@@ -286,7 +359,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, goto out; } -sun4i_spi_drain_fifo (sspi,SUN4I_FIFO_DEPTH ); +if (sun4i_spi_can_dma (master, spi, tfr) && desc_rx) { +/* The receive transfer should be the last one to finish */ +dma_wait_for_async_tx (desc_rx); + }else { +sun4i_spi_drain_fifo (sspi,SUN4I_FIFO_DEPTH ); + } out:sun4i_spi_write (sspi,SUN4I_INT_CTL_REG ,0 );@@ -351,6 +429,7 @@ static int sun4i_spi_runtime_suspend(struct device *dev)static int sun4i_spi_probe (struct platform_device *pdev) { +struct dma_slave_config dma_sconfig;struct spi_master *master;struct sun4i_spi *sspi;struct resource *res;@@ -386,7 +465,10 @@ static int sun4i_spi_probe(struct platform_device *pdev) goto err_free_master; } +init_completion (&sspi->done); sspi->master = master; + master->can_dma = sun4i_spi_can_dma; + master->prepare_message = sun4i_spi_prepare_message; master->set_cs = sun4i_spi_set_cs; master->transfer_one = sun4i_spi_transfer_one; master->num_chipselect =4 ;@@ -409,7 +491,45 @@ static int sun4i_spi_probe(struct platform_device *pdev) goto err_free_master; } -init_completion (&sspi->done); + sspi->tx_dma_chan =dma_request_slave_channel_reason (&pdev->dev,"tx" ); +if (IS_ERR (sspi->tx_dma_chan)) { +dev_err (&pdev->dev,"Unable to acquire DMA channel TX\n" ); + ret =PTR_ERR (sspi->tx_dma_chan); + goto err_free_master; + } + + dma_sconfig.direction =DMA_MEM_TO_DEV ; + dma_sconfig.src_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr = res->start +SUN4I_TXDATA_REG ; + dma_sconfig.src_maxburst =1 ; + dma_sconfig.dst_maxburst =1 ; + + ret =dmaengine_slave_config (sspi->tx_dma_chan, &dma_sconfig); +if (ret) { +dev_err (&pdev->dev,"Unable to configure TX DMA slave\n" ); + goto err_tx_dma_release; + } + + sspi->rx_dma_chan =dma_request_slave_channel_reason (&pdev->dev,"rx" ); +if (IS_ERR (sspi->rx_dma_chan)) { +dev_err (&pdev->dev,"Unable to acquire DMA channel RX\n" ); + ret =PTR_ERR (sspi->rx_dma_chan); + goto err_tx_dma_release; + } + + dma_sconfig.direction =DMA_DEV_TO_MEM ; + dma_sconfig.src_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.dst_addr_width =DMA_SLAVE_BUSWIDTH_1_BYTE ; + dma_sconfig.src_addr = res->start +SUN4I_RXDATA_REG ; + dma_sconfig.src_maxburst =1 ; + dma_sconfig.dst_maxburst =1 ; + + ret =dmaengine_slave_config (sspi->rx_dma_chan, &dma_sconfig); +if (ret) { +dev_err (&pdev->dev,"Unable to configure RX DMA slave\n" ); + goto err_rx_dma_release; + }/* * This wake-up/shutdown pattern is to be able to have the @@ -418,7 +538,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) ret =sun4i_spi_runtime_resume (&pdev->dev);if (ret) {dev_err (&pdev->dev,"Couldn't resume the device\n" ); - goto err_free_master; + goto err_rx_dma_release; }pm_runtime_set_active (&pdev->dev);@@ -436,6 +556,10 @@ static int sun4i_spi_probe(struct platform_device *pdev) err_pm_disable:pm_runtime_disable (&pdev->dev);sun4i_spi_runtime_suspend (&pdev->dev); +err_rx_dma_release: +dma_release_channel (sspi->rx_dma_chan); +err_tx_dma_release: +dma_release_channel (sspi->tx_dma_chan); err_free_master:spi_master_put (master);return ret;@@ -443,8 +567,17 @@ err_free_master:
Quoted 10 lines
drivers/spi/spi-sun4i.c err_free_master: · line 575
Quoted 8 lines
drivers/spi/spi-sun4i.c +6
-0static int sun4i_spi_remove (struct platform_device *pdev) { +struct spi_master *master =platform_get_drvdata (pdev); +struct sun4i_spi *sspi =spi_master_get_devdata (master); + +if (pm_runtime_active (&pdev->dev)) +sun4i_spi_runtime_suspend (&pdev->dev); +This doesn't really belong in this patch, please send a separate one.
Sure
Thanks!
Emilio
Mark Brown
maintainerPATCH v3 2/8 spi: sun4i: add DMA support
In reply to Emilio Lópezdrivers/spi/spi-sun4i.c static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) · line 157
Quoted 10 lines · Emilio L?pez
drivers/spi/spi-sun4i.c +10
-0+static int sun4i_spi_prepare_message (struct spi_master *master, +struct spi_message *msg) +{ +struct sun4i_spi *sspi =spi_master_get_devdata (master); + + master->dma_rx = sspi->rx_dma_chan; + master->dma_tx = sspi->tx_dma_chan; + +return 0 ; +}
Why is this being done per transfer rather than at probe time?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140807/930f5ba3/attachment.sig>
PATCH v3 3/8 ARM: sun4i: dt: Add node to represent the DMA controller
In reply to Emilio LópezLet's add a node to represent the A10 DMA controller on the device tree.
---
Changes from v2:
* Fix whitespace
* Collect tag from Chen-Yu
arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/arm/boot/dts/sun4i-a10.dtsi +8 -0
@@ -339,6 +339,14 @@ #size-cells = <1>; ranges; + dma: dma-controller at 01c02000 { + compatible = "allwinner,sun4i-a10-dma"; + reg = <0x01c02000 0x1000>; + interrupts = <27>; + clocks = <&ahb_gates 6>; + #dma-cells = <2>; + }; + spi0: spi at 01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>;
Signature · 1 line
2.0.4
PATCH v3 3/8 ARM: sun4i: dt: Add node to represent the DMA controller
Quoted 4 lines · Emilio L?pez
Let's add a node to represent the A10 DMA controller on the device tree.
Signed-off-by Emilio L?pez <[email protected]>Reviewed-by Chen-Yu Tsai <[email protected]>
Merged, together with the next patches.
Thanks!
Maxime
Signature · 10 lines
Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140805/33dc84da/attachment.sig>
PATCH v3 4/8 ARM: sun5i: dt: Add nodes to represent the DMA controllers
In reply to Emilio LópezThe A10S and A13 SoCs have sun4i-compatible DMA controllers. Let's add the corresponding nodes to represent them on the device tree.
---
Changes from v2:
* Fix whitespace
* Collect tag from Chen-Yu
Note that I have only tested on A10S, if anyone with an A13 device could test as well, that would be great! I don't expect any issues however.
arch/arm/boot/dts/sun5i-a10s.dtsi | 8 ++++++++ arch/arm/boot/dts/sun5i-a13.dtsi | 8 ++++++++ 2 files changed, 16 insertions(+)
arch/arm/boot/dts/sun5i-a10s.dtsi +8 -0
@@ -300,6 +300,14 @@ #size-cells = <1>; ranges; + dma: dma-controller at 01c02000 { + compatible = "allwinner,sun4i-a10-dma"; + reg = <0x01c02000 0x1000>; + interrupts = <27>; + clocks = <&ahb_gates 6>; + #dma-cells = <2>; + }; + spi0: spi at 01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>;
arch/arm/boot/dts/sun5i-a13.dtsi +8 -0
@@ -298,6 +298,14 @@ #size-cells = <1>; ranges; + dma: dma-controller at 01c02000 { + compatible = "allwinner,sun4i-a10-dma"; + reg = <0x01c02000 0x1000>; + interrupts = <27>; + clocks = <&ahb_gates 6>; + #dma-cells = <2>; + }; + spi0: spi at 01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>;
Signature · 1 line
2.0.4
PATCH v3 5/8 ARM: sun7i: dt: Add node to represent the DMA controller
In reply to Emilio LópezThe A20 SoC has a sun4i-compatible DMA controller. Let's add a node to represent it on the device tree.
---
Changes from v2:
* Fix whitespace
* Collect tag from Chen-Yu
arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/arm/boot/dts/sun7i-a20.dtsi +8 -0
@@ -423,6 +423,14 @@ interrupts = <0 0 4>; }; + dma: dma-controller at 01c02000 { + compatible = "allwinner,sun4i-a10-dma"; + reg = <0x01c02000 0x1000>; + interrupts = <0 27 4>; + clocks = <&ahb_gates 6>; + #dma-cells = <2>; + }; + spi0: spi at 01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>;
Signature · 1 line
2.0.4
PATCH v3 6/8 ARM: sun4i: dt: enable DMA on SPI
In reply to Emilio LópezAll of our SPI controllers support DMA transfers, so let's add the properties here so they can be used when it's best to do so.
---
Changes from v2:
* Collect tag from Chen-Yu
arch/arm/boot/dts/sun4i-a10.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/arm/boot/dts/sun4i-a10.dtsi +8 -0
@@ -353,6 +353,8 @@ interrupts = <10>; clocks = <&ahb_gates 20>, <&spi0_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 27>, <&dma 1 26>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -364,6 +366,8 @@ interrupts = <11>; clocks = <&ahb_gates 21>, <&spi1_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 9>, <&dma 1 8>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -459,6 +463,8 @@ interrupts = <12>; clocks = <&ahb_gates 22>, <&spi2_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 29>, <&dma 1 28>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -498,6 +504,8 @@ interrupts = <50>; clocks = <&ahb_gates 23>, <&spi3_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 31>, <&dma 1 30>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
Signature · 1 line
2.0.4
PATCH v3 7/8 ARM: sun5i: dt: enable DMA on SPI
In reply to Emilio LópezAll of our SPI controllers support DMA transfers, so let's add the properties here so they can be used when it's best to do so.
---
Changes from v2:
* Collect tag from Chen-Yu
arch/arm/boot/dts/sun5i-a10s.dtsi | 6 ++++++ arch/arm/boot/dts/sun5i-a13.dtsi | 6 ++++++ 2 files changed, 12 insertions(+)
arch/arm/boot/dts/sun5i-a10s.dtsi +6 -0
@@ -314,6 +314,8 @@ interrupts = <10>; clocks = <&ahb_gates 20>, <&spi0_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 27>, <&dma 1 26>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -325,6 +327,8 @@ interrupts = <11>; clocks = <&ahb_gates 21>, <&spi1_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 9>, <&dma 1 8>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -411,6 +415,8 @@ interrupts = <12>; clocks = <&ahb_gates 22>, <&spi2_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 29>, <&dma 1 28>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
arch/arm/boot/dts/sun5i-a13.dtsi +6 -0
@@ -312,6 +312,8 @@ interrupts = <10>; clocks = <&ahb_gates 20>, <&spi0_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 27>, <&dma 1 26>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -323,6 +325,8 @@ interrupts = <11>; clocks = <&ahb_gates 21>, <&spi1_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 9>, <&dma 1 8>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -384,6 +388,8 @@ interrupts = <12>; clocks = <&ahb_gates 22>, <&spi2_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 29>, <&dma 1 28>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
Signature · 1 line
2.0.4
PATCH v3 8/8 ARM: sun7i: dt: enable DMA on SPI
In reply to Emilio LópezAll of our SPI controllers support DMA transfers, so let's add the properties here so they can be used when it's best to do so.
---
Changes from v2:
* Collect tag from Chen-Yu
arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/arm/boot/dts/sun7i-a20.dtsi +8 -0
@@ -437,6 +437,8 @@ interrupts = <0 10 4>; clocks = <&ahb_gates 20>, <&spi0_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 27>, <&dma 1 26>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -448,6 +450,8 @@ interrupts = <0 11 4>; clocks = <&ahb_gates 21>, <&spi1_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 9>, <&dma 1 8>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -543,6 +547,8 @@ interrupts = <0 12 4>; clocks = <&ahb_gates 22>, <&spi2_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 29>, <&dma 1 28>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
@@ -582,6 +588,8 @@ interrupts = <0 50 4>; clocks = <&ahb_gates 23>, <&spi3_clk>; clock-names = "ahb", "mod"; + dmas = <&dma 1 31>, <&dma 1 30>; + dma-names = "rx", "tx"; status = "disabled"; #address-cells = <1>; #size-cells = <0>;
Signature · 1 line
2.0.4