mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
dmaengine updates for v6.0-rc1
New support/Core - Remove DMA_MEMCPY_SG for lack of users - Tegra 234 dmaengine support - Mediatek MT8365 dma support - Apple ADMAC driver Updates: - Yaml conversion for ST-Ericsson DMA40 binding and Freescale edma - rz-dmac updates and device_synchronize support - Bunch of typo in comments fixes in drivers - multithread support in sf-pdma driver -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmLrWacACgkQfBQHDyUj g0f/wRAAsGxg7IQqMKhWTiE6xN3/B4vxTD9Er4jCwjVw+ibivH9Nvhp9n4Cv5qr0 Me1eGNq6e4KMD1RRBvy2KmK44pBodrCeDpWLGonOBToWPlKBGFRjOZ0v/H3/eVOs kjfYb73zPmleGZy2w0i6g8g5cwCwb5eDUGtztqIcYRET3jH+rWKYrHnMG/gaa1iF 9isMKUNqplv2mKSXmxsMRJPzY7NRuPJthnsQSKdEXaY9HEmEUX9wAB8K1Dy+UPK/ vAPg/Zn9XSnir4JWYxLSMI2bDrOz4xkaQ2Xac9pV1KIAMyx76RGu/Yz0JdVUsgGU w6aI/AYDtKeQe5sZSpbt3K/Ef2s5tVRfnCO3avtva6ozO39vOxpqTyujidxF8gJW xCsQVa8t92mKB8Y9/pwGIjYEnSoyLoxclBTMl7eVLvbHPa+maVeOnixfb/5uWD45 +6djWv3FW/D7WilsjyZe57tSjvhw3RrDQEpKwuMCpmScMqitu0pVzFBYv+vpIjxL q5lbRK0mP9trdGHqsoD/GVjdxv+O7bwZjBNPzahxoRpN4+jktb8xfRQEZUW2Uqyf HPLvoLNbVPK0UyHkPTAj/QnTq56M21fMIuCn1Jp6RjzRzD2w7fHFtoOF6+wsFVx6 iBYDzQRTq2lNIGFnoQ8N94XiKORfdJNv+ZstGTirWKc6xaKDw7E= =/IFO -----END PGP SIGNATURE----- Merge tag 'dmaengine-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine Pull dmaengine updates from Vinod Koul: "New support / Core: - Remove DMA_MEMCPY_SG for lack of users - Tegra 234 dmaengine support - Mediatek MT8365 dma support - Apple ADMAC driver Updates: - Yaml conversion for ST-Ericsson DMA40 binding and Freescale edma - rz-dmac updates and device_synchronize support - Bunch of typo in comments fixes in drivers - multithread support in sf-pdma driver" * tag 'dmaengine-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (50 commits) dmaengine: mediatek: mtk-hsdma: Fix typo 'the the' in comment dmaengine: axi-dmac: check cache coherency register dmaengine: sh: rz-dmac: Add device_synchronize callback dmaengine: sprd: Cleanup in .remove() after pm_runtime_get_sync() failed dmaengine: tegra: Add terminate() for Tegra234 dt-bindings: dmaengine: Add compatible for Tegra234 dmaengine: xilinx: use strscpy to replace strlcpy dmaengine: imx-sdma: Add FIFO stride support for multi FIFO script dmaengine: idxd: Correct IAX operation code names dmaengine: imx-dma: Cast of_device_get_match_data() with (uintptr_t) dmaengine: dw-axi-dmac: ignore interrupt if no descriptor dmaengine: dw-axi-dmac: do not print NULL LLI during error dmaengine: altera-msgdma: Fixed some inconsistent function name descriptions dmaengine: imx-sdma: Add missing struct documentation dmaengine: sf-pdma: Add multithread support for a DMA channel dt-bindings: dma: dw-axi-dmac: extend the number of interrupts dmaengine: dmatest: use strscpy to replace strlcpy dmaengine: ste_dma40: fix typo in comment dmaengine: jz4780: fix typo in comment dmaengine: s3c24xx: fix typo in comment ...
This commit is contained in:
commit
31be1d0fbd
80
Documentation/devicetree/bindings/dma/apple,admac.yaml
Normal file
80
Documentation/devicetree/bindings/dma/apple,admac.yaml
Normal file
@ -0,0 +1,80 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/apple,admac.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple Audio DMA Controller (ADMAC)
|
||||
|
||||
description: |
|
||||
Apple's Audio DMA Controller (ADMAC) is used to fetch and store audio samples
|
||||
on SoCs from the "Apple Silicon" family.
|
||||
|
||||
The controller has been seen with up to 24 channels. Even-numbered channels
|
||||
are TX-only, odd-numbered are RX-only. Individual channels are coupled to
|
||||
fixed device endpoints.
|
||||
|
||||
maintainers:
|
||||
- Martin Povišer <povik+lin@cutebit.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t6000-admac
|
||||
- apple,t8103-admac
|
||||
- const: apple,admac
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#dma-cells':
|
||||
const: 1
|
||||
description:
|
||||
Clients specify a single cell with channel number.
|
||||
|
||||
dma-channels:
|
||||
maximum: 24
|
||||
|
||||
interrupts:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
description:
|
||||
Interrupts that correspond to the 4 IRQ outputs of the controller. Usually
|
||||
only one of the controller outputs will be connected as an usable interrupt
|
||||
source. The remaining interrupts will be left without a valid value, e.g.
|
||||
in an interrupts-extended list the disconnected positions will contain
|
||||
an empty phandle reference <0>.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#dma-cells'
|
||||
- dma-channels
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
aic: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
};
|
||||
|
||||
admac: dma-controller@238200000 {
|
||||
compatible = "apple,t8103-admac", "apple,admac";
|
||||
reg = <0x38200000 0x34000>;
|
||||
dma-channels = <24>;
|
||||
interrupts-extended = <0>,
|
||||
<&aic AIC_IRQ 626 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0>,
|
||||
<0>;
|
||||
#dma-cells = <1>;
|
||||
};
|
155
Documentation/devicetree/bindings/dma/fsl,edma.yaml
Normal file
155
Documentation/devicetree/bindings/dma/fsl,edma.yaml
Normal file
@ -0,0 +1,155 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/fsl,edma.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale enhanced Direct Memory Access(eDMA) Controller
|
||||
|
||||
description: |
|
||||
The eDMA channels have multiplex capability by programmable
|
||||
memory-mapped registers. channels are split into two groups, called
|
||||
DMAMUX0 and DMAMUX1, specific DMA request source can only be multiplexed
|
||||
by any channel of certain group, DMAMUX0 or DMAMUX1, but not both.
|
||||
|
||||
maintainers:
|
||||
- Peng Fan <peng.fan@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,vf610-edma
|
||||
- fsl,imx7ulp-edma
|
||||
- items:
|
||||
- const: fsl,ls1028a-edma
|
||||
- const: fsl,vf610-edma
|
||||
|
||||
reg:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
interrupts:
|
||||
minItems: 2
|
||||
maxItems: 17
|
||||
|
||||
interrupt-names:
|
||||
minItems: 2
|
||||
maxItems: 17
|
||||
|
||||
"#dma-cells":
|
||||
const: 2
|
||||
|
||||
dma-channels:
|
||||
const: 32
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
maxItems: 2
|
||||
|
||||
big-endian:
|
||||
description: |
|
||||
If present registers and hardware scatter/gather descriptors of the
|
||||
eDMA are implemented in big endian mode, otherwise in little mode.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- "#dma-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- dma-channels
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,vf610-edma
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: dmamux0
|
||||
- const: dmamux1
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: edma-tx
|
||||
- const: edma-err
|
||||
reg:
|
||||
maxItems: 3
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,imx7ulp-edma
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: dma
|
||||
- const: dmamux0
|
||||
interrupts:
|
||||
maxItems: 17
|
||||
reg:
|
||||
maxItems: 2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/vf610-clock.h>
|
||||
|
||||
edma0: dma-controller@40018000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,vf610-edma";
|
||||
reg = <0x40018000 0x2000>,
|
||||
<0x40024000 0x1000>,
|
||||
<0x40025000 0x1000>;
|
||||
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "edma-tx", "edma-err";
|
||||
dma-channels = <32>;
|
||||
clock-names = "dmamux0", "dmamux1";
|
||||
clocks = <&clks VF610_CLK_DMAMUX0>, <&clks VF610_CLK_DMAMUX1>;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/imx7ulp-clock.h>
|
||||
|
||||
edma1: dma-controller@40080000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,imx7ulp-edma";
|
||||
reg = <0x40080000 0x2000>,
|
||||
<0x40210000 0x1000>;
|
||||
dma-channels = <32>;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
|
||||
/* last is eDMA2-ERR interrupt */
|
||||
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "dma", "dmamux0";
|
||||
clocks = <&pcc2 IMX7ULP_CLK_DMA1>, <&pcc2 IMX7ULP_CLK_DMA_MUX1>;
|
||||
};
|
@ -1,111 +0,0 @@
|
||||
* Freescale enhanced Direct Memory Access(eDMA) Controller
|
||||
|
||||
The eDMA channels have multiplex capability by programmble memory-mapped
|
||||
registers. channels are split into two groups, called DMAMUX0 and DMAMUX1,
|
||||
specific DMA request source can only be multiplexed by any channel of certain
|
||||
group, DMAMUX0 or DMAMUX1, but not both.
|
||||
|
||||
* eDMA Controller
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC
|
||||
- "fsl,imx7ulp-edma" for eDMA2 used similar to that on i.mx7ulp
|
||||
- "fsl,ls1028a-edma" followed by "fsl,vf610-edma" for eDMA used on the
|
||||
LS1028A SoC.
|
||||
- reg : Specifies base physical address(s) and size of the eDMA registers.
|
||||
The 1st region is eDMA control register's address and size.
|
||||
The 2nd and the 3rd regions are programmable channel multiplexing
|
||||
control register's address and size.
|
||||
- interrupts : A list of interrupt-specifiers, one for each entry in
|
||||
interrupt-names on vf610 similar SoC. But for i.mx7ulp per channel
|
||||
per transmission interrupt, total 16 channel interrupt and 1
|
||||
error interrupt(located in the last), no interrupt-names list on
|
||||
i.mx7ulp for clean on dts.
|
||||
- #dma-cells : Must be <2>.
|
||||
The 1st cell specifies the DMAMUX(0 for DMAMUX0 and 1 for DMAMUX1).
|
||||
Specific request source can only be multiplexed by specific channels
|
||||
group called DMAMUX.
|
||||
The 2nd cell specifies the request source(slot) ID.
|
||||
See the SoC's reference manual for all the supported request sources.
|
||||
- dma-channels : Number of channels supported by the controller
|
||||
- clock-names : A list of channel group clock names. Should contain:
|
||||
"dmamux0" - clock name of mux0 group
|
||||
"dmamux1" - clock name of mux1 group
|
||||
Note: No dmamux0 on i.mx7ulp, but another 'dma' clk added on i.mx7ulp.
|
||||
- clocks : A list of phandle and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
|
||||
Optional properties:
|
||||
- big-endian: If present registers and hardware scatter/gather descriptors
|
||||
of the eDMA are implemented in big endian mode, otherwise in little
|
||||
mode.
|
||||
- interrupt-names : Should contain the below on vf610 similar SoC but not used
|
||||
on i.mx7ulp similar SoC:
|
||||
"edma-tx" - the transmission interrupt
|
||||
"edma-err" - the error interrupt
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
edma0: dma-controller@40018000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,vf610-edma";
|
||||
reg = <0x40018000 0x2000>,
|
||||
<0x40024000 0x1000>,
|
||||
<0x40025000 0x1000>;
|
||||
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "edma-tx", "edma-err";
|
||||
dma-channels = <32>;
|
||||
clock-names = "dmamux0", "dmamux1";
|
||||
clocks = <&clks VF610_CLK_DMAMUX0>,
|
||||
<&clks VF610_CLK_DMAMUX1>;
|
||||
}; /* vf610 */
|
||||
|
||||
edma1: dma-controller@40080000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,imx7ulp-edma";
|
||||
reg = <0x40080000 0x2000>,
|
||||
<0x40210000 0x1000>;
|
||||
dma-channels = <32>;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
|
||||
/* last is eDMA2-ERR interrupt */
|
||||
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "dma", "dmamux0";
|
||||
clocks = <&pcc2 IMX7ULP_CLK_DMA1>,
|
||||
<&pcc2 IMX7ULP_CLK_DMA_MUX1>;
|
||||
}; /* i.mx7ulp */
|
||||
|
||||
* DMA clients
|
||||
DMA client drivers that uses the DMA function must use the format described
|
||||
in the dma.txt file, using a two-cell specifier for each channel: the 1st
|
||||
specifies the channel group(DMAMUX) in which this request can be multiplexed,
|
||||
and the 2nd specifies the request source.
|
||||
|
||||
Examples:
|
||||
|
||||
sai2: sai@40031000 {
|
||||
compatible = "fsl,vf610-sai";
|
||||
reg = <0x40031000 0x1000>;
|
||||
interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "sai";
|
||||
clocks = <&clks VF610_CLK_SAI2>;
|
||||
dma-names = "tx", "rx";
|
||||
dmas = <&edma0 0 21>,
|
||||
<&edma0 0 20>;
|
||||
};
|
@ -22,6 +22,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt2712-uart-dma
|
||||
- mediatek,mt8365-uart-dma
|
||||
- mediatek,mt8516-uart-dma
|
||||
- const: mediatek,mt6577-uart-dma
|
||||
- enum:
|
||||
|
@ -23,7 +23,9 @@ properties:
|
||||
oneOf:
|
||||
- const: nvidia,tegra186-gpcdma
|
||||
- items:
|
||||
- const: nvidia,tegra194-gpcdma
|
||||
- enum:
|
||||
- nvidia,tegra234-gpcdma
|
||||
- nvidia,tegra194-gpcdma
|
||||
- const: nvidia,tegra186-gpcdma
|
||||
|
||||
"#dma-cells":
|
||||
|
@ -34,7 +34,12 @@ properties:
|
||||
- const: axidma_apb_regs
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
If the IP-core synthesis parameter DMAX_INTR_IO_TYPE is set to 1, this
|
||||
will be per-channel interrupts. Otherwise, this is a single combined IRQ
|
||||
for all channels.
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
clocks:
|
||||
items:
|
||||
|
@ -1,138 +0,0 @@
|
||||
* DMA40 DMA Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "stericsson,dma40"
|
||||
- reg: Address range of the DMAC registers
|
||||
- reg-names: Names of the above areas to use during resource look-up
|
||||
- interrupt: Should contain the DMAC interrupt number
|
||||
- #dma-cells: must be <3>
|
||||
- memcpy-channels: Channels to be used for memcpy
|
||||
|
||||
Optional properties:
|
||||
- dma-channels: Number of channels supported by hardware - if not present
|
||||
the driver will attempt to obtain the information from H/W
|
||||
- disabled-channels: Channels which can not be used
|
||||
|
||||
Example:
|
||||
|
||||
dma: dma-controller@801c0000 {
|
||||
compatible = "stericsson,db8500-dma40", "stericsson,dma40";
|
||||
reg = <0x801C0000 0x1000 0x40010000 0x800>;
|
||||
reg-names = "base", "lcpa";
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 25 0x4>;
|
||||
|
||||
#dma-cells = <2>;
|
||||
memcpy-channels = <56 57 58 59 60>;
|
||||
disabled-channels = <12>;
|
||||
dma-channels = <8>;
|
||||
};
|
||||
|
||||
Clients
|
||||
Required properties:
|
||||
- dmas: Comma separated list of dma channel requests
|
||||
- dma-names: Names of the aforementioned requested channels
|
||||
|
||||
Each dmas request consists of 4 cells:
|
||||
1. A phandle pointing to the DMA controller
|
||||
2. Device signal number, the signal line for single and burst requests
|
||||
connected from the device to the DMA40 engine
|
||||
3. The DMA request line number (only when 'use fixed channel' is set)
|
||||
4. A 32bit mask specifying; mode, direction and endianness
|
||||
[NB: This list will grow]
|
||||
0x00000001: Mode:
|
||||
Logical channel when unset
|
||||
Physical channel when set
|
||||
0x00000002: Direction:
|
||||
Memory to Device when unset
|
||||
Device to Memory when set
|
||||
0x00000004: Endianness:
|
||||
Little endian when unset
|
||||
Big endian when set
|
||||
0x00000008: Use fixed channel:
|
||||
Use automatic channel selection when unset
|
||||
Use DMA request line number when set
|
||||
0x00000010: Set channel as high priority:
|
||||
Normal priority when unset
|
||||
High priority when set
|
||||
|
||||
Existing signal numbers for the DB8500 ASIC. Unless specified, the signals are
|
||||
bidirectional, i.e. the same for RX and TX operations:
|
||||
|
||||
0: SPI controller 0
|
||||
1: SD/MMC controller 0 (unused)
|
||||
2: SD/MMC controller 1 (unused)
|
||||
3: SD/MMC controller 2 (unused)
|
||||
4: I2C port 1
|
||||
5: I2C port 3
|
||||
6: I2C port 2
|
||||
7: I2C port 4
|
||||
8: Synchronous Serial Port SSP0
|
||||
9: Synchronous Serial Port SSP1
|
||||
10: Multi-Channel Display Engine MCDE RX
|
||||
11: UART port 2
|
||||
12: UART port 1
|
||||
13: UART port 0
|
||||
14: Multirate Serial Port MSP2
|
||||
15: I2C port 0
|
||||
16: USB OTG in/out endpoints 7 & 15
|
||||
17: USB OTG in/out endpoints 6 & 14
|
||||
18: USB OTG in/out endpoints 5 & 13
|
||||
19: USB OTG in/out endpoints 4 & 12
|
||||
20: SLIMbus or HSI channel 0
|
||||
21: SLIMbus or HSI channel 1
|
||||
22: SLIMbus or HSI channel 2
|
||||
23: SLIMbus or HSI channel 3
|
||||
24: Multimedia DSP SXA0
|
||||
25: Multimedia DSP SXA1
|
||||
26: Multimedia DSP SXA2
|
||||
27: Multimedia DSP SXA3
|
||||
28: SD/MM controller 2
|
||||
29: SD/MM controller 0
|
||||
30: MSP port 1 on DB8500 v1, MSP port 3 on DB8500 v2
|
||||
31: MSP port 0 or SLIMbus channel 0
|
||||
32: SD/MM controller 1
|
||||
33: SPI controller 2
|
||||
34: i2c3 RX2 TX2
|
||||
35: SPI controller 1
|
||||
36: USB OTG in/out endpoints 3 & 11
|
||||
37: USB OTG in/out endpoints 2 & 10
|
||||
38: USB OTG in/out endpoints 1 & 9
|
||||
39: USB OTG in/out endpoints 8
|
||||
40: SPI controller 3
|
||||
41: SD/MM controller 3
|
||||
42: SD/MM controller 4
|
||||
43: SD/MM controller 5
|
||||
44: Multimedia DSP SXA4
|
||||
45: Multimedia DSP SXA5
|
||||
46: SLIMbus channel 8 or Multimedia DSP SXA6
|
||||
47: SLIMbus channel 9 or Multimedia DSP SXA7
|
||||
48: Crypto Accelerator 1
|
||||
49: Crypto Accelerator 1 TX or Hash Accelerator 1 TX
|
||||
50: Hash Accelerator 1 TX
|
||||
51: memcpy TX (to be used by the DMA driver for memcpy operations)
|
||||
52: SLIMbus or HSI channel 4
|
||||
53: SLIMbus or HSI channel 5
|
||||
54: SLIMbus or HSI channel 6
|
||||
55: SLIMbus or HSI channel 7
|
||||
56: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
57: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
58: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
59: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
60: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
61: Crypto Accelerator 0
|
||||
62: Crypto Accelerator 0 TX or Hash Accelerator 0 TX
|
||||
63: Hash Accelerator 0 TX
|
||||
|
||||
Example:
|
||||
|
||||
uart@80120000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x80120000 0x1000>;
|
||||
interrupts = <0 11 0x4>;
|
||||
|
||||
dmas = <&dma 13 0 0x2>, /* Logical - DevToMem */
|
||||
<&dma 13 0 0x0>; /* Logical - MemToDev */
|
||||
dma-names = "rx", "rx";
|
||||
|
||||
};
|
159
Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
Normal file
159
Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
Normal file
@ -0,0 +1,159 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/stericsson,dma40.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ST-Ericsson DMA40 DMA Engine
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
|
||||
properties:
|
||||
"#dma-cells":
|
||||
const: 3
|
||||
description: |
|
||||
The first cell is the unique device channel number as indicated by this
|
||||
table for DB8500 which is the only ASIC known to use DMA40:
|
||||
|
||||
0: SPI controller 0
|
||||
1: SD/MMC controller 0 (unused)
|
||||
2: SD/MMC controller 1 (unused)
|
||||
3: SD/MMC controller 2 (unused)
|
||||
4: I2C port 1
|
||||
5: I2C port 3
|
||||
6: I2C port 2
|
||||
7: I2C port 4
|
||||
8: Synchronous Serial Port SSP0
|
||||
9: Synchronous Serial Port SSP1
|
||||
10: Multi-Channel Display Engine MCDE RX
|
||||
11: UART port 2
|
||||
12: UART port 1
|
||||
13: UART port 0
|
||||
14: Multirate Serial Port MSP2
|
||||
15: I2C port 0
|
||||
16: USB OTG in/out endpoints 7 & 15
|
||||
17: USB OTG in/out endpoints 6 & 14
|
||||
18: USB OTG in/out endpoints 5 & 13
|
||||
19: USB OTG in/out endpoints 4 & 12
|
||||
20: SLIMbus or HSI channel 0
|
||||
21: SLIMbus or HSI channel 1
|
||||
22: SLIMbus or HSI channel 2
|
||||
23: SLIMbus or HSI channel 3
|
||||
24: Multimedia DSP SXA0
|
||||
25: Multimedia DSP SXA1
|
||||
26: Multimedia DSP SXA2
|
||||
27: Multimedia DSP SXA3
|
||||
28: SD/MMC controller 2
|
||||
29: SD/MMC controller 0
|
||||
30: MSP port 1 on DB8500 v1, MSP port 3 on DB8500 v2
|
||||
31: MSP port 0 or SLIMbus channel 0
|
||||
32: SD/MMC controller 1
|
||||
33: SPI controller 2
|
||||
34: i2c3 RX2 TX2
|
||||
35: SPI controller 1
|
||||
36: USB OTG in/out endpoints 3 & 11
|
||||
37: USB OTG in/out endpoints 2 & 10
|
||||
38: USB OTG in/out endpoints 1 & 9
|
||||
39: USB OTG in/out endpoints 8
|
||||
40: SPI controller 3
|
||||
41: SD/MMC controller 3
|
||||
42: SD/MMC controller 4
|
||||
43: SD/MMC controller 5
|
||||
44: Multimedia DSP SXA4
|
||||
45: Multimedia DSP SXA5
|
||||
46: SLIMbus channel 8 or Multimedia DSP SXA6
|
||||
47: SLIMbus channel 9 or Multimedia DSP SXA7
|
||||
48: Crypto Accelerator 1
|
||||
49: Crypto Accelerator 1 TX or Hash Accelerator 1 TX
|
||||
50: Hash Accelerator 1 TX
|
||||
51: memcpy TX (to be used by the DMA driver for memcpy operations)
|
||||
52: SLIMbus or HSI channel 4
|
||||
53: SLIMbus or HSI channel 5
|
||||
54: SLIMbus or HSI channel 6
|
||||
55: SLIMbus or HSI channel 7
|
||||
56: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
57: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
58: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
59: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
60: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
61: Crypto Accelerator 0
|
||||
62: Crypto Accelerator 0 TX or Hash Accelerator 0 TX
|
||||
63: Hash Accelerator 0 TX
|
||||
|
||||
The second cell is the DMA request line number. This is only used when
|
||||
a fixed channel is allocated, and indicated by setting bit 3 in the
|
||||
flags field (see below).
|
||||
|
||||
The third cell is a 32bit flags bitfield with the following possible
|
||||
bits set:
|
||||
0x00000001 (bit 0) - mode:
|
||||
Logical channel when unset
|
||||
Physical channel when set
|
||||
0x00000002 (bit 1) - direction:
|
||||
Memory to Device when unset
|
||||
Device to Memory when set
|
||||
0x00000004 (bit 2) - endianness:
|
||||
Little endian when unset
|
||||
Big endian when set
|
||||
0x00000008 (bit 3) - use fixed channel:
|
||||
Use automatic channel selection when unset
|
||||
Use DMA request line number when set
|
||||
0x00000010 (bit 4) - set channel as high priority:
|
||||
Normal priority when unset
|
||||
High priority when set
|
||||
|
||||
compatible:
|
||||
items:
|
||||
- const: stericsson,db8500-dma40
|
||||
- const: stericsson,dma40
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: DMA40 memory base
|
||||
- description: LCPA memory base
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: base
|
||||
- const: lcpa
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
memcpy-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: Array of u32 elements indicating which channels on the DMA
|
||||
engine are elegible for memcpy transfers
|
||||
|
||||
required:
|
||||
- "#dma-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- memcpy-channels
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/mfd/dbx500-prcmu.h>
|
||||
dma-controller@801C0000 {
|
||||
compatible = "stericsson,db8500-dma40", "stericsson,dma40";
|
||||
reg = <0x801C0000 0x1000>, <0x40010000 0x800>;
|
||||
reg-names = "base", "lcpa";
|
||||
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#dma-cells = <3>;
|
||||
memcpy-channels = <56 57 58 59 60>;
|
||||
clocks = <&prcmu_clk PRCMU_DMACLK>;
|
||||
};
|
||||
...
|
@ -162,16 +162,6 @@ Currently, the types available are:
|
||||
|
||||
- The device is able to do memory to memory copies
|
||||
|
||||
- - DMA_MEMCPY_SG
|
||||
|
||||
- The device supports memory to memory scatter-gather transfers.
|
||||
|
||||
- Even though a plain memcpy can look like a particular case of a
|
||||
scatter-gather transfer, with a single chunk to copy, it's a distinct
|
||||
transaction type in the mem2mem transfer case. This is because some very
|
||||
simple devices might be able to do contiguous single-chunk memory copies,
|
||||
but have no support for more complex SG transfers.
|
||||
|
||||
- No matter what the overall size of the combined chunks for source and
|
||||
destination is, only as many bytes as the smallest of the two will be
|
||||
transmitted. That means the number and size of the scatter-gather buffers in
|
||||
|
@ -1851,6 +1851,7 @@ T: git https://github.com/AsahiLinux/linux.git
|
||||
F: Documentation/devicetree/bindings/arm/apple.yaml
|
||||
F: Documentation/devicetree/bindings/arm/apple/*
|
||||
F: Documentation/devicetree/bindings/clock/apple,nco.yaml
|
||||
F: Documentation/devicetree/bindings/dma/apple,admac.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/apple,*
|
||||
F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
|
||||
@ -1864,6 +1865,7 @@ F: Documentation/devicetree/bindings/power/apple*
|
||||
F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
|
||||
F: arch/arm64/boot/dts/apple/
|
||||
F: drivers/clk/clk-apple-nco.c
|
||||
F: drivers/dma/apple-admac.c
|
||||
F: drivers/i2c/busses/i2c-pasemi-core.c
|
||||
F: drivers/i2c/busses/i2c-pasemi-platform.c
|
||||
F: drivers/iommu/apple-dart.c
|
||||
@ -6102,6 +6104,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git
|
||||
F: Documentation/devicetree/bindings/dma/
|
||||
F: Documentation/driver-api/dmaengine/
|
||||
F: drivers/dma/
|
||||
F: include/dt-bindings/dma/
|
||||
F: include/linux/dma/
|
||||
F: include/linux/dmaengine.h
|
||||
F: include/linux/of_dma.h
|
||||
@ -10188,7 +10191,8 @@ S: Supported
|
||||
Q: https://patchwork.kernel.org/project/linux-dmaengine/list/
|
||||
F: drivers/dma/ioat*
|
||||
|
||||
INTEL IADX DRIVER
|
||||
INTEL IDXD DRIVER
|
||||
M: Fenghua Yu <fenghua.yu@intel.com>
|
||||
M: Dave Jiang <dave.jiang@intel.com>
|
||||
L: dmaengine@vger.kernel.org
|
||||
S: Supported
|
||||
|
@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA
|
||||
help
|
||||
Enable support for the AMCC PPC440SPe RAID engines.
|
||||
|
||||
config APPLE_ADMAC
|
||||
tristate "Apple ADMAC support"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
select DMA_ENGINE
|
||||
default ARCH_APPLE
|
||||
help
|
||||
Enable support for Audio DMA Controller found on Apple Silicon SoCs.
|
||||
|
||||
config AT_HDMAC
|
||||
tristate "Atmel AHB DMA support"
|
||||
depends on ARCH_AT91
|
||||
|
@ -17,6 +17,7 @@ obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o
|
||||
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
|
||||
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
|
||||
obj-$(CONFIG_AMD_PTDMA) += ptdma/
|
||||
obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o
|
||||
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
|
||||
obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
|
||||
obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
|
||||
|
@ -749,7 +749,7 @@ static irqreturn_t msgdma_irq_handler(int irq, void *data)
|
||||
}
|
||||
|
||||
/**
|
||||
* msgdma_chan_remove - Channel remove function
|
||||
* msgdma_dev_remove() - Device remove function
|
||||
* @mdev: Pointer to the Altera mSGDMA device structure
|
||||
*/
|
||||
static void msgdma_dev_remove(struct msgdma_device *mdev)
|
||||
@ -918,7 +918,7 @@ fail:
|
||||
}
|
||||
|
||||
/**
|
||||
* msgdma_dma_remove - Driver remove function
|
||||
* msgdma_remove() - Driver remove function
|
||||
* @pdev: Pointer to the platform_device structure
|
||||
*
|
||||
* Return: Always '0'
|
||||
|
@ -231,7 +231,7 @@ enum pl08x_dma_chan_state {
|
||||
|
||||
/**
|
||||
* struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
|
||||
* @vc: wrappped virtual channel
|
||||
* @vc: wrapped virtual channel
|
||||
* @phychan: the physical channel utilized by this channel, if there is one
|
||||
* @name: name of channel
|
||||
* @cd: channel platform data
|
||||
|
818
drivers/dma/apple-admac.c
Normal file
818
drivers/dma/apple-admac.c
Normal file
@ -0,0 +1,818 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for Audio DMA Controller (ADMAC) on t8103 (M1) and other Apple chips
|
||||
*
|
||||
* Copyright (C) The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
|
||||
#define NCHANNELS_MAX 64
|
||||
#define IRQ_NOUTPUTS 4
|
||||
|
||||
#define RING_WRITE_SLOT GENMASK(1, 0)
|
||||
#define RING_READ_SLOT GENMASK(5, 4)
|
||||
#define RING_FULL BIT(9)
|
||||
#define RING_EMPTY BIT(8)
|
||||
#define RING_ERR BIT(10)
|
||||
|
||||
#define STATUS_DESC_DONE BIT(0)
|
||||
#define STATUS_ERR BIT(6)
|
||||
|
||||
#define FLAG_DESC_NOTIFY BIT(16)
|
||||
|
||||
#define REG_TX_START 0x0000
|
||||
#define REG_TX_STOP 0x0004
|
||||
#define REG_RX_START 0x0008
|
||||
#define REG_RX_STOP 0x000c
|
||||
|
||||
#define REG_CHAN_CTL(ch) (0x8000 + (ch) * 0x200)
|
||||
#define REG_CHAN_CTL_RST_RINGS BIT(0)
|
||||
|
||||
#define REG_DESC_RING(ch) (0x8070 + (ch) * 0x200)
|
||||
#define REG_REPORT_RING(ch) (0x8074 + (ch) * 0x200)
|
||||
|
||||
#define REG_RESIDUE(ch) (0x8064 + (ch) * 0x200)
|
||||
|
||||
#define REG_BUS_WIDTH(ch) (0x8040 + (ch) * 0x200)
|
||||
|
||||
#define BUS_WIDTH_8BIT 0x00
|
||||
#define BUS_WIDTH_16BIT 0x01
|
||||
#define BUS_WIDTH_32BIT 0x02
|
||||
#define BUS_WIDTH_FRAME_2_WORDS 0x10
|
||||
#define BUS_WIDTH_FRAME_4_WORDS 0x20
|
||||
|
||||
#define CHAN_BUFSIZE 0x8000
|
||||
|
||||
#define REG_CHAN_FIFOCTL(ch) (0x8054 + (ch) * 0x200)
|
||||
#define CHAN_FIFOCTL_LIMIT GENMASK(31, 16)
|
||||
#define CHAN_FIFOCTL_THRESHOLD GENMASK(15, 0)
|
||||
|
||||
#define REG_DESC_WRITE(ch) (0x10000 + ((ch) / 2) * 0x4 + ((ch) & 1) * 0x4000)
|
||||
#define REG_REPORT_READ(ch) (0x10100 + ((ch) / 2) * 0x4 + ((ch) & 1) * 0x4000)
|
||||
|
||||
#define REG_TX_INTSTATE(idx) (0x0030 + (idx) * 4)
|
||||
#define REG_RX_INTSTATE(idx) (0x0040 + (idx) * 4)
|
||||
#define REG_CHAN_INTSTATUS(ch, idx) (0x8010 + (ch) * 0x200 + (idx) * 4)
|
||||
#define REG_CHAN_INTMASK(ch, idx) (0x8020 + (ch) * 0x200 + (idx) * 4)
|
||||
|
||||
struct admac_data;
|
||||
struct admac_tx;
|
||||
|
||||
struct admac_chan {
|
||||
unsigned int no;
|
||||
struct admac_data *host;
|
||||
struct dma_chan chan;
|
||||
struct tasklet_struct tasklet;
|
||||
|
||||
spinlock_t lock;
|
||||
struct admac_tx *current_tx;
|
||||
int nperiod_acks;
|
||||
|
||||
/*
|
||||
* We maintain a 'submitted' and 'issued' list mainly for interface
|
||||
* correctness. Typical use of the driver (per channel) will be
|
||||
* prepping, submitting and issuing a single cyclic transaction which
|
||||
* will stay current until terminate_all is called.
|
||||
*/
|
||||
struct list_head submitted;
|
||||
struct list_head issued;
|
||||
|
||||
struct list_head to_free;
|
||||
};
|
||||
|
||||
struct admac_data {
|
||||
struct dma_device dma;
|
||||
struct device *dev;
|
||||
__iomem void *base;
|
||||
|
||||
int irq_index;
|
||||
int nchannels;
|
||||
struct admac_chan channels[];
|
||||
};
|
||||
|
||||
struct admac_tx {
|
||||
struct dma_async_tx_descriptor tx;
|
||||
bool cyclic;
|
||||
dma_addr_t buf_addr;
|
||||
dma_addr_t buf_end;
|
||||
size_t buf_len;
|
||||
size_t period_len;
|
||||
|
||||
size_t submitted_pos;
|
||||
size_t reclaimed_pos;
|
||||
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static void admac_modify(struct admac_data *ad, int reg, u32 mask, u32 val)
|
||||
{
|
||||
void __iomem *addr = ad->base + reg;
|
||||
u32 curr = readl_relaxed(addr);
|
||||
|
||||
writel_relaxed((curr & ~mask) | (val & mask), addr);
|
||||
}
|
||||
|
||||
static struct admac_chan *to_admac_chan(struct dma_chan *chan)
|
||||
{
|
||||
return container_of(chan, struct admac_chan, chan);
|
||||
}
|
||||
|
||||
static struct admac_tx *to_admac_tx(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
return container_of(tx, struct admac_tx, tx);
|
||||
}
|
||||
|
||||
static enum dma_transfer_direction admac_chan_direction(int channo)
|
||||
{
|
||||
/* Channel directions are hardwired */
|
||||
return (channo & 1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
|
||||
}
|
||||
|
||||
static dma_cookie_t admac_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
struct admac_tx *adtx = to_admac_tx(tx);
|
||||
struct admac_chan *adchan = to_admac_chan(tx->chan);
|
||||
unsigned long flags;
|
||||
dma_cookie_t cookie;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
cookie = dma_cookie_assign(tx);
|
||||
list_add_tail(&adtx->node, &adchan->submitted);
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static int admac_desc_free(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
kfree(to_admac_tx(tx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *admac_prep_dma_cyclic(
|
||||
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
|
||||
size_t period_len, enum dma_transfer_direction direction,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct admac_chan *adchan = container_of(chan, struct admac_chan, chan);
|
||||
struct admac_tx *adtx;
|
||||
|
||||
if (direction != admac_chan_direction(adchan->no))
|
||||
return NULL;
|
||||
|
||||
adtx = kzalloc(sizeof(*adtx), GFP_NOWAIT);
|
||||
if (!adtx)
|
||||
return NULL;
|
||||
|
||||
adtx->cyclic = true;
|
||||
|
||||
adtx->buf_addr = buf_addr;
|
||||
adtx->buf_len = buf_len;
|
||||
adtx->buf_end = buf_addr + buf_len;
|
||||
adtx->period_len = period_len;
|
||||
|
||||
adtx->submitted_pos = 0;
|
||||
adtx->reclaimed_pos = 0;
|
||||
|
||||
dma_async_tx_descriptor_init(&adtx->tx, chan);
|
||||
adtx->tx.tx_submit = admac_tx_submit;
|
||||
adtx->tx.desc_free = admac_desc_free;
|
||||
|
||||
return &adtx->tx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write one hardware descriptor for a dmaengine cyclic transaction.
|
||||
*/
|
||||
static void admac_cyclic_write_one_desc(struct admac_data *ad, int channo,
|
||||
struct admac_tx *tx)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
|
||||
addr = tx->buf_addr + (tx->submitted_pos % tx->buf_len);
|
||||
|
||||
/* If happens means we have buggy code */
|
||||
WARN_ON_ONCE(addr + tx->period_len > tx->buf_end);
|
||||
|
||||
dev_dbg(ad->dev, "ch%d descriptor: addr=0x%pad len=0x%zx flags=0x%lx\n",
|
||||
channo, &addr, tx->period_len, FLAG_DESC_NOTIFY);
|
||||
|
||||
writel_relaxed(lower_32_bits(addr), ad->base + REG_DESC_WRITE(channo));
|
||||
writel_relaxed(upper_32_bits(addr), ad->base + REG_DESC_WRITE(channo));
|
||||
writel_relaxed(tx->period_len, ad->base + REG_DESC_WRITE(channo));
|
||||
writel_relaxed(FLAG_DESC_NOTIFY, ad->base + REG_DESC_WRITE(channo));
|
||||
|
||||
tx->submitted_pos += tx->period_len;
|
||||
tx->submitted_pos %= 2 * tx->buf_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write all the hardware descriptors for a dmaengine cyclic
|
||||
* transaction there is space for.
|
||||
*/
|
||||
static void admac_cyclic_write_desc(struct admac_data *ad, int channo,
|
||||
struct admac_tx *tx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (readl_relaxed(ad->base + REG_DESC_RING(channo)) & RING_FULL)
|
||||
break;
|
||||
admac_cyclic_write_one_desc(ad, channo, tx);
|
||||
}
|
||||
}
|
||||
|
||||
static int admac_ring_noccupied_slots(int ringval)
|
||||
{
|
||||
int wrslot = FIELD_GET(RING_WRITE_SLOT, ringval);
|
||||
int rdslot = FIELD_GET(RING_READ_SLOT, ringval);
|
||||
|
||||
if (wrslot != rdslot) {
|
||||
return (wrslot + 4 - rdslot) % 4;
|
||||
} else {
|
||||
WARN_ON((ringval & (RING_FULL | RING_EMPTY)) == 0);
|
||||
|
||||
if (ringval & RING_FULL)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from hardware the residue of a cyclic dmaengine transaction.
|
||||
*/
|
||||
static u32 admac_cyclic_read_residue(struct admac_data *ad, int channo,
|
||||
struct admac_tx *adtx)
|
||||
{
|
||||
u32 ring1, ring2;
|
||||
u32 residue1, residue2;
|
||||
int nreports;
|
||||
size_t pos;
|
||||
|
||||
ring1 = readl_relaxed(ad->base + REG_REPORT_RING(channo));
|
||||
residue1 = readl_relaxed(ad->base + REG_RESIDUE(channo));
|
||||
ring2 = readl_relaxed(ad->base + REG_REPORT_RING(channo));
|
||||
residue2 = readl_relaxed(ad->base + REG_RESIDUE(channo));
|
||||
|
||||
if (residue2 > residue1) {
|
||||
/*
|
||||
* Controller must have loaded next descriptor between
|
||||
* the two residue reads
|
||||
*/
|
||||
nreports = admac_ring_noccupied_slots(ring1) + 1;
|
||||
} else {
|
||||
/* No descriptor load between the two reads, ring2 is safe to use */
|
||||
nreports = admac_ring_noccupied_slots(ring2);
|
||||
}
|
||||
|
||||
pos = adtx->reclaimed_pos + adtx->period_len * (nreports + 1) - residue2;
|
||||
|
||||
return adtx->buf_len - pos % adtx->buf_len;
|
||||
}
|
||||
|
||||
static enum dma_status admac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
||||
struct dma_tx_state *txstate)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_data *ad = adchan->host;
|
||||
struct admac_tx *adtx;
|
||||
|
||||
enum dma_status ret;
|
||||
size_t residue;
|
||||
unsigned long flags;
|
||||
|
||||
ret = dma_cookie_status(chan, cookie, txstate);
|
||||
if (ret == DMA_COMPLETE || !txstate)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
adtx = adchan->current_tx;
|
||||
|
||||
if (adtx && adtx->tx.cookie == cookie) {
|
||||
ret = DMA_IN_PROGRESS;
|
||||
residue = admac_cyclic_read_residue(ad, adchan->no, adtx);
|
||||
} else {
|
||||
ret = DMA_IN_PROGRESS;
|
||||
residue = 0;
|
||||
list_for_each_entry(adtx, &adchan->issued, node) {
|
||||
if (adtx->tx.cookie == cookie) {
|
||||
residue = adtx->buf_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
dma_set_residue(txstate, residue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void admac_start_chan(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
u32 startbit = 1 << (adchan->no / 2);
|
||||
|
||||
writel_relaxed(STATUS_DESC_DONE | STATUS_ERR,
|
||||
ad->base + REG_CHAN_INTSTATUS(adchan->no, ad->irq_index));
|
||||
writel_relaxed(STATUS_DESC_DONE | STATUS_ERR,
|
||||
ad->base + REG_CHAN_INTMASK(adchan->no, ad->irq_index));
|
||||
|
||||
switch (admac_chan_direction(adchan->no)) {
|
||||
case DMA_MEM_TO_DEV:
|
||||
writel_relaxed(startbit, ad->base + REG_TX_START);
|
||||
break;
|
||||
case DMA_DEV_TO_MEM:
|
||||
writel_relaxed(startbit, ad->base + REG_RX_START);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev_dbg(adchan->host->dev, "ch%d start\n", adchan->no);
|
||||
}
|
||||
|
||||
static void admac_stop_chan(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
u32 stopbit = 1 << (adchan->no / 2);
|
||||
|
||||
switch (admac_chan_direction(adchan->no)) {
|
||||
case DMA_MEM_TO_DEV:
|
||||
writel_relaxed(stopbit, ad->base + REG_TX_STOP);
|
||||
break;
|
||||
case DMA_DEV_TO_MEM:
|
||||
writel_relaxed(stopbit, ad->base + REG_RX_STOP);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev_dbg(adchan->host->dev, "ch%d stop\n", adchan->no);
|
||||
}
|
||||
|
||||
static void admac_reset_rings(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
|
||||
writel_relaxed(REG_CHAN_CTL_RST_RINGS,
|
||||
ad->base + REG_CHAN_CTL(adchan->no));
|
||||
writel_relaxed(0, ad->base + REG_CHAN_CTL(adchan->no));
|
||||
}
|
||||
|
||||
static void admac_start_current_tx(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
int ch = adchan->no;
|
||||
|
||||
admac_reset_rings(adchan);
|
||||
writel_relaxed(0, ad->base + REG_CHAN_CTL(ch));
|
||||
|
||||
admac_cyclic_write_one_desc(ad, ch, adchan->current_tx);
|
||||
admac_start_chan(adchan);
|
||||
admac_cyclic_write_desc(ad, ch, adchan->current_tx);
|
||||
}
|
||||
|
||||
static void admac_issue_pending(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_tx *tx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
list_splice_tail_init(&adchan->submitted, &adchan->issued);
|
||||
if (!list_empty(&adchan->issued) && !adchan->current_tx) {
|
||||
tx = list_first_entry(&adchan->issued, struct admac_tx, node);
|
||||
list_del(&tx->node);
|
||||
|
||||
adchan->current_tx = tx;
|
||||
adchan->nperiod_acks = 0;
|
||||
admac_start_current_tx(adchan);
|
||||
}
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
}
|
||||
|
||||
static int admac_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
|
||||
admac_stop_chan(adchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_resume(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
|
||||
admac_start_chan(adchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_terminate_all(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
admac_stop_chan(adchan);
|
||||
admac_reset_rings(adchan);
|
||||
|
||||
adchan->current_tx = NULL;
|
||||
/*
|
||||
* Descriptors can only be freed after the tasklet
|
||||
* has been killed (in admac_synchronize).
|
||||
*/
|
||||
list_splice_tail_init(&adchan->submitted, &adchan->to_free);
|
||||
list_splice_tail_init(&adchan->issued, &adchan->to_free);
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void admac_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_tx *adtx, *_adtx;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(head);
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
list_splice_tail_init(&adchan->to_free, &head);
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
tasklet_kill(&adchan->tasklet);
|
||||
|
||||
list_for_each_entry_safe(adtx, _adtx, &head, node) {
|
||||
list_del(&adtx->node);
|
||||
admac_desc_free(&adtx->tx);
|
||||
}
|
||||
}
|
||||
|
||||
static int admac_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
|
||||
dma_cookie_init(&adchan->chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void admac_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
admac_terminate_all(chan);
|
||||
admac_synchronize(chan);
|
||||
}
|
||||
|
||||
static struct dma_chan *admac_dma_of_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
struct admac_data *ad = (struct admac_data *) ofdma->of_dma_data;
|
||||
unsigned int index;
|
||||
|
||||
if (dma_spec->args_count != 1)
|
||||
return NULL;
|
||||
|
||||
index = dma_spec->args[0];
|
||||
|
||||
if (index >= ad->nchannels) {
|
||||
dev_err(ad->dev, "channel index %u out of bounds\n", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &ad->channels[index].chan;
|
||||
}
|
||||
|
||||
static int admac_drain_reports(struct admac_data *ad, int channo)
|
||||
{
|
||||
int count;
|
||||
|
||||
for (count = 0; count < 4; count++) {
|
||||
u32 countval_hi, countval_lo, unk1, flags;
|
||||
|
||||
if (readl_relaxed(ad->base + REG_REPORT_RING(channo)) & RING_EMPTY)
|
||||
break;
|
||||
|
||||
countval_lo = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
countval_hi = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
unk1 = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
flags = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
|
||||
dev_dbg(ad->dev, "ch%d report: countval=0x%llx unk1=0x%x flags=0x%x\n",
|
||||
channo, ((u64) countval_hi) << 32 | countval_lo, unk1, flags);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void admac_handle_status_err(struct admac_data *ad, int channo)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if (readl_relaxed(ad->base + REG_DESC_RING(channo)) & RING_ERR) {
|
||||
writel_relaxed(RING_ERR, ad->base + REG_DESC_RING(channo));
|
||||
dev_err_ratelimited(ad->dev, "ch%d descriptor ring error\n", channo);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (readl_relaxed(ad->base + REG_REPORT_RING(channo)) & RING_ERR) {
|
||||
writel_relaxed(RING_ERR, ad->base + REG_REPORT_RING(channo));
|
||||
dev_err_ratelimited(ad->dev, "ch%d report ring error\n", channo);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (unlikely(!handled)) {
|
||||
dev_err(ad->dev, "ch%d unknown error, masking errors as cause of IRQs\n", channo);
|
||||
admac_modify(ad, REG_CHAN_INTMASK(channo, ad->irq_index),
|
||||
STATUS_ERR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void admac_handle_status_desc_done(struct admac_data *ad, int channo)
|
||||
{
|
||||
struct admac_chan *adchan = &ad->channels[channo];
|
||||
unsigned long flags;
|
||||
int nreports;
|
||||
|
||||
writel_relaxed(STATUS_DESC_DONE,
|
||||
ad->base + REG_CHAN_INTSTATUS(channo, ad->irq_index));
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
nreports = admac_drain_reports(ad, channo);
|
||||
|
||||
if (adchan->current_tx) {
|
||||
struct admac_tx *tx = adchan->current_tx;
|
||||
|
||||
adchan->nperiod_acks += nreports;
|
||||
tx->reclaimed_pos += nreports * tx->period_len;
|
||||
tx->reclaimed_pos %= 2 * tx->buf_len;
|
||||
|
||||
admac_cyclic_write_desc(ad, channo, tx);
|
||||
tasklet_schedule(&adchan->tasklet);
|
||||
}
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
}
|
||||
|
||||
static void admac_handle_chan_int(struct admac_data *ad, int no)
|
||||
{
|
||||
u32 cause = readl_relaxed(ad->base + REG_CHAN_INTSTATUS(no, ad->irq_index));
|
||||
|
||||
if (cause & STATUS_ERR)
|
||||
admac_handle_status_err(ad, no);
|
||||
|
||||
if (cause & STATUS_DESC_DONE)
|
||||
admac_handle_status_desc_done(ad, no);
|
||||
}
|
||||
|
||||
static irqreturn_t admac_interrupt(int irq, void *devid)
|
||||
{
|
||||
struct admac_data *ad = devid;
|
||||
u32 rx_intstate, tx_intstate;
|
||||
int i;
|
||||
|
||||
rx_intstate = readl_relaxed(ad->base + REG_RX_INTSTATE(ad->irq_index));
|
||||
tx_intstate = readl_relaxed(ad->base + REG_TX_INTSTATE(ad->irq_index));
|
||||
|
||||
if (!tx_intstate && !rx_intstate)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ad->nchannels; i += 2) {
|
||||
if (tx_intstate & 1)
|
||||
admac_handle_chan_int(ad, i);
|
||||
tx_intstate >>= 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < ad->nchannels; i += 2) {
|
||||
if (rx_intstate & 1)
|
||||
admac_handle_chan_int(ad, i);
|
||||
rx_intstate >>= 1;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void admac_chan_tasklet(struct tasklet_struct *t)
|
||||
{
|
||||
struct admac_chan *adchan = from_tasklet(adchan, t, tasklet);
|
||||
struct admac_tx *adtx;
|
||||
struct dmaengine_desc_callback cb;
|
||||
struct dmaengine_result tx_result;
|
||||
int nacks;
|
||||
|
||||
spin_lock_irq(&adchan->lock);
|
||||
adtx = adchan->current_tx;
|
||||
nacks = adchan->nperiod_acks;
|
||||
adchan->nperiod_acks = 0;
|
||||
spin_unlock_irq(&adchan->lock);
|
||||
|
||||
if (!adtx || !nacks)
|
||||
return;
|
||||
|
||||
tx_result.result = DMA_TRANS_NOERROR;
|
||||
tx_result.residue = 0;
|
||||
|
||||
dmaengine_desc_get_callback(&adtx->tx, &cb);
|
||||
while (nacks--)
|
||||
dmaengine_desc_callback_invoke(&cb, &tx_result);
|
||||
}
|
||||
|
||||
static int admac_device_config(struct dma_chan *chan,
|
||||
struct dma_slave_config *config)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_data *ad = adchan->host;
|
||||
bool is_tx = admac_chan_direction(adchan->no) == DMA_MEM_TO_DEV;
|
||||
int wordsize = 0;
|
||||
u32 bus_width = 0;
|
||||
|
||||
switch (is_tx ? config->dst_addr_width : config->src_addr_width) {
|
||||
case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
||||
wordsize = 1;
|
||||
bus_width |= BUS_WIDTH_8BIT;
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
||||
wordsize = 2;
|
||||
bus_width |= BUS_WIDTH_16BIT;
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
||||
wordsize = 4;
|
||||
bus_width |= BUS_WIDTH_32BIT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We take port_window_size to be the number of words in a frame.
|
||||
*
|
||||
* The controller has some means of out-of-band signalling, to the peripheral,
|
||||
* of words position in a frame. That's where the importance of this control
|
||||
* comes from.
|
||||
*/
|
||||
switch (is_tx ? config->dst_port_window_size : config->src_port_window_size) {
|
||||
case 0 ... 1:
|
||||
break;
|
||||
case 2:
|
||||
bus_width |= BUS_WIDTH_FRAME_2_WORDS;
|
||||
break;
|
||||
case 4:
|
||||
bus_width |= BUS_WIDTH_FRAME_4_WORDS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel_relaxed(bus_width, ad->base + REG_BUS_WIDTH(adchan->no));
|
||||
|
||||
/*
|
||||
* By FIFOCTL_LIMIT we seem to set the maximal number of bytes allowed to be
|
||||
* held in controller's per-channel FIFO. Transfers seem to be triggered
|
||||
* around the time FIFO occupancy touches FIFOCTL_THRESHOLD.
|
||||
*
|
||||
* The numbers we set are more or less arbitrary.
|
||||
*/
|
||||
writel_relaxed(FIELD_PREP(CHAN_FIFOCTL_LIMIT, 0x30 * wordsize)
|
||||
| FIELD_PREP(CHAN_FIFOCTL_THRESHOLD, 0x18 * wordsize),
|
||||
ad->base + REG_CHAN_FIFOCTL(adchan->no));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct admac_data *ad;
|
||||
struct dma_device *dma;
|
||||
int nchannels;
|
||||
int err, irq, i;
|
||||
|
||||
err = of_property_read_u32(np, "dma-channels", &nchannels);
|
||||
if (err || nchannels > NCHANNELS_MAX) {
|
||||
dev_err(&pdev->dev, "missing or invalid dma-channels property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ad = devm_kzalloc(&pdev->dev, struct_size(ad, channels, nchannels), GFP_KERNEL);
|
||||
if (!ad)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ad);
|
||||
ad->dev = &pdev->dev;
|
||||
ad->nchannels = nchannels;
|
||||
|
||||
/*
|
||||
* The controller has 4 IRQ outputs. Try them all until
|
||||
* we find one we can use.
|
||||
*/
|
||||
for (i = 0; i < IRQ_NOUTPUTS; i++) {
|
||||
irq = platform_get_irq_optional(pdev, i);
|
||||
if (irq >= 0) {
|
||||
ad->irq_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq < 0)
|
||||
return dev_err_probe(&pdev->dev, irq, "no usable interrupt\n");
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, admac_interrupt,
|
||||
0, dev_name(&pdev->dev), ad);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err,
|
||||
"unable to register interrupt\n");
|
||||
|
||||
ad->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ad->base))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(ad->base),
|
||||
"unable to obtain MMIO resource\n");
|
||||
|
||||
dma = &ad->dma;
|
||||
|
||||
dma_cap_set(DMA_PRIVATE, dma->cap_mask);
|
||||
dma_cap_set(DMA_CYCLIC, dma->cap_mask);
|
||||
|
||||
dma->dev = &pdev->dev;
|
||||
dma->device_alloc_chan_resources = admac_alloc_chan_resources;
|
||||
dma->device_free_chan_resources = admac_free_chan_resources;
|
||||
dma->device_tx_status = admac_tx_status;
|
||||
dma->device_issue_pending = admac_issue_pending;
|
||||
dma->device_terminate_all = admac_terminate_all;
|
||||
dma->device_synchronize = admac_synchronize;
|
||||
dma->device_prep_dma_cyclic = admac_prep_dma_cyclic;
|
||||
dma->device_config = admac_device_config;
|
||||
dma->device_pause = admac_pause;
|
||||
dma->device_resume = admac_resume;
|
||||
|
||||
dma->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
|
||||
dma->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
|
||||
|
||||
INIT_LIST_HEAD(&dma->channels);
|
||||
for (i = 0; i < nchannels; i++) {
|
||||
struct admac_chan *adchan = &ad->channels[i];
|
||||
|
||||
adchan->host = ad;
|
||||
adchan->no = i;
|
||||
adchan->chan.device = &ad->dma;
|
||||
spin_lock_init(&adchan->lock);
|
||||
INIT_LIST_HEAD(&adchan->submitted);
|
||||
INIT_LIST_HEAD(&adchan->issued);
|
||||
INIT_LIST_HEAD(&adchan->to_free);
|
||||
list_add_tail(&adchan->chan.device_node, &dma->channels);
|
||||
tasklet_setup(&adchan->tasklet, admac_chan_tasklet);
|
||||
}
|
||||
|
||||
err = dma_async_device_register(&ad->dma);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err, "failed to register DMA device\n");
|
||||
|
||||
err = of_dma_controller_register(pdev->dev.of_node, admac_dma_of_xlate, ad);
|
||||
if (err) {
|
||||
dma_async_device_unregister(&ad->dma);
|
||||
return dev_err_probe(&pdev->dev, err, "failed to register with OF\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct admac_data *ad = platform_get_drvdata(pdev);
|
||||
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
dma_async_device_unregister(&ad->dma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id admac_of_match[] = {
|
||||
{ .compatible = "apple,admac", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, admac_of_match);
|
||||
|
||||
static struct platform_driver apple_admac_driver = {
|
||||
.driver = {
|
||||
.name = "apple-admac",
|
||||
.of_match_table = admac_of_match,
|
||||
},
|
||||
.probe = admac_probe,
|
||||
.remove = admac_remove,
|
||||
};
|
||||
module_platform_driver(apple_admac_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
|
||||
MODULE_DESCRIPTION("Driver for Audio DMA Controller (ADMAC) on Apple SoCs");
|
||||
MODULE_LICENSE("GPL");
|
@ -649,7 +649,7 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
|
||||
}
|
||||
|
||||
/*
|
||||
* Only check that maxburst and addr width values are supported by the
|
||||
* Only check that maxburst and addr width values are supported by
|
||||
* the controller but not that the configuration is good to perform the
|
||||
* transfer since we don't know the direction at this stage.
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
@ -55,6 +56,9 @@
|
||||
#define AXI_DMAC_DMA_DST_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_TYPE_MSK, x)
|
||||
#define AXI_DMAC_DMA_DST_WIDTH_MSK GENMASK(3, 0)
|
||||
#define AXI_DMAC_DMA_DST_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_WIDTH_MSK, x)
|
||||
#define AXI_DMAC_REG_COHERENCY_DESC 0x14
|
||||
#define AXI_DMAC_DST_COHERENT_MSK BIT(0)
|
||||
#define AXI_DMAC_DST_COHERENT_GET(x) FIELD_GET(AXI_DMAC_DST_COHERENT_MSK, x)
|
||||
|
||||
#define AXI_DMAC_REG_IRQ_MASK 0x80
|
||||
#define AXI_DMAC_REG_IRQ_PENDING 0x84
|
||||
@ -979,6 +983,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
|
||||
|
||||
axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x00);
|
||||
|
||||
if (of_dma_is_coherent(pdev->dev.of_node)) {
|
||||
ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC);
|
||||
|
||||
if (version < ADI_AXI_PCORE_VER(4, 4, 'a') ||
|
||||
!AXI_DMAC_DST_COHERENT_GET(ret)) {
|
||||
dev_err(dmac->dma_dev.dev,
|
||||
"Coherent DMA not supported in hardware");
|
||||
ret = -EINVAL;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dma_async_device_register(dma_dev);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
@ -388,7 +388,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg(
|
||||
|
||||
if (i != (sg_len - 1) &&
|
||||
!(jzdma->soc_data->flags & JZ_SOC_DATA_BREAK_LINKS)) {
|
||||
/* Automatically proceeed to the next descriptor. */
|
||||
/* Automatically proceed to the next descriptor. */
|
||||
desc->desc[i].dcm |= JZ_DMA_DCM_LINK;
|
||||
|
||||
/*
|
||||
|
@ -1153,13 +1153,6 @@ int dma_async_device_register(struct dma_device *device)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dma_has_cap(DMA_MEMCPY_SG, device->cap_mask) && !device->device_prep_dma_memcpy_sg) {
|
||||
dev_err(device->dev,
|
||||
"Device claims capability %s, but op is not defined\n",
|
||||
"DMA_MEMCPY_SG");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dma_has_cap(DMA_XOR, device->cap_mask) && !device->device_prep_dma_xor) {
|
||||
dev_err(device->dev,
|
||||
"Device claims capability %s, but op is not defined\n",
|
||||
|
@ -22,51 +22,50 @@
|
||||
#include <linux/wait.h>
|
||||
|
||||
static unsigned int test_buf_size = 16384;
|
||||
module_param(test_buf_size, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(test_buf_size, uint, 0644);
|
||||
MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
|
||||
|
||||
static char test_device[32];
|
||||
module_param_string(device, test_device, sizeof(test_device),
|
||||
S_IRUGO | S_IWUSR);
|
||||
module_param_string(device, test_device, sizeof(test_device), 0644);
|
||||
MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)");
|
||||
|
||||
static unsigned int threads_per_chan = 1;
|
||||
module_param(threads_per_chan, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(threads_per_chan, uint, 0644);
|
||||
MODULE_PARM_DESC(threads_per_chan,
|
||||
"Number of threads to start per channel (default: 1)");
|
||||
|
||||
static unsigned int max_channels;
|
||||
module_param(max_channels, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(max_channels, uint, 0644);
|
||||
MODULE_PARM_DESC(max_channels,
|
||||
"Maximum number of channels to use (default: all)");
|
||||
|
||||
static unsigned int iterations;
|
||||
module_param(iterations, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(iterations, uint, 0644);
|
||||
MODULE_PARM_DESC(iterations,
|
||||
"Iterations before stopping test (default: infinite)");
|
||||
|
||||
static unsigned int dmatest;
|
||||
module_param(dmatest, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(dmatest, uint, 0644);
|
||||
MODULE_PARM_DESC(dmatest,
|
||||
"dmatest 0-memcpy 1-memset (default: 0)");
|
||||
|
||||
static unsigned int xor_sources = 3;
|
||||
module_param(xor_sources, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(xor_sources, uint, 0644);
|
||||
MODULE_PARM_DESC(xor_sources,
|
||||
"Number of xor source buffers (default: 3)");
|
||||
|
||||
static unsigned int pq_sources = 3;
|
||||
module_param(pq_sources, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(pq_sources, uint, 0644);
|
||||
MODULE_PARM_DESC(pq_sources,
|
||||
"Number of p+q source buffers (default: 3)");
|
||||
|
||||
static int timeout = 3000;
|
||||
module_param(timeout, int, S_IRUGO | S_IWUSR);
|
||||
module_param(timeout, int, 0644);
|
||||
MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
|
||||
"Pass -1 for infinite timeout");
|
||||
|
||||
static bool noverify;
|
||||
module_param(noverify, bool, S_IRUGO | S_IWUSR);
|
||||
module_param(noverify, bool, 0644);
|
||||
MODULE_PARM_DESC(noverify, "Disable data verification (default: verify)");
|
||||
|
||||
static bool norandom;
|
||||
@ -74,7 +73,7 @@ module_param(norandom, bool, 0644);
|
||||
MODULE_PARM_DESC(norandom, "Disable random offset setup (default: random)");
|
||||
|
||||
static bool verbose;
|
||||
module_param(verbose, bool, S_IRUGO | S_IWUSR);
|
||||
module_param(verbose, bool, 0644);
|
||||
MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)");
|
||||
|
||||
static int alignment = -1;
|
||||
@ -86,7 +85,7 @@ module_param(transfer_size, uint, 0644);
|
||||
MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))");
|
||||
|
||||
static bool polled;
|
||||
module_param(polled, bool, S_IRUGO | S_IWUSR);
|
||||
module_param(polled, bool, 0644);
|
||||
MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts");
|
||||
|
||||
/**
|
||||
@ -154,7 +153,7 @@ static const struct kernel_param_ops run_ops = {
|
||||
.get = dmatest_run_get,
|
||||
};
|
||||
static bool dmatest_run;
|
||||
module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR);
|
||||
module_param_cb(run, &run_ops, &dmatest_run, 0644);
|
||||
MODULE_PARM_DESC(run, "Run the test (default: false)");
|
||||
|
||||
static int dmatest_chan_set(const char *val, const struct kernel_param *kp);
|
||||
@ -290,7 +289,7 @@ static const struct kernel_param_ops wait_ops = {
|
||||
.get = dmatest_wait_get,
|
||||
.set = param_set_bool,
|
||||
};
|
||||
module_param_cb(wait, &wait_ops, &wait, S_IRUGO);
|
||||
module_param_cb(wait, &wait_ops, &wait, 0444);
|
||||
MODULE_PARM_DESC(wait, "Wait for tests to complete (default: false)");
|
||||
|
||||
static bool dmatest_match_channel(struct dmatest_params *params,
|
||||
@ -579,10 +578,10 @@ static int dmatest_func(void *data)
|
||||
unsigned int total_tests = 0;
|
||||
dma_cookie_t cookie;
|
||||
enum dma_status status;
|
||||
enum dma_ctrl_flags flags;
|
||||
enum dma_ctrl_flags flags;
|
||||
u8 *pq_coefs = NULL;
|
||||
int ret;
|
||||
unsigned int buf_size;
|
||||
unsigned int buf_size;
|
||||
struct dmatest_data *src;
|
||||
struct dmatest_data *dst;
|
||||
int i;
|
||||
@ -1095,8 +1094,8 @@ static void add_threaded_test(struct dmatest_info *info)
|
||||
|
||||
/* Copy test parameters */
|
||||
params->buf_size = test_buf_size;
|
||||
strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
|
||||
strlcpy(params->device, strim(test_device), sizeof(params->device));
|
||||
strscpy(params->channel, strim(test_channel), sizeof(params->channel));
|
||||
strscpy(params->device, strim(test_device), sizeof(params->device));
|
||||
params->threads_per_chan = threads_per_chan;
|
||||
params->max_channels = max_channels;
|
||||
params->iterations = iterations;
|
||||
@ -1240,7 +1239,7 @@ static int dmatest_chan_set(const char *val, const struct kernel_param *kp)
|
||||
dtc = list_last_entry(&info->channels,
|
||||
struct dmatest_chan,
|
||||
node);
|
||||
strlcpy(chan_reset_val,
|
||||
strscpy(chan_reset_val,
|
||||
dma_chan_name(dtc->chan),
|
||||
sizeof(chan_reset_val));
|
||||
ret = -EBUSY;
|
||||
@ -1263,14 +1262,14 @@ static int dmatest_chan_set(const char *val, const struct kernel_param *kp)
|
||||
if ((strcmp(dma_chan_name(dtc->chan), strim(test_channel)) != 0)
|
||||
&& (strcmp("", strim(test_channel)) != 0)) {
|
||||
ret = -EINVAL;
|
||||
strlcpy(chan_reset_val, dma_chan_name(dtc->chan),
|
||||
strscpy(chan_reset_val, dma_chan_name(dtc->chan),
|
||||
sizeof(chan_reset_val));
|
||||
goto add_chan_err;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Clear test_channel if no channels were added successfully */
|
||||
strlcpy(chan_reset_val, "", sizeof(chan_reset_val));
|
||||
strscpy(chan_reset_val, "", sizeof(chan_reset_val));
|
||||
ret = -EBUSY;
|
||||
goto add_chan_err;
|
||||
}
|
||||
@ -1295,7 +1294,7 @@ static int dmatest_chan_get(char *val, const struct kernel_param *kp)
|
||||
mutex_lock(&info->lock);
|
||||
if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) {
|
||||
stop_threaded_test(info);
|
||||
strlcpy(test_channel, "", sizeof(test_channel));
|
||||
strscpy(test_channel, "", sizeof(test_channel));
|
||||
}
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
|
@ -982,6 +982,11 @@ static int dw_axi_dma_chan_slave_config(struct dma_chan *dchan,
|
||||
static void axi_chan_dump_lli(struct axi_dma_chan *chan,
|
||||
struct axi_dma_hw_desc *desc)
|
||||
{
|
||||
if (!desc->lli) {
|
||||
dev_err(dchan2dev(&chan->vc.chan), "NULL LLI\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_err(dchan2dev(&chan->vc.chan),
|
||||
"SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x",
|
||||
le64_to_cpu(desc->lli->sar),
|
||||
@ -1049,6 +1054,11 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
|
||||
|
||||
/* The completed descriptor currently is in the head of vc list */
|
||||
vd = vchan_next_desc(&chan->vc);
|
||||
if (!vd) {
|
||||
dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n",
|
||||
axi_chan_name(chan));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chan->cyclic) {
|
||||
desc = vd_to_axi_desc(vd);
|
||||
@ -1078,6 +1088,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
|
||||
axi_chan_start_first_queued(chan);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
}
|
||||
|
||||
|
@ -414,19 +414,11 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
|
||||
SET_CH_32(dw, chan->dir, chan->id, ch_control1,
|
||||
(DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
|
||||
/* Linked list */
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* llp is not aligned on 64bit -> keep 32bit accesses */
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
|
||||
lower_32_bits(chunk->ll_region.paddr));
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
|
||||
upper_32_bits(chunk->ll_region.paddr));
|
||||
#else /* CONFIG_64BIT */
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
|
||||
lower_32_bits(chunk->ll_region.paddr));
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
|
||||
upper_32_bits(chunk->ll_region.paddr));
|
||||
#endif /* CONFIG_64BIT */
|
||||
}
|
||||
/* Doorbell */
|
||||
SET_RW_32(dw, chan->dir, doorbell,
|
||||
|
@ -102,10 +102,12 @@ free_map:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id rzn1_dmac_match[] = {
|
||||
{ .compatible = "renesas,rzn1-dma" },
|
||||
{}
|
||||
};
|
||||
#endif
|
||||
|
||||
static int rzn1_dmamux_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -140,6 +142,7 @@ static const struct of_device_id rzn1_dmamux_match[] = {
|
||||
{ .compatible = "renesas,rzn1-dmamux" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzn1_dmamux_match);
|
||||
|
||||
static struct platform_driver rzn1_dmamux_driver = {
|
||||
.driver = {
|
||||
|
@ -1183,7 +1183,7 @@ fail:
|
||||
*
|
||||
* Synchronizes 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 associated
|
||||
* descriptors have stopped and it is safe to free the memory associated
|
||||
* 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.
|
||||
|
@ -559,9 +559,6 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
|
||||
}
|
||||
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
/* get next sg's physical address */
|
||||
last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
|
||||
|
||||
if (direction == DMA_MEM_TO_DEV) {
|
||||
src_addr = sg_dma_address(sg);
|
||||
dst_addr = fsl_chan->dma_dev_addr;
|
||||
|
@ -1047,7 +1047,7 @@ static int __init imxdma_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
imxdma->dev = &pdev->dev;
|
||||
imxdma->devtype = (enum imx_dma_type)of_device_get_match_data(&pdev->dev);
|
||||
imxdma->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
imxdma->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -183,12 +183,14 @@
|
||||
BIT(DMA_DEV_TO_DEV))
|
||||
|
||||
#define SDMA_WATERMARK_LEVEL_N_FIFOS GENMASK(15, 12)
|
||||
#define SDMA_WATERMARK_LEVEL_OFF_FIFOS GENMASK(19, 16)
|
||||
#define SDMA_WATERMARK_LEVEL_WORDS_PER_FIFO GENMASK(31, 28)
|
||||
#define SDMA_WATERMARK_LEVEL_SW_DONE BIT(23)
|
||||
|
||||
#define SDMA_DONE0_CONFIG_DONE_SEL BIT(7)
|
||||
#define SDMA_DONE0_CONFIG_DONE_DIS BIT(6)
|
||||
|
||||
/**
|
||||
/*
|
||||
* struct sdma_script_start_addrs - SDMA script start pointers
|
||||
*
|
||||
* start addresses of the different functions in the physical
|
||||
@ -424,6 +426,14 @@ struct sdma_desc {
|
||||
* @data: specific sdma interface structure
|
||||
* @bd_pool: dma_pool for bd
|
||||
* @terminate_worker: used to call back into terminate work function
|
||||
* @terminated: terminated list
|
||||
* @is_ram_script: flag for script in ram
|
||||
* @n_fifos_src: number of source device fifos
|
||||
* @n_fifos_dst: number of destination device fifos
|
||||
* @sw_done: software done flag
|
||||
* @stride_fifos_src: stride for source device FIFOs
|
||||
* @stride_fifos_dst: stride for destination device FIFOs
|
||||
* @words_per_fifo: copy number of words one time for one FIFO
|
||||
*/
|
||||
struct sdma_channel {
|
||||
struct virt_dma_chan vc;
|
||||
@ -451,6 +461,9 @@ struct sdma_channel {
|
||||
bool is_ram_script;
|
||||
unsigned int n_fifos_src;
|
||||
unsigned int n_fifos_dst;
|
||||
unsigned int stride_fifos_src;
|
||||
unsigned int stride_fifos_dst;
|
||||
unsigned int words_per_fifo;
|
||||
bool sw_done;
|
||||
};
|
||||
|
||||
@ -1240,17 +1253,29 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
|
||||
static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac)
|
||||
{
|
||||
unsigned int n_fifos;
|
||||
unsigned int stride_fifos;
|
||||
unsigned int words_per_fifo;
|
||||
|
||||
if (sdmac->sw_done)
|
||||
sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE;
|
||||
|
||||
if (sdmac->direction == DMA_DEV_TO_MEM)
|
||||
if (sdmac->direction == DMA_DEV_TO_MEM) {
|
||||
n_fifos = sdmac->n_fifos_src;
|
||||
else
|
||||
stride_fifos = sdmac->stride_fifos_src;
|
||||
} else {
|
||||
n_fifos = sdmac->n_fifos_dst;
|
||||
stride_fifos = sdmac->stride_fifos_dst;
|
||||
}
|
||||
|
||||
words_per_fifo = sdmac->words_per_fifo;
|
||||
|
||||
sdmac->watermark_level |=
|
||||
FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos);
|
||||
sdmac->watermark_level |=
|
||||
FIELD_PREP(SDMA_WATERMARK_LEVEL_OFF_FIFOS, stride_fifos);
|
||||
if (words_per_fifo)
|
||||
sdmac->watermark_level |=
|
||||
FIELD_PREP(SDMA_WATERMARK_LEVEL_WORDS_PER_FIFO, (words_per_fifo - 1));
|
||||
}
|
||||
|
||||
static int sdma_config_channel(struct dma_chan *chan)
|
||||
@ -1764,6 +1789,9 @@ static int sdma_config(struct dma_chan *chan,
|
||||
}
|
||||
sdmac->n_fifos_src = sdmacfg->n_fifos_src;
|
||||
sdmac->n_fifos_dst = sdmacfg->n_fifos_dst;
|
||||
sdmac->stride_fifos_src = sdmacfg->stride_fifos_src;
|
||||
sdmac->stride_fifos_dst = sdmacfg->stride_fifos_dst;
|
||||
sdmac->words_per_fifo = sdmacfg->words_per_fifo;
|
||||
sdmac->sw_done = sdmacfg->sw_done;
|
||||
}
|
||||
|
||||
@ -2183,8 +2211,8 @@ static int sdma_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, "sdma",
|
||||
sdma);
|
||||
ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0,
|
||||
dev_name(&pdev->dev), sdma);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
|
@ -373,7 +373,7 @@ static void mtk_cqdma_tasklet_cb(struct tasklet_struct *t)
|
||||
|
||||
/*
|
||||
* free child CVD after completion.
|
||||
* the parent CVD would be freeed with desc_free by user.
|
||||
* the parent CVD would be freed with desc_free by user.
|
||||
*/
|
||||
if (cvd->parent != cvd)
|
||||
kfree(cvd);
|
||||
|
@ -138,7 +138,7 @@ struct mtk_hsdma_vdesc {
|
||||
|
||||
/**
|
||||
* struct mtk_hsdma_cb - This is the struct holding extra info required for RX
|
||||
* ring to know what relevant VD the the PD is being
|
||||
* ring to know what relevant VD the PD is being
|
||||
* mapped to.
|
||||
* @vd: Pointer to the relevant VD.
|
||||
* @flag: Flag indicating what action should be taken when VD
|
||||
@ -761,7 +761,7 @@ static void mtk_hsdma_free_active_desc(struct dma_chan *c)
|
||||
/*
|
||||
* Once issue_synchronize is being set, which means once the hardware
|
||||
* consumes all descriptors for the channel in the ring, the
|
||||
* synchronization must be be notified immediately it is completed.
|
||||
* synchronization must be notified immediately it is completed.
|
||||
*/
|
||||
spin_lock(&hvc->vc.lock);
|
||||
if (!list_empty(&hvc->desc_hw_processing)) {
|
||||
|
@ -313,7 +313,7 @@ mv_xor_v2_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
"%s sw_desc %p: async_tx %p\n",
|
||||
__func__, sw_desc, &sw_desc->async_tx);
|
||||
|
||||
/* assign coookie */
|
||||
/* assign cookie */
|
||||
spin_lock_bh(&xor_dev->lock);
|
||||
cookie = dma_cookie_assign(tx);
|
||||
|
||||
|
@ -193,7 +193,7 @@ struct owl_dma_pchan {
|
||||
|
||||
/**
|
||||
* struct owl_dma_pchan - Wrapper for DMA ENGINE channel
|
||||
* @vc: wrappped virtual channel
|
||||
* @vc: wrapped virtual channel
|
||||
* @pchan: the physical channel utilized by this channel
|
||||
* @txd: active transaction on this channel
|
||||
* @cfg: slave configuration for this channel
|
||||
|
@ -202,7 +202,7 @@ struct s3c24xx_dma_phy {
|
||||
* struct s3c24xx_dma_chan - this structure wraps a DMA ENGINE channel
|
||||
* @id: the id of the channel
|
||||
* @name: name of the channel
|
||||
* @vc: wrappped virtual channel
|
||||
* @vc: wrapped virtual channel
|
||||
* @phy: the physical channel utilized by this channel, if there is one
|
||||
* @runtime_addr: address for RX/TX according to the runtime config
|
||||
* @at: active transaction on this channel
|
||||
|
@ -52,16 +52,6 @@ static inline struct sf_pdma_desc *to_sf_pdma_desc(struct virt_dma_desc *vd)
|
||||
static struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan)
|
||||
{
|
||||
struct sf_pdma_desc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
if (chan->desc && !chan->desc->in_use) {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
return chan->desc;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
|
||||
if (!desc)
|
||||
@ -111,7 +101,6 @@ sf_pdma_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest, dma_addr_t src,
|
||||
desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, iflags);
|
||||
chan->desc = desc;
|
||||
sf_pdma_fill_desc(desc, dest, src, len);
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, iflags);
|
||||
|
||||
@ -170,11 +159,17 @@ static size_t sf_pdma_desc_residue(struct sf_pdma_chan *chan,
|
||||
unsigned long flags;
|
||||
u64 residue = 0;
|
||||
struct sf_pdma_desc *desc;
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct dma_async_tx_descriptor *tx = NULL;
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
|
||||
tx = &chan->desc->vdesc.tx;
|
||||
list_for_each_entry(vd, &chan->vchan.desc_submitted, node)
|
||||
if (vd->tx.cookie == cookie)
|
||||
tx = &vd->tx;
|
||||
|
||||
if (!tx)
|
||||
goto out;
|
||||
|
||||
if (cookie == tx->chan->completed_cookie)
|
||||
goto out;
|
||||
|
||||
@ -241,6 +236,19 @@ static void sf_pdma_enable_request(struct sf_pdma_chan *chan)
|
||||
writel(v, regs->ctrl);
|
||||
}
|
||||
|
||||
static struct sf_pdma_desc *sf_pdma_get_first_pending_desc(struct sf_pdma_chan *chan)
|
||||
{
|
||||
struct virt_dma_chan *vchan = &chan->vchan;
|
||||
struct virt_dma_desc *vdesc;
|
||||
|
||||
if (list_empty(&vchan->desc_issued))
|
||||
return NULL;
|
||||
|
||||
vdesc = list_first_entry(&vchan->desc_issued, struct virt_dma_desc, node);
|
||||
|
||||
return container_of(vdesc, struct sf_pdma_desc, vdesc);
|
||||
}
|
||||
|
||||
static void sf_pdma_xfer_desc(struct sf_pdma_chan *chan)
|
||||
{
|
||||
struct sf_pdma_desc *desc = chan->desc;
|
||||
@ -268,8 +276,11 @@ static void sf_pdma_issue_pending(struct dma_chan *dchan)
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
|
||||
if (vchan_issue_pending(&chan->vchan) && chan->desc)
|
||||
if (!chan->desc && vchan_issue_pending(&chan->vchan)) {
|
||||
/* vchan_issue_pending has made a check that desc in not NULL */
|
||||
chan->desc = sf_pdma_get_first_pending_desc(chan);
|
||||
sf_pdma_xfer_desc(chan);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
}
|
||||
@ -298,6 +309,11 @@ static void sf_pdma_donebh_tasklet(struct tasklet_struct *t)
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
list_del(&chan->desc->vdesc.node);
|
||||
vchan_cookie_complete(&chan->desc->vdesc);
|
||||
|
||||
chan->desc = sf_pdma_get_first_pending_desc(chan);
|
||||
if (chan->desc)
|
||||
sf_pdma_xfer_desc(chan);
|
||||
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -630,6 +631,21 @@ static void rz_dmac_virt_desc_free(struct virt_dma_desc *vd)
|
||||
*/
|
||||
}
|
||||
|
||||
static void rz_dmac_device_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
|
||||
struct rz_dmac *dmac = to_rz_dmac(chan->device);
|
||||
u32 chstat;
|
||||
int ret;
|
||||
|
||||
ret = read_poll_timeout(rz_dmac_ch_readl, chstat, !(chstat & CHSTAT_EN),
|
||||
100, 100000, false, channel, CHSTAT, 1);
|
||||
if (ret < 0)
|
||||
dev_warn(dmac->dev, "DMA Timeout");
|
||||
|
||||
rz_dmac_set_dmars_register(dmac, channel->index, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* IRQ handling
|
||||
@ -909,6 +925,7 @@ static int rz_dmac_probe(struct platform_device *pdev)
|
||||
engine->device_config = rz_dmac_config;
|
||||
engine->device_terminate_all = rz_dmac_terminate_all;
|
||||
engine->device_issue_pending = rz_dmac_issue_pending;
|
||||
engine->device_synchronize = rz_dmac_device_synchronize;
|
||||
|
||||
engine->copy_align = DMAENGINE_ALIGN_1_BYTE;
|
||||
dma_set_max_seg_size(engine->dev, U32_MAX);
|
||||
|
@ -1237,11 +1237,8 @@ static int sprd_dma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sprd_dma_dev *sdev = platform_get_drvdata(pdev);
|
||||
struct sprd_dma_chn *c, *cn;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
/* explicitly free the irq */
|
||||
if (sdev->irq > 0)
|
||||
|
@ -1970,7 +1970,7 @@ static int d40_config_memcpy(struct d40_chan *d40c)
|
||||
dma_has_cap(DMA_SLAVE, cap)) {
|
||||
d40c->dma_cfg = dma40_memcpy_conf_phy;
|
||||
|
||||
/* Generate interrrupt at end of transfer or relink. */
|
||||
/* Generate interrupt at end of transfer or relink. */
|
||||
d40c->dst_def_cfg |= BIT(D40_SREG_CFG_TIM_POS);
|
||||
|
||||
/* Generate interrupt on error. */
|
||||
|
@ -1328,12 +1328,7 @@ static irqreturn_t stm32_mdma_irq_handler(int irq, void *devid)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
id = __ffs(status);
|
||||
|
||||
chan = &dmadev->chan[id];
|
||||
if (!chan) {
|
||||
dev_warn(mdma2dev(dmadev), "MDMA channel not initialized\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Handle interrupt for the channel */
|
||||
spin_lock(&chan->vchan.lock);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -122,6 +123,15 @@
|
||||
SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) | \
|
||||
SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2))
|
||||
|
||||
/*
|
||||
* Normal DMA supports individual transfers (segments) up to 128k.
|
||||
* Dedicated DMA supports transfers up to 16M. We can only report
|
||||
* one size limit, so we have to use the smaller value.
|
||||
*/
|
||||
#define SUN4I_NDMA_MAX_SEG_SIZE SZ_128K
|
||||
#define SUN4I_DDMA_MAX_SEG_SIZE SZ_16M
|
||||
#define SUN4I_DMA_MAX_SEG_SIZE SUN4I_NDMA_MAX_SEG_SIZE
|
||||
|
||||
struct sun4i_dma_pchan {
|
||||
/* Register base of channel */
|
||||
void __iomem *base;
|
||||
@ -155,7 +165,8 @@ struct sun4i_dma_contract {
|
||||
struct virt_dma_desc vd;
|
||||
struct list_head demands;
|
||||
struct list_head completed_demands;
|
||||
int is_cyclic;
|
||||
bool is_cyclic : 1;
|
||||
bool use_half_int : 1;
|
||||
};
|
||||
|
||||
struct sun4i_dma_dev {
|
||||
@ -372,7 +383,7 @@ static int __execute_vchan_pending(struct sun4i_dma_dev *priv,
|
||||
if (promise) {
|
||||
vchan->contract = contract;
|
||||
vchan->pchan = pchan;
|
||||
set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1);
|
||||
set_pchan_interrupt(priv, pchan, contract->use_half_int, 1);
|
||||
configure_pchan(pchan, promise);
|
||||
}
|
||||
|
||||
@ -735,12 +746,21 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
|
||||
*
|
||||
* Which requires half the engine programming for the same
|
||||
* functionality.
|
||||
*
|
||||
* This only works if two periods fit in a single promise. That will
|
||||
* always be the case for dedicated DMA, where the hardware has a much
|
||||
* larger maximum transfer size than advertised to clients.
|
||||
*/
|
||||
nr_periods = DIV_ROUND_UP(len / period_len, 2);
|
||||
if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) {
|
||||
period_len *= 2;
|
||||
contract->use_half_int = 1;
|
||||
}
|
||||
|
||||
nr_periods = DIV_ROUND_UP(len, period_len);
|
||||
for (i = 0; i < nr_periods; i++) {
|
||||
/* Calculate the offset in the buffer and the length needed */
|
||||
offset = i * period_len * 2;
|
||||
plength = min((len - offset), (period_len * 2));
|
||||
offset = i * period_len;
|
||||
plength = min((len - offset), period_len);
|
||||
if (dir == DMA_MEM_TO_DEV)
|
||||
src = buf + offset;
|
||||
else
|
||||
@ -1149,6 +1169,8 @@ static int sun4i_dma_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, priv);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE);
|
||||
|
||||
dma_cap_zero(priv->slave.cap_mask);
|
||||
dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);
|
||||
dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask);
|
||||
|
@ -157,8 +157,8 @@
|
||||
* If any burst is in flight and DMA paused then this is the time to complete
|
||||
* on-flight burst and update DMA status register.
|
||||
*/
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETE_TIME 20
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT 100
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETE_TIME 10
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT 5000 /* 5 msec */
|
||||
|
||||
/* Channel base address offset from GPCDMA base address */
|
||||
#define TEGRA_GPCDMA_CHANNEL_BASE_ADD_OFFSET 0x20000
|
||||
@ -432,6 +432,17 @@ static int tegra_dma_device_resume(struct dma_chan *dc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tegra_dma_pause_noerr(struct tegra_dma_channel *tdc)
|
||||
{
|
||||
/* Return 0 irrespective of PAUSE status.
|
||||
* This is useful to recover channels that can exit out of flush
|
||||
* state when the channel is disabled.
|
||||
*/
|
||||
|
||||
tegra_dma_pause(tdc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_dma_disable(struct tegra_dma_channel *tdc)
|
||||
{
|
||||
u32 csr, status;
|
||||
@ -1292,6 +1303,14 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
|
||||
.terminate = tegra_dma_pause,
|
||||
};
|
||||
|
||||
static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
|
||||
.nr_channels = 31,
|
||||
.channel_reg_size = SZ_64K,
|
||||
.max_dma_count = SZ_1G,
|
||||
.hw_support_pause = true,
|
||||
.terminate = tegra_dma_pause_noerr,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_dma_of_match[] = {
|
||||
{
|
||||
.compatible = "nvidia,tegra186-gpcdma",
|
||||
@ -1299,6 +1318,9 @@ static const struct of_device_id tegra_dma_of_match[] = {
|
||||
}, {
|
||||
.compatible = "nvidia,tegra194-gpcdma",
|
||||
.data = &tegra194_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra234-gpcdma",
|
||||
.data = &tegra234_dma_chip_data,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
|
@ -112,6 +112,11 @@ static struct psil_ep j721s2_src_ep_map[] = {
|
||||
PSIL_PDMA_XY_PKT(0x4707),
|
||||
PSIL_PDMA_XY_PKT(0x4708),
|
||||
PSIL_PDMA_XY_PKT(0x4709),
|
||||
/* MAIN SA2UL */
|
||||
PSIL_SA2UL(0x4a40, 0),
|
||||
PSIL_SA2UL(0x4a41, 0),
|
||||
PSIL_SA2UL(0x4a42, 0),
|
||||
PSIL_SA2UL(0x4a43, 0),
|
||||
/* CPSW0 */
|
||||
PSIL_ETHERNET(0x7000),
|
||||
/* MCU_PDMA0 (MCU_PDMA_MISC_G0) - SPI0 */
|
||||
@ -144,6 +149,9 @@ static struct psil_ep j721s2_src_ep_map[] = {
|
||||
|
||||
/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */
|
||||
static struct psil_ep j721s2_dst_ep_map[] = {
|
||||
/* MAIN SA2UL */
|
||||
PSIL_SA2UL(0xca40, 1),
|
||||
PSIL_SA2UL(0xca41, 1),
|
||||
/* CPSW0 */
|
||||
PSIL_ETHERNET(0xf000),
|
||||
PSIL_ETHERNET(0xf001),
|
||||
|
@ -2127,126 +2127,6 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_cdma_prep_memcpy_sg - prepare descriptors for a memcpy_sg transaction
|
||||
* @dchan: DMA channel
|
||||
* @dst_sg: Destination scatter list
|
||||
* @dst_sg_len: Number of entries in destination scatter list
|
||||
* @src_sg: Source scatter list
|
||||
* @src_sg_len: Number of entries in source scatter list
|
||||
* @flags: transfer ack flags
|
||||
*
|
||||
* Return: Async transaction descriptor on success and NULL on failure
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *xilinx_cdma_prep_memcpy_sg(
|
||||
struct dma_chan *dchan, struct scatterlist *dst_sg,
|
||||
unsigned int dst_sg_len, struct scatterlist *src_sg,
|
||||
unsigned int src_sg_len, unsigned long flags)
|
||||
{
|
||||
struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
|
||||
struct xilinx_dma_tx_descriptor *desc;
|
||||
struct xilinx_cdma_tx_segment *segment, *prev = NULL;
|
||||
struct xilinx_cdma_desc_hw *hw;
|
||||
size_t len, dst_avail, src_avail;
|
||||
dma_addr_t dma_dst, dma_src;
|
||||
|
||||
if (unlikely(dst_sg_len == 0 || src_sg_len == 0))
|
||||
return NULL;
|
||||
|
||||
if (unlikely(!dst_sg || !src_sg))
|
||||
return NULL;
|
||||
|
||||
desc = xilinx_dma_alloc_tx_descriptor(chan);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
|
||||
desc->async_tx.tx_submit = xilinx_dma_tx_submit;
|
||||
|
||||
dst_avail = sg_dma_len(dst_sg);
|
||||
src_avail = sg_dma_len(src_sg);
|
||||
/*
|
||||
* loop until there is either no more source or no more destination
|
||||
* scatterlist entry
|
||||
*/
|
||||
while (true) {
|
||||
len = min_t(size_t, src_avail, dst_avail);
|
||||
len = min_t(size_t, len, chan->xdev->max_buffer_len);
|
||||
if (len == 0)
|
||||
goto fetch;
|
||||
|
||||
/* Allocate the link descriptor from DMA pool */
|
||||
segment = xilinx_cdma_alloc_tx_segment(chan);
|
||||
if (!segment)
|
||||
goto error;
|
||||
|
||||
dma_dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) -
|
||||
dst_avail;
|
||||
dma_src = sg_dma_address(src_sg) + sg_dma_len(src_sg) -
|
||||
src_avail;
|
||||
hw = &segment->hw;
|
||||
hw->control = len;
|
||||
hw->src_addr = dma_src;
|
||||
hw->dest_addr = dma_dst;
|
||||
if (chan->ext_addr) {
|
||||
hw->src_addr_msb = upper_32_bits(dma_src);
|
||||
hw->dest_addr_msb = upper_32_bits(dma_dst);
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->hw.next_desc = segment->phys;
|
||||
if (chan->ext_addr)
|
||||
prev->hw.next_desc_msb =
|
||||
upper_32_bits(segment->phys);
|
||||
}
|
||||
|
||||
prev = segment;
|
||||
dst_avail -= len;
|
||||
src_avail -= len;
|
||||
list_add_tail(&segment->node, &desc->segments);
|
||||
|
||||
fetch:
|
||||
/* Fetch the next dst scatterlist entry */
|
||||
if (dst_avail == 0) {
|
||||
if (dst_sg_len == 0)
|
||||
break;
|
||||
dst_sg = sg_next(dst_sg);
|
||||
if (dst_sg == NULL)
|
||||
break;
|
||||
dst_sg_len--;
|
||||
dst_avail = sg_dma_len(dst_sg);
|
||||
}
|
||||
/* Fetch the next src scatterlist entry */
|
||||
if (src_avail == 0) {
|
||||
if (src_sg_len == 0)
|
||||
break;
|
||||
src_sg = sg_next(src_sg);
|
||||
if (src_sg == NULL)
|
||||
break;
|
||||
src_sg_len--;
|
||||
src_avail = sg_dma_len(src_sg);
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(&desc->segments)) {
|
||||
dev_err(chan->xdev->dev,
|
||||
"%s: Zero-size SG transfer requested\n", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Link the last hardware descriptor with the first. */
|
||||
segment = list_first_entry(&desc->segments,
|
||||
struct xilinx_cdma_tx_segment, node);
|
||||
desc->async_tx.phys = segment->phys;
|
||||
prev->hw.next_desc = segment->phys;
|
||||
|
||||
return &desc->async_tx;
|
||||
|
||||
error:
|
||||
xilinx_dma_free_tx_descriptor(chan, desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
|
||||
* @dchan: DMA channel
|
||||
@ -3240,9 +3120,7 @@ static int xilinx_dma_probe(struct platform_device *pdev)
|
||||
DMA_RESIDUE_GRANULARITY_SEGMENT;
|
||||
} else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
|
||||
dma_cap_set(DMA_MEMCPY, xdev->common.cap_mask);
|
||||
dma_cap_set(DMA_MEMCPY_SG, xdev->common.cap_mask);
|
||||
xdev->common.device_prep_dma_memcpy = xilinx_cdma_prep_memcpy;
|
||||
xdev->common.device_prep_dma_memcpy_sg = xilinx_cdma_prep_memcpy_sg;
|
||||
/* Residue calculation is supported by only AXI DMA and CDMA */
|
||||
xdev->common.residue_granularity =
|
||||
DMA_RESIDUE_GRANULARITY_SEGMENT;
|
||||
|
@ -376,7 +376,7 @@ static ssize_t xilinx_dpdma_debugfs_read(struct file *f, char __user *buf,
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
} else {
|
||||
strlcpy(kern_buff, "No testcase executed",
|
||||
strscpy(kern_buff, "No testcase executed",
|
||||
XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE);
|
||||
}
|
||||
|
||||
@ -1652,10 +1652,8 @@ static int xilinx_dpdma_probe(struct platform_device *pdev)
|
||||
dpdma_hw_init(xdev);
|
||||
|
||||
xdev->irq = platform_get_irq(pdev, 0);
|
||||
if (xdev->irq < 0) {
|
||||
dev_err(xdev->dev, "failed to get platform irq\n");
|
||||
if (xdev->irq < 0)
|
||||
return xdev->irq;
|
||||
}
|
||||
|
||||
ret = request_irq(xdev->irq, xilinx_dpdma_irq_handler, IRQF_SHARED,
|
||||
dev_name(xdev->dev), xdev);
|
||||
|
@ -70,6 +70,16 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
|
||||
* struct sdma_peripheral_config - SDMA config for audio
|
||||
* @n_fifos_src: Number of FIFOs for recording
|
||||
* @n_fifos_dst: Number of FIFOs for playback
|
||||
* @stride_fifos_src: FIFO address stride for recording, 0 means all FIFOs are
|
||||
* continuous, 1 means 1 word stride between FIFOs. All stride
|
||||
* between FIFOs should be same.
|
||||
* @stride_fifos_dst: FIFO address stride for playback
|
||||
* @words_per_fifo: numbers of words per FIFO fetch/fill, 1 means
|
||||
* one channel per FIFO, 2 means 2 channels per FIFO..
|
||||
* If 'n_fifos_src = 4' and 'words_per_fifo = 2', it
|
||||
* means the first two words(channels) fetch from FIFO0
|
||||
* and then jump to FIFO1 for next two words, and so on
|
||||
* after the last FIFO3 fetched, roll back to FIFO0.
|
||||
* @sw_done: Use software done. Needed for PDM (micfil)
|
||||
*
|
||||
* Some i.MX Audio devices (SAI, micfil) have multiple successive FIFO
|
||||
@ -82,6 +92,9 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
|
||||
struct sdma_peripheral_config {
|
||||
int n_fifos_src;
|
||||
int n_fifos_dst;
|
||||
int stride_fifos_src;
|
||||
int stride_fifos_dst;
|
||||
int words_per_fifo;
|
||||
bool sw_done;
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ enum spi_transfer_cmd {
|
||||
* @clk_div: source clock divider
|
||||
* @clk_src: serial clock
|
||||
* @cmd: spi cmd
|
||||
* @fragmentation: keep CS assserted at end of sequence
|
||||
* @fragmentation: keep CS asserted at end of sequence
|
||||
* @cs: chip select toggle
|
||||
* @set_config: set peripheral config
|
||||
* @rx_len: receive length for buffer
|
||||
|
@ -50,7 +50,6 @@ enum dma_status {
|
||||
*/
|
||||
enum dma_transaction_type {
|
||||
DMA_MEMCPY,
|
||||
DMA_MEMCPY_SG,
|
||||
DMA_XOR,
|
||||
DMA_PQ,
|
||||
DMA_XOR_VAL,
|
||||
@ -887,11 +886,6 @@ struct dma_device {
|
||||
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
|
||||
struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
|
||||
size_t len, unsigned long flags);
|
||||
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy_sg)(
|
||||
struct dma_chan *chan,
|
||||
struct scatterlist *dst_sg, unsigned int dst_nents,
|
||||
struct scatterlist *src_sg, unsigned int src_nents,
|
||||
unsigned long flags);
|
||||
struct dma_async_tx_descriptor *(*device_prep_dma_xor)(
|
||||
struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src,
|
||||
unsigned int src_cnt, size_t len, unsigned long flags);
|
||||
@ -1060,20 +1054,6 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy(
|
||||
len, flags);
|
||||
}
|
||||
|
||||
static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy_sg(
|
||||
struct dma_chan *chan,
|
||||
struct scatterlist *dst_sg, unsigned int dst_nents,
|
||||
struct scatterlist *src_sg, unsigned int src_nents,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (!chan || !chan->device || !chan->device->device_prep_dma_memcpy_sg)
|
||||
return NULL;
|
||||
|
||||
return chan->device->device_prep_dma_memcpy_sg(chan, dst_sg, dst_nents,
|
||||
src_sg, src_nents,
|
||||
flags);
|
||||
}
|
||||
|
||||
static inline bool dmaengine_is_metadata_mode_supported(struct dma_chan *chan,
|
||||
enum dma_desc_metadata_mode mode)
|
||||
{
|
||||
|
@ -89,14 +89,14 @@ enum iax_opcode {
|
||||
IAX_OPCODE_CRC64,
|
||||
IAX_OPCODE_ZERO_DECOMP_32 = 0x48,
|
||||
IAX_OPCODE_ZERO_DECOMP_16,
|
||||
IAX_OPCODE_DECOMP_32 = 0x4c,
|
||||
IAX_OPCODE_DECOMP_16,
|
||||
IAX_OPCODE_ZERO_COMP_32 = 0x4c,
|
||||
IAX_OPCODE_ZERO_COMP_16,
|
||||
IAX_OPCODE_SCAN = 0x50,
|
||||
IAX_OPCODE_SET_MEMBER,
|
||||
IAX_OPCODE_EXTRACT,
|
||||
IAX_OPCODE_SELECT,
|
||||
IAX_OPCODE_RLE_BURST,
|
||||
IAX_OPCDE_FIND_UNIQUE,
|
||||
IAX_OPCODE_FIND_UNIQUE,
|
||||
IAX_OPCODE_EXPAND,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user