Merge tag 'dmaengine-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine

Pull dmaengine updates from Vinod Koul:
 "Core:
   - New devm_of_dma_controller_register() API

  New Support:
   - Support for RZ/G3L SoC
   - Loongson Multi-Channel DMA controller support
   - Conversion of Xilinx AXI DMA binding
   - DW AXI CV1800B DMA support
   - Switchtec DMA engine driver

  Updates:
   - AMD MDB Endpoint and non-LL mode support
   - DW edma virtual IRQ for interrupt-emulation, cyclic transfers support"

* tag 'dmaengine-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (65 commits)
  dmaengine: dw-edma: Add non-LL mode
  dmaengine: dw-edma: Add AMD MDB Endpoint Support
  dt-bindings: dmaengine: Fix spelling mistake "Looongson" -> "Looogson"
  dmaengine: loongson: Fix spelling mistake "Looongson" -> "Looogson"
  dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller
  dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller
  dmaengine: loongson: loongson2-apb: Simplify locking with guard() and scoped_guard()
  dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled()
  dmaengine: loongson: loongson2-apb: Convert to dmaenginem_async_device_register()
  dmaengine: loongson: New directory for Loongson DMA controllers drivers
  dt-bindings: dma: xlnx,axi-dma: Convert to DT schema
  dt-bindings: dma: rz-dmac: Add conditional schema for RZ/G3L
  dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks
  dmaengine: sh: rz-dmac: Add device_tx_status() callback
  dmaengine: sh: rz-dmac: Use rz_lmdesc_setup() to invalidate descriptors
  dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call
  dmaengine: sh: rz-dmac: Drop goto instruction and label
  dmaengine: sh: rz-dmac: Drop read of CHCTRL register
  dmaengine: sh: rz_dmac: add RZ/{T2H,N2H} support
  dt-bindings: dma: renesas,rz-dmac: document RZ/{T2H,N2H}
  ...
This commit is contained in:
Linus Torvalds
2026-04-17 10:29:01 -07:00
40 changed files with 3852 additions and 534 deletions
@@ -0,0 +1,81 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dma.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Loongson-2 Multi-Channel DMA controller
description:
The Loongson-2 Multi-Channel DMA controller is used for transferring data
between system memory and the peripherals on the APB bus.
maintainers:
- Binbin Zhou <zhoubinbin@loongson.cn>
allOf:
- $ref: dma-controller.yaml#
properties:
compatible:
enum:
- loongson,ls2k0300-dma
- loongson,ls2k3000-dma
reg:
maxItems: 1
interrupts:
description:
Should contain all of the per-channel DMA interrupts in ascending order
with respect to the DMA channel index.
minItems: 4
maxItems: 8
clocks:
maxItems: 1
'#dma-cells':
const: 2
description: |
DMA request from clients consists of 2 cells:
1. Channel index
2. Transfer request factor number, If no transfer factor, use 0.
The number is SoC-specific, and this should be specified with
relation to the device to use the DMA controller.
dma-channels:
enum: [4, 8]
required:
- compatible
- reg
- interrupts
- clocks
- '#dma-cells'
- dma-channels
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/loongson,ls2k-clk.h>
dma-controller@1612c000 {
compatible = "loongson,ls2k0300-dma";
reg = <0x1612c000 0xff>;
interrupt-parent = <&liointc0>;
interrupts = <23 IRQ_TYPE_LEVEL_HIGH>,
<24 IRQ_TYPE_LEVEL_HIGH>,
<25 IRQ_TYPE_LEVEL_HIGH>,
<26 IRQ_TYPE_LEVEL_HIGH>,
<27 IRQ_TYPE_LEVEL_HIGH>,
<28 IRQ_TYPE_LEVEL_HIGH>,
<29 IRQ_TYPE_LEVEL_HIGH>,
<30 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk LS2K0300_CLK_APB_GATE>;
#dma-cells = <2>;
dma-channels = <8>;
};
...
@@ -19,6 +19,7 @@ properties:
- renesas,r9a07g044-dmac # RZ/G2{L,LC}
- renesas,r9a07g054-dmac # RZ/V2L
- renesas,r9a08g045-dmac # RZ/G3S
- renesas,r9a08g046-dmac # RZ/G3L
- const: renesas,rz-dmac
- items:
@@ -29,6 +30,13 @@ properties:
- const: renesas,r9a09g057-dmac # RZ/V2H(P)
- const: renesas,r9a09g077-dmac # RZ/T2H
- items:
- enum:
- renesas,r9a09g087-dmac # RZ/N2H
- const: renesas,r9a09g077-dmac
reg:
items:
- description: Control and channel register block
@@ -36,27 +44,12 @@ properties:
minItems: 1
interrupts:
minItems: 16
maxItems: 17
interrupt-names:
items:
- const: error
- const: ch0
- const: ch1
- const: ch2
- const: ch3
- const: ch4
- const: ch5
- const: ch6
- const: ch7
- const: ch8
- const: ch9
- const: ch10
- const: ch11
- const: ch12
- const: ch13
- const: ch14
- const: ch15
minItems: 16
maxItems: 17
clocks:
items:
@@ -122,6 +115,35 @@ required:
allOf:
- $ref: dma-controller.yaml#
- if:
properties:
compatible:
contains:
enum:
- renesas,rz-dmac
- renesas,r9a09g057-dmac
then:
properties:
interrupt-names:
items:
- const: error
- const: ch0
- const: ch1
- const: ch2
- const: ch3
- const: ch4
- const: ch5
- const: ch6
- const: ch7
- const: ch8
- const: ch9
- const: ch10
- const: ch11
- const: ch12
- const: ch13
- const: ch14
- const: ch15
- if:
properties:
compatible:
@@ -131,6 +153,7 @@ allOf:
- renesas,r9a07g044-dmac
- renesas,r9a07g054-dmac
- renesas,r9a08g045-dmac
- renesas,r9a08g046-dmac
then:
properties:
reg:
@@ -189,6 +212,49 @@ allOf:
- renesas,icu
- resets
- if:
properties:
compatible:
contains:
const: renesas,r9a09g077-dmac
then:
properties:
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names: false
resets: false
reset-names: false
interrupts:
maxItems: 16
interrupt-names:
items:
- const: ch0
- const: ch1
- const: ch2
- const: ch3
- const: ch4
- const: ch5
- const: ch6
- const: ch7
- const: ch8
- const: ch9
- const: ch10
- const: ch11
- const: ch12
- const: ch13
- const: ch14
- const: ch15
required:
- clocks
- power-domains
- renesas,icu
additionalProperties: false
examples:
@@ -21,6 +21,7 @@ properties:
- enum:
- snps,axi-dma-1.01a
- intel,kmb-axi-dma
- sophgo,cv1800b-axi-dma
- starfive,jh7110-axi-dma
- starfive,jh8100-axi-dma
- items:
@@ -68,6 +69,8 @@ properties:
dma-noncoherent: true
dma-coherent: true
resets:
minItems: 1
maxItems: 2
@@ -1,111 +0,0 @@
Xilinx AXI VDMA engine, it does transfers between memory and video devices.
It can be configured to have one channel or two channels. If configured
as two channels, one is to transmit to the video device and another is
to receive from the video device.
Xilinx AXI DMA engine, it does transfers between memory and AXI4 stream
target devices. It can be configured to have one channel or two channels.
If configured as two channels, one is to transmit to the device and another
is to receive from the device.
Xilinx AXI CDMA engine, it does transfers between memory-mapped source
address and a memory-mapped destination address.
Xilinx AXI MCDMA engine, it does transfer between memory and AXI4 stream
target devices. It can be configured to have up to 16 independent transmit
and receive channels.
Required properties:
- compatible: Should be one of-
"xlnx,axi-vdma-1.00.a"
"xlnx,axi-dma-1.00.a"
"xlnx,axi-cdma-1.00.a"
"xlnx,axi-mcdma-1.00.a"
- #dma-cells: Should be <1>, see "dmas" property below
- reg: Should contain VDMA registers location and length.
- xlnx,addrwidth: Should be the vdma addressing size in bits(ex: 32 bits).
- dma-ranges: Should be as the following <dma_addr cpu_addr max_len>.
- dma-channel child node: Should have at least one channel and can have up to
two channels per device. This node specifies the properties of each
DMA channel (see child node properties below).
- clocks: Input clock specifier. Refer to common clock bindings.
- clock-names: List of input clocks
For VDMA:
Required elements: "s_axi_lite_aclk"
Optional elements: "m_axi_mm2s_aclk" "m_axi_s2mm_aclk",
"m_axis_mm2s_aclk", "s_axis_s2mm_aclk"
For CDMA:
Required elements: "s_axi_lite_aclk", "m_axi_aclk"
For AXIDMA and MCDMA:
Required elements: "s_axi_lite_aclk"
Optional elements: "m_axi_mm2s_aclk", "m_axi_s2mm_aclk",
"m_axi_sg_aclk"
Required properties for VDMA:
- xlnx,num-fstores: Should be the number of framebuffers as configured in h/w.
Optional properties for AXI DMA and MCDMA:
- xlnx,sg-length-width: Should be set to the width in bits of the length
register as configured in h/w. Takes values {8...26}. If the property
is missing or invalid then the default value 23 is used. This is the
maximum value that is supported by all IP versions.
Optional properties for AXI DMA:
- xlnx,axistream-connected: Tells whether DMA is connected to AXI stream IP.
- xlnx,irq-delay: Tells the interrupt delay timeout value. Valid range is from
0-255. Setting this value to zero disables the delay timer interrupt.
1 timeout interval = 125 * clock period of SG clock.
Optional properties for VDMA:
- xlnx,flush-fsync: Tells which channel to Flush on Frame sync.
It takes following values:
{1}, flush both channels
{2}, flush mm2s channel
{3}, flush s2mm channel
Required child node properties:
- compatible:
For VDMA: It should be either "xlnx,axi-vdma-mm2s-channel" or
"xlnx,axi-vdma-s2mm-channel".
For CDMA: It should be "xlnx,axi-cdma-channel".
For AXIDMA and MCDMA: It should be either "xlnx,axi-dma-mm2s-channel"
or "xlnx,axi-dma-s2mm-channel".
- interrupts: Should contain per channel VDMA interrupts.
- xlnx,datawidth: Should contain the stream data width, take values
{32,64...1024}.
Optional child node properties:
- xlnx,include-dre: Tells hardware is configured for Data
Realignment Engine.
Optional child node properties for VDMA:
- xlnx,genlock-mode: Tells Genlock synchronization is
enabled/disabled in hardware.
- xlnx,enable-vert-flip: Tells vertical flip is
enabled/disabled in hardware(S2MM path).
Optional child node properties for MCDMA:
- dma-channels: Number of dma channels in child node.
Example:
++++++++
axi_vdma_0: axivdma@40030000 {
compatible = "xlnx,axi-vdma-1.00.a";
#dma_cells = <1>;
reg = < 0x40030000 0x10000 >;
dma-ranges = <0x00000000 0x00000000 0x40000000>;
xlnx,num-fstores = <0x8>;
xlnx,flush-fsync = <0x1>;
xlnx,addrwidth = <0x20>;
clocks = <&clk 0>, <&clk 1>, <&clk 2>, <&clk 3>, <&clk 4>;
clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk",
"m_axis_mm2s_aclk", "s_axis_s2mm_aclk";
dma-channel@40030000 {
compatible = "xlnx,axi-vdma-mm2s-channel";
interrupts = < 0 54 4 >;
xlnx,datawidth = <0x40>;
} ;
dma-channel@40030030 {
compatible = "xlnx,axi-vdma-s2mm-channel";
interrupts = < 0 53 4 >;
xlnx,datawidth = <0x40>;
} ;
} ;
@@ -0,0 +1,299 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dma/xilinx/xlnx,axi-dma.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx AXI VDMA, DMA, CDMA and MCDMA IP
maintainers:
- Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
- Abin Joseph <abin.joseph@amd.com>
description: >
Xilinx AXI VDMA engine, it does transfers between memory and video devices.
It can be configured to have one channel or two channels. If configured
as two channels, one is to transmit to the video device and another is
to receive from the video device.
Xilinx AXI DMA engine, it does transfers between memory and AXI4 stream
target devices. It can be configured to have one channel or two channels.
If configured as two channels, one is to transmit to the device and another
is to receive from the device.
Xilinx AXI CDMA engine, it does transfers between memory-mapped source
address and a memory-mapped destination address.
Xilinx AXI MCDMA engine, it does transfer between memory and AXI4 stream
target devices. It can be configured to have up to 16 independent transmit
and receive channels.
properties:
compatible:
enum:
- xlnx,axi-cdma-1.00.a
- xlnx,axi-dma-1.00.a
- xlnx,axi-mcdma-1.00.a
- xlnx,axi-vdma-1.00.a
reg:
maxItems: 1
"#dma-cells":
const: 1
"#address-cells":
const: 1
"#size-cells":
const: 1
interrupts:
items:
- description: Interrupt for single channel (MM2S or S2MM)
- description: Interrupt for dual channel configuration
minItems: 1
description:
Interrupt lines for the DMA controller. Only used when
xlnx,axistream-connected is present (DMA connected to AXI Stream
IP). When child dma-channel nodes are present, interrupts are
specified in the child nodes instead.
clocks:
minItems: 1
maxItems: 5
clock-names:
minItems: 1
maxItems: 5
dma-ranges: true
xlnx,addrwidth:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [32, 64]
description: The DMA addressing size in bits.
xlnx,num-fstores:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 32
description: Should be the number of framebuffers as configured in h/w.
xlnx,flush-fsync:
type: boolean
description: Tells which channel to Flush on Frame sync.
xlnx,sg-length-width:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 8
maximum: 26
default: 23
description:
Width in bits of the length register as configured in hardware.
xlnx,irq-delay:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 255
description:
Tells the interrupt delay timeout value. Valid range is from 0-255.
Setting this value to zero disables the delay timer interrupt.
1 timeout interval = 125 * clock period of SG clock.
xlnx,axistream-connected:
type: boolean
description: Tells whether DMA is connected to AXI stream IP.
patternProperties:
"^dma-channel(-mm2s|-s2mm)?$":
type: object
description:
Should have at least one channel and can have up to two channels per
device. This node specifies the properties of each DMA channel.
properties:
compatible:
enum:
- xlnx,axi-vdma-mm2s-channel
- xlnx,axi-vdma-s2mm-channel
- xlnx,axi-cdma-channel
- xlnx,axi-dma-mm2s-channel
- xlnx,axi-dma-s2mm-channel
interrupts:
maxItems: 1
xlnx,datawidth:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [32, 64, 128, 256, 512, 1024]
description: Should contain the stream data width, take values {32,64...1024}.
xlnx,include-dre:
type: boolean
description: Tells hardware is configured for Data Realignment Engine.
xlnx,genlock-mode:
type: boolean
description: Tells Genlock synchronization is enabled/disabled in hardware.
xlnx,enable-vert-flip:
type: boolean
description:
Tells vertical flip is enabled/disabled in hardware(S2MM path).
dma-channels:
$ref: /schemas/types.yaml#/definitions/uint32
description: Number of dma channels in child node.
required:
- compatible
- interrupts
- xlnx,datawidth
additionalProperties: false
allOf:
- $ref: ../dma-controller.yaml#
- if:
properties:
compatible:
contains:
const: xlnx,axi-vdma-1.00.a
then:
properties:
clock-names:
items:
- const: s_axi_lite_aclk
- const: m_axi_mm2s_aclk
- const: m_axi_s2mm_aclk
- const: m_axis_mm2s_aclk
- const: s_axis_s2mm_aclk
minItems: 1
interrupts: false
patternProperties:
"^dma-channel(-mm2s|-s2mm)?$":
properties:
compatible:
enum:
- xlnx,axi-vdma-mm2s-channel
- xlnx,axi-vdma-s2mm-channel
required:
- xlnx,num-fstores
- if:
properties:
compatible:
contains:
const: xlnx,axi-cdma-1.00.a
then:
properties:
clock-names:
items:
- const: s_axi_lite_aclk
- const: m_axi_aclk
interrupts: false
patternProperties:
"^dma-channel(-mm2s|-s2mm)?$":
properties:
compatible:
enum:
- xlnx,axi-cdma-channel
- if:
properties:
compatible:
contains:
enum:
- xlnx,axi-dma-1.00.a
- xlnx,axi-mcdma-1.00.a
then:
properties:
clock-names:
items:
- const: s_axi_lite_aclk
- const: m_axi_mm2s_aclk
- const: m_axi_s2mm_aclk
- const: m_axi_sg_aclk
minItems: 1
patternProperties:
"^dma-channel(-mm2s|-s2mm)?(@[0-9a-f]+)?$":
properties:
compatible:
enum:
- xlnx,axi-dma-mm2s-channel
- xlnx,axi-dma-s2mm-channel
required:
- "#dma-cells"
- reg
- xlnx,addrwidth
- dma-ranges
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
dma-controller@40030000 {
compatible = "xlnx,axi-vdma-1.00.a";
reg = <0x40030000 0x10000>;
#dma-cells = <1>;
#address-cells = <1>;
#size-cells = <1>;
dma-ranges = <0x0 0x0 0x40000000>;
clocks = <&clk 0>, <&clk 1>, <&clk 2>, <&clk 3>, <&clk 4>;
clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk",
"m_axi_s2mm_aclk", "m_axis_mm2s_aclk",
"s_axis_s2mm_aclk";
xlnx,num-fstores = <8>;
xlnx,flush-fsync;
xlnx,addrwidth = <32>;
dma-channel-mm2s {
compatible = "xlnx,axi-vdma-mm2s-channel";
interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
xlnx,datawidth = <64>;
};
dma-channel-s2mm {
compatible = "xlnx,axi-vdma-s2mm-channel";
interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
xlnx,datawidth = <64>;
};
};
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
dma-controller@a4030000 {
compatible = "xlnx,axi-dma-1.00.a";
reg = <0xa4030000 0x10000>;
#dma-cells = <1>;
#address-cells = <1>;
#size-cells = <1>;
dma-ranges = <0x0 0x0 0x40000000>;
clocks = <&clk 0>, <&clk 1>, <&clk 2>, <&clk 3>;
clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk",
"m_axi_s2mm_aclk", "m_axi_sg_aclk";
xlnx,addrwidth = <32>;
xlnx,sg-length-width = <14>;
dma-channel-mm2s {
compatible = "xlnx,axi-dma-mm2s-channel";
interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
xlnx,datawidth = <64>;
xlnx,include-dre;
};
dma-channel-s2mm {
compatible = "xlnx,axi-dma-s2mm-channel";
interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
xlnx,datawidth = <64>;
xlnx,include-dre;
};
};
+12 -2
View File
@@ -15039,12 +15039,14 @@ S: Maintained
F: Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml
F: drivers/gpio/gpio-loongson-64bit.c
LOONGSON-2 APB DMA DRIVER
LOONGSON-2 DMA DRIVER
M: Binbin Zhou <zhoubinbin@loongson.cn>
L: dmaengine@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
F: Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml
F: drivers/dma/loongson2-apb-dma.c
F: drivers/dma/loongson/loongson2-apb-cmc-dma.c
F: drivers/dma/loongson/loongson2-apb-dma.c
LOONGSON LS2X I2C DRIVER
M: Binbin Zhou <zhoubinbin@loongson.cn>
@@ -17832,6 +17834,7 @@ F: arch/mips/boot/dts/loongson/loongson1*
F: arch/mips/configs/loongson1_defconfig
F: arch/mips/loongson32/
F: drivers/*/*loongson1*
F: drivers/dma/loongson/loongson1-apb-dma.c
F: drivers/mtd/nand/raw/loongson-nand-controller.c
F: drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
F: sound/soc/loongson/loongson1_ac97.c
@@ -25724,6 +25727,13 @@ S: Supported
F: include/net/switchdev.h
F: net/switchdev/
SWITCHTEC DMA DRIVER
M: Kelvin Cao <kelvin.cao@microchip.com>
M: Logan Gunthorpe <logang@deltatee.com>
L: dmaengine@vger.kernel.org
S: Maintained
F: drivers/dma/switchtec_dma.c
SY8106A REGULATOR DRIVER
M: Icenowy Zheng <icenowy@aosc.io>
S: Maintained
+12 -24
View File
@@ -376,29 +376,6 @@ config K3_DMA
Support the DMA engine for Hisilicon K3 platform
devices.
config LOONGSON1_APB_DMA
tristate "Loongson1 APB DMA support"
depends on MACH_LOONGSON32 || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
This selects support for the APB DMA controller in Loongson1 SoCs,
which is required by Loongson1 NAND and audio support.
config LOONGSON2_APB_DMA
tristate "Loongson2 APB DMA support"
depends on LOONGARCH || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Support for the Loongson2 APB DMA controller driver. The
DMA controller is having single DMA channel which can be
configured for different peripherals like audio, nand, sdio
etc which is in APB bus.
This DMA controller transfers data from memory to peripheral fifo.
It does not support memory to memory data transfer.
config LPC18XX_DMAMUX
bool "NXP LPC18xx/43xx DMA MUX for PL080"
depends on ARCH_LPC18XX || COMPILE_TEST
@@ -505,7 +482,7 @@ config MV_XOR_V2
platforms.
config MXS_DMA
bool "MXS DMA support"
tristate "MXS DMA support"
depends on ARCH_MXS || ARCH_MXC || COMPILE_TEST
select STMP_DEVICE
select DMA_ENGINE
@@ -610,6 +587,15 @@ config SPRD_DMA
help
Enable support for the on-chip DMA controller on Spreadtrum platform.
config SWITCHTEC_DMA
tristate "Switchtec PSX/PFX Switch DMA Engine Support"
depends on PCI
select DMA_ENGINE
help
Some Switchtec PSX/PFX PCIe Switches support additional DMA engines.
These are exposed via an extra function on the switch's upstream
port.
config TXX9_DMAC
tristate "Toshiba TXx9 SoC DMA support"
depends on MACH_TX49XX
@@ -774,6 +760,8 @@ source "drivers/dma/fsl-dpaa2-qdma/Kconfig"
source "drivers/dma/lgm/Kconfig"
source "drivers/dma/loongson/Kconfig"
source "drivers/dma/stm32/Kconfig"
# clients
+2 -2
View File
@@ -49,8 +49,6 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
obj-$(CONFIG_INTEL_IOATDMA) += ioat/
obj-y += idxd/
obj-$(CONFIG_K3_DMA) += k3dma.o
obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o
obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o
obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
@@ -74,6 +72,7 @@ obj-$(CONFIG_SF_PDMA) += sf-pdma/
obj-$(CONFIG_SOPHGO_CV1800B_DMAMUX) += cv1800b-dmamux.o
obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
obj-$(CONFIG_SPRD_DMA) += sprd-dma.o
obj-$(CONFIG_SWITCHTEC_DMA) += switchtec_dma.o
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
obj-$(CONFIG_TEGRA186_GPC_DMA) += tegra186-gpc-dma.o
obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o
@@ -87,6 +86,7 @@ obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/
obj-$(CONFIG_INTEL_LDMA) += lgm/
obj-y += amd/
obj-y += loongson/
obj-y += mediatek/
obj-y += qcom/
obj-y += stm32/
+149 -21
View File
@@ -134,6 +134,7 @@ struct axi_dmac_desc {
struct axi_dmac_chan *chan;
bool cyclic;
bool cyclic_eot;
bool have_partial_xfer;
unsigned int num_submitted;
@@ -162,6 +163,7 @@ struct axi_dmac_chan {
bool hw_cyclic;
bool hw_2d;
bool hw_sg;
bool hw_cyclic_hotfix;
};
struct axi_dmac {
@@ -227,29 +229,94 @@ static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr)
return true;
}
static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan)
{
return list_first_entry_or_null(&chan->active_descs,
struct axi_dmac_desc, vdesc.node);
}
static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac,
struct axi_dmac_chan *chan)
{
struct axi_dmac_desc *active = axi_dmac_active_desc(chan);
struct virt_dma_desc *vdesc;
struct axi_dmac_desc *desc;
unsigned int val;
/*
* Just play safe and ignore any SOF if we have an active cyclic transfer
* flagged to end. We'll start it as soon as the current cyclic one ends.
*/
if (active && active->cyclic_eot)
return NULL;
/*
* It means a SW cyclic transfer is in place so we should just return
* the same descriptor. SW cyclic transfer termination is handled
* in axi_dmac_transfer_done().
*/
if (chan->next_desc)
return chan->next_desc;
vdesc = vchan_next_desc(&chan->vchan);
if (!vdesc)
return NULL;
if (active && active->cyclic && !(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) {
struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev;
dev_warn(dev, "Discarding non EOT transfer after cyclic\n");
list_del(&vdesc->node);
return NULL;
}
list_move_tail(&vdesc->node, &chan->active_descs);
desc = to_axi_dmac_desc(vdesc);
chan->next_desc = desc;
if (!active || !active->cyclic)
return desc;
active->cyclic_eot = true;
if (chan->hw_sg) {
unsigned long flags = AXI_DMAC_HW_FLAG_IRQ | AXI_DMAC_HW_FLAG_LAST;
/*
* Let's then stop the current cyclic transfer by making sure we
* get an EOT interrupt and to open the cyclic loop by marking
* the last segment.
*/
active->sg[active->num_sgs - 1].hw->flags = flags;
return NULL;
}
/*
* Clear the cyclic bit if there's no Scatter-Gather HW so that we get
* at the end of the transfer.
*/
val = axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS);
val &= ~AXI_DMAC_FLAG_CYCLIC;
axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, val);
return NULL;
}
static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
{
struct axi_dmac *dmac = chan_to_axi_dmac(chan);
struct virt_dma_desc *vdesc;
struct axi_dmac_desc *desc;
struct axi_dmac_sg *sg;
unsigned int flags = 0;
unsigned int val;
desc = axi_dmac_get_next_desc(dmac, chan);
if (!desc)
return;
val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER);
if (val) /* Queue is full, wait for the next SOT IRQ */
return;
desc = chan->next_desc;
if (!desc) {
vdesc = vchan_next_desc(&chan->vchan);
if (!vdesc)
return;
list_move_tail(&vdesc->node, &chan->active_descs);
desc = to_axi_dmac_desc(vdesc);
chan->next_desc = desc;
}
sg = &desc->sg[desc->num_submitted];
/* Already queued in cyclic mode. Wait for it to finish */
@@ -291,10 +358,12 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
* call, enable hw cyclic mode to avoid unnecessary interrupts.
*/
if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) {
if (chan->hw_sg)
if (chan->hw_sg) {
desc->sg[desc->num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_IRQ;
else if (desc->num_sgs == 1)
} else if (desc->num_sgs == 1) {
chan->next_desc = NULL;
flags |= AXI_DMAC_FLAG_CYCLIC;
}
}
if (chan->hw_partial_xfer)
@@ -312,12 +381,6 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
axi_dmac_write(dmac, AXI_DMAC_REG_START_TRANSFER, 1);
}
static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan)
{
return list_first_entry_or_null(&chan->active_descs,
struct axi_dmac_desc, vdesc.node);
}
static inline unsigned int axi_dmac_total_sg_bytes(struct axi_dmac_chan *chan,
struct axi_dmac_sg *sg)
{
@@ -398,6 +461,61 @@ static void axi_dmac_compute_residue(struct axi_dmac_chan *chan,
}
}
static bool axi_dmac_handle_cyclic_eot(struct axi_dmac_chan *chan,
struct axi_dmac_desc *active)
{
struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev;
struct virt_dma_desc *vdesc;
/* wrap around */
active->num_completed = 0;
if (active->cyclic_eot) {
/*
* It means an HW cyclic transfer was marked to stop. And we
* know we have something to schedule, so start the next
* transfer now the cyclic one is done.
*/
list_del(&active->vdesc.node);
vchan_cookie_complete(&active->vdesc);
if (chan->hw_cyclic_hotfix) {
struct axi_dmac *dmac = chan_to_axi_dmac(chan);
/*
* In older IP cores, ending a cyclic transfer by clearing
* the CYCLIC flag does not guarantee a graceful end.
* It can happen that some data (of the next frame) is
* already prefetched and will be wrongly visible in the
* next transfer. To workaround this, we need to reenable
* the core so everything is flushed. Newer cores handles
* this correctly and do not require this "hotfix". The
* SG IP also does not require this.
*/
dev_dbg(dev, "HW cyclic hotfix\n");
axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE);
}
return true;
}
vdesc = vchan_next_desc(&chan->vchan);
if (!vdesc)
return false;
if (!(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) {
dev_warn(dev, "Discarding non EOT transfer after cyclic\n");
list_del(&vdesc->node);
return false;
}
/* then let's end the cyclic transfer */
chan->next_desc = NULL;
list_del(&active->vdesc.node);
vchan_cookie_complete(&active->vdesc);
return true;
}
static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
unsigned int completed_transfers)
{
@@ -416,6 +534,7 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
if (chan->hw_sg) {
if (active->cyclic) {
vchan_cyclic_callback(&active->vdesc);
start_next = axi_dmac_handle_cyclic_eot(chan, active);
} else {
list_del(&active->vdesc.node);
vchan_cookie_complete(&active->vdesc);
@@ -445,7 +564,8 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
if (active->num_completed == active->num_sgs ||
sg->partial_len) {
if (active->cyclic) {
active->num_completed = 0; /* wrap around */
/* keep start_next as is, if already true... */
start_next |= axi_dmac_handle_cyclic_eot(chan, active);
} else {
list_del(&active->vdesc.node);
vchan_cookie_complete(&active->vdesc);
@@ -657,7 +777,12 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs,
vecs[i].len, dsg);
}
desc->cyclic = false;
desc->cyclic = flags & DMA_PREP_REPEAT;
if (desc->cyclic) {
/* Chain the last descriptor to the first, and remove its "last" flag */
desc->sg[num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_LAST;
desc->sg[num_sgs - 1].hw->next_sg_addr = desc->sg[0].hw_phys;
}
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
}
@@ -1053,6 +1178,9 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
chan->length_align_mask = chan->address_align_mask;
}
if (version < ADI_AXI_PCORE_VER(4, 6, 0) && !chan->hw_sg)
chan->hw_cyclic_hotfix = true;
return 0;
}
+26 -24
View File
@@ -31,29 +31,29 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/hardirq.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/percpu.h>
#include <linux/rcupdate.h>
#include <linux/mutex.h>
#include <linux/jiffies.h>
#include <linux/rculist.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/acpi_dma.h>
#include <linux/of_dma.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/hardirq.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/mempool.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/numa.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/percpu.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "dmaengine.h"
@@ -765,7 +765,7 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
mutex_lock(&dma_list_mutex);
list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
/* Finds a DMA controller with matching device node */
if (np && device->dev->of_node && np != device->dev->of_node)
if (np && !device_match_of_node(device->dev, np))
continue;
chan = find_candidate(device, mask, fn, fn_param);
@@ -943,12 +943,14 @@ static void dmaenginem_release_channel(void *chan)
struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name)
{
struct dma_chan *chan = dma_request_chan(dev, name);
int ret = 0;
struct dma_chan *chan;
int ret;
if (!IS_ERR(chan))
ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan);
chan = dma_request_chan(dev, name);
if (IS_ERR(chan))
return chan;
ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan);
if (ret)
return ERR_PTR(ret);
+29 -13
View File
@@ -50,6 +50,7 @@
#define AXI_DMA_FLAG_HAS_APB_REGS BIT(0)
#define AXI_DMA_FLAG_HAS_RESETS BIT(1)
#define AXI_DMA_FLAG_USE_CFG2 BIT(2)
#define AXI_DMA_FLAG_ARG0_AS_CHAN BIT(3)
static inline void
axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val)
@@ -342,8 +343,8 @@ static void axi_desc_put(struct axi_dma_desc *desc)
kfree(desc);
atomic_sub(descs_put, &chan->descs_allocated);
dev_vdbg(chan2dev(chan), "%s: %d descs put, %d still allocated\n",
axi_chan_name(chan), descs_put,
atomic_read(&chan->descs_allocated));
axi_chan_name(chan), descs_put,
atomic_read(&chan->descs_allocated));
}
static void vchan_desc_put(struct virt_dma_desc *vdesc)
@@ -353,7 +354,7 @@ static void vchan_desc_put(struct virt_dma_desc *vdesc)
static enum dma_status
dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
struct dma_tx_state *txstate)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
struct virt_dma_desc *vdesc;
@@ -419,6 +420,7 @@ static void dw_axi_dma_set_byte_halfword(struct axi_dma_chan *chan, bool set)
iowrite32(val, chan->chip->apb_regs + offset);
}
/* Called in chan locked context */
static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
struct axi_dma_desc *first)
@@ -491,7 +493,7 @@ static void axi_chan_start_first_queued(struct axi_dma_chan *chan)
desc = vd_to_axi_desc(vd);
dev_vdbg(chan2dev(chan), "%s: started %u\n", axi_chan_name(chan),
vd->tx.cookie);
vd->tx.cookie);
axi_chan_block_xfer_start(chan, desc);
}
@@ -592,8 +594,6 @@ static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set)
(chan->id * DMA_APB_HS_SEL_BIT_SIZE));
reg_value |= (val << (chan->id * DMA_APB_HS_SEL_BIT_SIZE));
lo_hi_writeq(reg_value, chip->apb_regs + DMAC_APB_HW_HS_SEL_0);
return;
}
/*
@@ -1162,7 +1162,7 @@ static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id)
axi_chan_irq_clear(chan, status);
dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n",
axi_chan_name(chan), i, status);
axi_chan_name(chan), i, status);
if (status & DWAXIDMAC_IRQ_ALL_ERR)
axi_chan_handle_err(chan, status);
@@ -1358,16 +1358,27 @@ static int __maybe_unused axi_dma_runtime_resume(struct device *dev)
static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
{
unsigned int handshake = dma_spec->args[0];
struct dw_axi_dma *dw = ofdma->of_dma_data;
struct axi_dma_chan *chan;
struct axi_dma_chan *chan = NULL;
struct dma_chan *dchan;
dchan = dma_get_any_slave_channel(&dw->dma);
if (dw->hdata->use_handshake_as_channel_number) {
if (handshake >= dw->hdata->nr_channels)
return NULL;
chan = &dw->chan[handshake];
dchan = dma_get_slave_channel(&chan->vc.chan);
} else {
dchan = dma_get_any_slave_channel(&dw->dma);
}
if (!dchan)
return NULL;
chan = dchan_to_axi_dma_chan(dchan);
chan->hw_handshake_num = dma_spec->args[0];
if (!chan)
chan = dchan_to_axi_dma_chan(dchan);
chan->hw_handshake_num = handshake;
return dchan;
}
@@ -1451,7 +1462,7 @@ static int axi_req_irqs(struct platform_device *pdev, struct axi_dma_chip *chip)
if (chip->irq[i] < 0)
return chip->irq[i];
ret = devm_request_irq(chip->dev, chip->irq[i], dw_axi_dma_interrupt,
IRQF_SHARED, KBUILD_MODNAME, chip);
IRQF_SHARED, KBUILD_MODNAME, chip);
if (ret < 0)
return ret;
}
@@ -1506,6 +1517,8 @@ static int dw_probe(struct platform_device *pdev)
return ret;
}
chip->dw->hdata->use_handshake_as_channel_number = !!(flags & AXI_DMA_FLAG_ARG0_AS_CHAN);
chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2);
chip->core_clk = devm_clk_get(chip->dev, "core-clk");
@@ -1645,7 +1658,7 @@ static void dw_remove(struct platform_device *pdev)
of_dma_controller_free(chip->dev->of_node);
list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
vc.chan.device_node) {
vc.chan.device_node) {
list_del(&chan->vc.chan.device_node);
tasklet_kill(&chan->vc.task);
}
@@ -1661,6 +1674,9 @@ static const struct of_device_id dw_dma_of_id_table[] = {
}, {
.compatible = "intel,kmb-axi-dma",
.data = (void *)AXI_DMA_FLAG_HAS_APB_REGS,
}, {
.compatible = "sophgo,cv1800b-axi-dma",
.data = (void *)AXI_DMA_FLAG_ARG0_AS_CHAN,
}, {
.compatible = "starfive,jh7110-axi-dma",
.data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
+1
View File
@@ -34,6 +34,7 @@ struct dw_axi_dma_hcfg {
bool reg_map_8_channels;
bool restrict_axi_burst_len;
bool use_cfg2;
bool use_handshake_as_channel_number;
};
struct axi_dma_chan {
+168 -6
View File
@@ -223,6 +223,43 @@ static int dw_edma_device_config(struct dma_chan *dchan,
struct dma_slave_config *config)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
bool cfg_non_ll;
int non_ll = 0;
chan->non_ll = false;
if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE) {
if (config->peripheral_config &&
config->peripheral_size != sizeof(int)) {
dev_err(dchan->device->dev,
"config param peripheral size mismatch\n");
return -EINVAL;
}
/*
* When there is no valid LLP base address available then the
* default DMA ops will use the non-LL mode.
*
* Cases where LL mode is enabled and client wants to use the
* non-LL mode then also client can do so via providing the
* peripheral_config param.
*/
cfg_non_ll = chan->dw->chip->cfg_non_ll;
if (config->peripheral_config) {
non_ll = *(int *)config->peripheral_config;
if (cfg_non_ll && !non_ll) {
dev_err(dchan->device->dev, "invalid configuration\n");
return -EINVAL;
}
}
if (cfg_non_ll || non_ll)
chan->non_ll = true;
} else if (config->peripheral_config) {
dev_err(dchan->device->dev,
"peripheral config param applicable only for HDMA\n");
return -EINVAL;
}
memcpy(&chan->config, config, sizeof(*config));
chan->configured = true;
@@ -358,6 +395,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
struct dw_edma_desc *desc;
u64 src_addr, dst_addr;
size_t fsz = 0;
u32 bursts_max;
u32 cnt = 0;
int i;
@@ -415,6 +453,13 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
return NULL;
}
/*
* For non-LL mode, only a single burst can be handled
* in a single chunk unlike LL mode where multiple bursts
* can be configured in a single chunk.
*/
bursts_max = chan->non_ll ? 1 : chan->ll_max;
desc = dw_edma_alloc_desc(chan);
if (unlikely(!desc))
goto err_alloc;
@@ -450,7 +495,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
break;
if (chunk->bursts_alloc == chan->ll_max) {
if (chunk->bursts_alloc == bursts_max) {
chunk = dw_edma_alloc_chunk(desc);
if (unlikely(!chunk))
goto err_alloc;
@@ -663,7 +708,96 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
chan->status = EDMA_ST_IDLE;
}
static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
static void dw_edma_emul_irq_ack(struct irq_data *d)
{
struct dw_edma *dw = irq_data_get_irq_chip_data(d);
dw_edma_core_ack_emulated_irq(dw);
}
/*
* irq_chip implementation for interrupt-emulation doorbells.
*
* The emulated source has no mask/unmask mechanism. With handle_level_irq(),
* the flow is therefore:
* 1) .irq_ack() deasserts the source
* 2) registered handlers (if any) are dispatched
* Since deassertion is already done in .irq_ack(), handlers do not need to take
* care of it, hence IRQCHIP_ONESHOT_SAFE.
*/
static struct irq_chip dw_edma_emul_irqchip = {
.name = "dw-edma-emul",
.irq_ack = dw_edma_emul_irq_ack,
.flags = IRQCHIP_ONESHOT_SAFE | IRQCHIP_SKIP_SET_WAKE,
};
static int dw_edma_emul_irq_alloc(struct dw_edma *dw)
{
struct dw_edma_chip *chip = dw->chip;
int virq;
chip->db_irq = 0;
chip->db_offset = ~0;
/*
* Only meaningful when the core provides the deassert sequence
* for interrupt emulation.
*/
if (!dw->core->ack_emulated_irq)
return 0;
/*
* Allocate a single, requestable Linux virtual IRQ number.
* Use >= 1 so that 0 can remain a "not available" sentinel.
*/
virq = irq_alloc_desc(NUMA_NO_NODE);
if (virq < 0)
return virq;
irq_set_chip_and_handler(virq, &dw_edma_emul_irqchip, handle_level_irq);
irq_set_chip_data(virq, dw);
irq_set_noprobe(virq);
chip->db_irq = virq;
chip->db_offset = dw_edma_core_db_offset(dw);
return 0;
}
static void dw_edma_emul_irq_free(struct dw_edma *dw)
{
struct dw_edma_chip *chip = dw->chip;
if (!chip)
return;
if (chip->db_irq <= 0)
return;
irq_free_descs(chip->db_irq, 1);
chip->db_irq = 0;
chip->db_offset = ~0;
}
static inline irqreturn_t dw_edma_interrupt_emulated(void *data)
{
struct dw_edma_irq *dw_irq = data;
struct dw_edma *dw = dw_irq->dw;
int db_irq = dw->chip->db_irq;
if (db_irq > 0) {
/*
* Interrupt emulation may assert the IRQ line without updating the
* normal DONE/ABORT status bits. With a shared IRQ handler we
* cannot reliably detect such events by status registers alone, so
* always perform the core-specific deassert sequence.
*/
generic_handle_irq(db_irq);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data)
{
struct dw_edma_irq *dw_irq = data;
@@ -672,7 +806,7 @@ static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
dw_edma_abort_interrupt);
}
static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data)
{
struct dw_edma_irq *dw_irq = data;
@@ -681,12 +815,33 @@ static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
dw_edma_abort_interrupt);
}
static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
ret |= dw_edma_interrupt_write(irq, data);
ret |= dw_edma_interrupt_read(irq, data);
ret |= dw_edma_interrupt_write_inner(irq, data);
ret |= dw_edma_interrupt_emulated(data);
return ret;
}
static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
ret |= dw_edma_interrupt_read_inner(irq, data);
ret |= dw_edma_interrupt_emulated(data);
return ret;
}
static inline irqreturn_t dw_edma_interrupt_common(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
ret |= dw_edma_interrupt_write_inner(irq, data);
ret |= dw_edma_interrupt_read_inner(irq, data);
ret |= dw_edma_interrupt_emulated(data);
return ret;
}
@@ -977,6 +1132,11 @@ int dw_edma_probe(struct dw_edma_chip *chip)
if (err)
return err;
/* Allocate a dedicated virtual IRQ for interrupt-emulation doorbells */
err = dw_edma_emul_irq_alloc(dw);
if (err)
dev_warn(dev, "Failed to allocate emulation IRQ: %d\n", err);
/* Setup write/read channels */
err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc);
if (err)
@@ -992,6 +1152,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
err_irq_free:
for (i = (dw->nr_irqs - 1); i >= 0; i--)
free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
dw_edma_emul_irq_free(dw);
return err;
}
@@ -1014,6 +1175,7 @@ int dw_edma_remove(struct dw_edma_chip *chip)
/* Free irqs */
for (i = (dw->nr_irqs - 1); i >= 0; i--)
free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
dw_edma_emul_irq_free(dw);
/* Deregister eDMA device */
dma_async_device_unregister(&dw->dma);
+18
View File
@@ -86,6 +86,7 @@ struct dw_edma_chan {
u8 configured;
struct dma_slave_config config;
bool non_ll;
};
struct dw_edma_irq {
@@ -126,6 +127,8 @@ struct dw_edma_core_ops {
void (*start)(struct dw_edma_chunk *chunk, bool first);
void (*ch_config)(struct dw_edma_chan *chan);
void (*debugfs_on)(struct dw_edma *dw);
void (*ack_emulated_irq)(struct dw_edma *dw);
resource_size_t (*db_offset)(struct dw_edma *dw);
};
struct dw_edma_sg {
@@ -206,4 +209,19 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw)
dw->core->debugfs_on(dw);
}
static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw)
{
if (!dw->core->ack_emulated_irq)
return -EOPNOTSUPP;
dw->core->ack_emulated_irq(dw);
return 0;
}
static inline resource_size_t
dw_edma_core_db_offset(struct dw_edma *dw)
{
return dw->core->db_offset(dw);
}
#endif /* _DW_EDMA_CORE_H */
+200 -20
View File
@@ -14,14 +14,35 @@
#include <linux/pci-epf.h>
#include <linux/msi.h>
#include <linux/bitfield.h>
#include <linux/sizes.h>
#include "dw-edma-core.h"
#define DW_PCIE_VSEC_DMA_ID 0x6
#define DW_PCIE_VSEC_DMA_BAR GENMASK(10, 8)
#define DW_PCIE_VSEC_DMA_MAP GENMASK(2, 0)
#define DW_PCIE_VSEC_DMA_WR_CH GENMASK(9, 0)
#define DW_PCIE_VSEC_DMA_RD_CH GENMASK(25, 16)
/* Synopsys */
#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID 0x6
#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR GENMASK(10, 8)
#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP GENMASK(2, 0)
#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH GENMASK(9, 0)
#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH GENMASK(25, 16)
/* AMD MDB (Xilinx) specific defines */
#define PCI_DEVICE_ID_XILINX_B054 0xb054
#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID 0x6
#define DW_PCIE_XILINX_MDB_VSEC_ID 0x20
#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR GENMASK(10, 8)
#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP GENMASK(2, 0)
#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH GENMASK(9, 0)
#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH GENMASK(25, 16)
#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH 0xc
#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW 0x8
#define DW_PCIE_XILINX_MDB_INVALID_ADDR (~0ULL)
#define DW_PCIE_XILINX_MDB_LL_OFF_GAP 0x200000
#define DW_PCIE_XILINX_MDB_LL_SIZE 0x800
#define DW_PCIE_XILINX_MDB_DT_OFF_GAP 0x100000
#define DW_PCIE_XILINX_MDB_DT_SIZE 0x800
#define DW_BLOCK(a, b, c) \
{ \
@@ -50,6 +71,7 @@ struct dw_edma_pcie_data {
u8 irqs;
u16 wr_ch_cnt;
u16 rd_ch_cnt;
u64 devmem_phys_off;
};
static const struct dw_edma_pcie_data snps_edda_data = {
@@ -90,6 +112,64 @@ static const struct dw_edma_pcie_data snps_edda_data = {
.rd_ch_cnt = 2,
};
static const struct dw_edma_pcie_data xilinx_mdb_data = {
/* MDB registers location */
.rg.bar = BAR_0,
.rg.off = SZ_4K, /* 4 Kbytes */
.rg.sz = SZ_8K, /* 8 Kbytes */
/* Other */
.mf = EDMA_MF_HDMA_NATIVE,
.irqs = 1,
.wr_ch_cnt = 8,
.rd_ch_cnt = 8,
};
static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data *pdata,
enum pci_barno bar, off_t start_off,
off_t ll_off_gap, size_t ll_size,
off_t dt_off_gap, size_t dt_size)
{
u16 wr_ch = pdata->wr_ch_cnt;
u16 rd_ch = pdata->rd_ch_cnt;
off_t off;
u16 i;
off = start_off;
/* Write channel LL region */
for (i = 0; i < wr_ch; i++) {
pdata->ll_wr[i].bar = bar;
pdata->ll_wr[i].off = off;
pdata->ll_wr[i].sz = ll_size;
off += ll_off_gap;
}
/* Read channel LL region */
for (i = 0; i < rd_ch; i++) {
pdata->ll_rd[i].bar = bar;
pdata->ll_rd[i].off = off;
pdata->ll_rd[i].sz = ll_size;
off += ll_off_gap;
}
/* Write channel data region */
for (i = 0; i < wr_ch; i++) {
pdata->dt_wr[i].bar = bar;
pdata->dt_wr[i].off = off;
pdata->dt_wr[i].sz = dt_size;
off += dt_off_gap;
}
/* Read channel data region */
for (i = 0; i < rd_ch; i++) {
pdata->dt_rd[i].bar = bar;
pdata->dt_rd[i].off = off;
pdata->dt_rd[i].sz = dt_size;
off += dt_off_gap;
}
}
static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr)
{
return pci_irq_vector(to_pci_dev(dev), nr);
@@ -114,15 +194,15 @@ static const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = {
.pci_address = dw_edma_pcie_address,
};
static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
struct dw_edma_pcie_data *pdata)
static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev,
struct dw_edma_pcie_data *pdata)
{
u32 val, map;
u16 vsec;
u64 off;
vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
DW_PCIE_VSEC_DMA_ID);
DW_PCIE_SYNOPSYS_VSEC_DMA_ID);
if (!vsec)
return;
@@ -131,9 +211,9 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
PCI_VNDR_HEADER_LEN(val) != 0x18)
return;
pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n");
pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific Extended Capability DMA\n");
pci_read_config_dword(pdev, vsec + 0x8, &val);
map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val);
if (map != EDMA_MF_EDMA_LEGACY &&
map != EDMA_MF_EDMA_UNROLL &&
map != EDMA_MF_HDMA_COMPAT &&
@@ -141,13 +221,13 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
return;
pdata->mf = map;
pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
pci_read_config_dword(pdev, vsec + 0xc, &val);
pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val));
pci_read_config_dword(pdev, vsec + 0x14, &val);
off = val;
@@ -157,6 +237,73 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
pdata->rg.off = off;
}
static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
struct dw_edma_pcie_data *pdata)
{
u32 val, map;
u16 vsec;
u64 off;
pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
if (!vsec)
return;
pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
PCI_VNDR_HEADER_LEN(val) != 0x18)
return;
pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended Capability DMA\n");
pci_read_config_dword(pdev, vsec + 0x8, &val);
map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
if (map != EDMA_MF_HDMA_NATIVE)
return;
pdata->mf = map;
pdata->rg.bar = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR, val);
pci_read_config_dword(pdev, vsec + 0xc, &val);
pdata->wr_ch_cnt = min(pdata->wr_ch_cnt,
FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val));
pdata->rd_ch_cnt = min(pdata->rd_ch_cnt,
FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
pci_read_config_dword(pdev, vsec + 0x14, &val);
off = val;
pci_read_config_dword(pdev, vsec + 0x10, &val);
off <<= 32;
off |= val;
pdata->rg.off = off;
vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
DW_PCIE_XILINX_MDB_VSEC_ID);
if (!vsec)
return;
pci_read_config_dword(pdev,
vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
&val);
off = val;
pci_read_config_dword(pdev,
vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
&val);
off <<= 32;
off |= val;
pdata->devmem_phys_off = off;
}
static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
struct dw_edma_pcie_data *pdata,
enum pci_barno bar)
{
if (pdev->vendor == PCI_VENDOR_ID_XILINX)
return pdata->devmem_phys_off;
return pci_bus_address(pdev, bar);
}
static int dw_edma_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *pid)
{
@@ -165,6 +312,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
struct dw_edma_chip *chip;
int err, nr_irqs;
int i, mask;
bool non_ll = false;
struct dw_edma_pcie_data *vsec_data __free(kfree) =
kmalloc_obj(*vsec_data);
@@ -184,7 +332,32 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
* Tries to find if exists a PCIe Vendor-Specific Extended Capability
* for the DMA, if one exists, then reconfigures it.
*/
dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
/*
* There is no valid address found for the LL memory
* space on the device side. In the absence of LL base
* address use the non-LL mode or simple mode supported by
* the HDMA IP.
*/
if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
non_ll = true;
/*
* Configure the channel LL and data blocks if number of
* channels enabled in VSEC capability are more than the
* channels configured in xilinx_mdb_data.
*/
if (!non_ll)
dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
DW_PCIE_XILINX_MDB_LL_OFF_GAP,
DW_PCIE_XILINX_MDB_LL_SIZE,
DW_PCIE_XILINX_MDB_DT_OFF_GAP,
DW_PCIE_XILINX_MDB_DT_SIZE);
}
/* Mapping PCI BAR regions */
mask = BIT(vsec_data->rg.bar);
@@ -231,6 +404,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
chip->mf = vsec_data->mf;
chip->nr_irqs = nr_irqs;
chip->ops = &dw_edma_pcie_plat_ops;
chip->cfg_non_ll = non_ll;
chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
@@ -239,7 +413,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
if (!chip->reg_base)
return -ENOMEM;
for (i = 0; i < chip->ll_wr_cnt; i++) {
for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
@@ -250,7 +424,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
ll_region->vaddr.io += ll_block->off;
ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
ll_block->bar);
ll_region->paddr += ll_block->off;
ll_region->sz = ll_block->sz;
@@ -259,12 +434,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
dt_region->vaddr.io += dt_block->off;
dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
dt_block->bar);
dt_region->paddr += dt_block->off;
dt_region->sz = dt_block->sz;
}
for (i = 0; i < chip->ll_rd_cnt; i++) {
for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
@@ -275,7 +451,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
ll_region->vaddr.io += ll_block->off;
ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
ll_block->bar);
ll_region->paddr += ll_block->off;
ll_region->sz = ll_block->sz;
@@ -284,7 +461,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
dt_region->vaddr.io += dt_block->off;
dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
dt_block->bar);
dt_region->paddr += dt_block->off;
dt_region->sz = dt_block->sz;
}
@@ -367,6 +545,8 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev)
static const struct pci_device_id dw_edma_pcie_id_table[] = {
{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
(kernel_ulong_t)&xilinx_mdb_data },
{ }
};
MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
+21
View File
@@ -509,6 +509,25 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
dw_edma_v0_debugfs_on(dw);
}
static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw)
{
/*
* Interrupt emulation may assert the IRQ without setting
* DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the
* emulated IRQ, while being a no-op for real interrupts.
*/
SET_BOTH_32(dw, int_clear, 0);
}
static resource_size_t dw_edma_v0_core_db_offset(struct dw_edma *dw)
{
/*
* rd_int_status is chosen arbitrarily, but wr_int_status would be
* equally suitable.
*/
return offsetof(struct dw_edma_v0_regs, rd_int_status);
}
static const struct dw_edma_core_ops dw_edma_v0_core = {
.off = dw_edma_v0_core_off,
.ch_count = dw_edma_v0_core_ch_count,
@@ -517,6 +536,8 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
.start = dw_edma_v0_core_start,
.ch_config = dw_edma_v0_core_ch_config,
.debugfs_on = dw_edma_v0_core_debugfs_on,
.ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq,
.db_offset = dw_edma_v0_core_db_offset,
};
void dw_edma_v0_core_register(struct dw_edma *dw)
+70 -1
View File
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
readl(chunk->ll_region.vaddr.io);
}
static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
@@ -263,6 +263,68 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
}
static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
struct dw_edma_burst *child;
u32 val;
child = list_first_entry_or_null(&chunk->burst->list,
struct dw_edma_burst, list);
if (!child)
return;
SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN);
/* Source address */
SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
lower_32_bits(child->sar));
SET_CH_32(dw, chan->dir, chan->id, sar.msb,
upper_32_bits(child->sar));
/* Destination address */
SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
lower_32_bits(child->dar));
SET_CH_32(dw, chan->dir, chan->id, dar.msb,
upper_32_bits(child->dar));
/* Transfer size */
SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
/* Interrupt setup */
val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
HDMA_V0_STOP_INT_MASK |
HDMA_V0_ABORT_INT_MASK |
HDMA_V0_LOCAL_STOP_INT_EN |
HDMA_V0_LOCAL_ABORT_INT_EN;
if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
val |= HDMA_V0_REMOTE_STOP_INT_EN |
HDMA_V0_REMOTE_ABORT_INT_EN;
}
SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
/* Channel control setup */
val = GET_CH_32(dw, chan->dir, chan->id, control1);
val &= ~HDMA_V0_LINKLIST_EN;
SET_CH_32(dw, chan->dir, chan->id, control1, val);
SET_CH_32(dw, chan->dir, chan->id, doorbell,
HDMA_V0_DOORBELL_START);
}
static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
if (chan->non_ll)
dw_hdma_v0_core_non_ll_start(chunk);
else
dw_hdma_v0_core_ll_start(chunk, first);
}
static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -283,6 +345,12 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
dw_hdma_v0_debugfs_on(dw);
}
static resource_size_t dw_hdma_v0_core_db_offset(struct dw_edma *dw)
{
/* Implement once the correct offset is known. */
return ~0;
}
static const struct dw_edma_core_ops dw_hdma_v0_core = {
.off = dw_hdma_v0_core_off,
.ch_count = dw_hdma_v0_core_ch_count,
@@ -291,6 +359,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
.start = dw_hdma_v0_core_start,
.ch_config = dw_hdma_v0_core_ch_config,
.debugfs_on = dw_hdma_v0_core_debugfs_on,
.db_offset = dw_hdma_v0_core_db_offset,
};
void dw_hdma_v0_core_register(struct dw_edma *dw)
+1
View File
@@ -12,6 +12,7 @@
#include <linux/dmaengine.h>
#define HDMA_V0_MAX_NR_CH 8
#define HDMA_V0_CH_EN BIT(0)
#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)
#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4)
+1 -2
View File
@@ -905,8 +905,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan)
fsl_chan->is_sw = false;
fsl_chan->srcid = 0;
fsl_chan->is_remote = false;
if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
clk_disable_unprepare(fsl_chan->clk);
clk_disable_unprepare(fsl_chan->clk);
}
void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
+22 -33
View File
@@ -705,16 +705,14 @@ static int fsl_edma_probe(struct platform_device *pdev)
int ret, i;
drvdata = device_get_match_data(&pdev->dev);
if (!drvdata) {
dev_err(&pdev->dev, "unable to find driver data\n");
return -EINVAL;
}
if (!drvdata)
return dev_err_probe(&pdev->dev, -EINVAL,
"unable to find driver data\n");
ret = of_property_read_u32(np, "dma-channels", &chans);
if (ret) {
dev_err(&pdev->dev, "Can't get dma-channels.\n");
return ret;
}
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Can't get dma-channels.\n");
fsl_edma = devm_kzalloc(&pdev->dev, struct_size(fsl_edma, chans, chans),
GFP_KERNEL);
@@ -738,10 +736,10 @@ static int fsl_edma_probe(struct platform_device *pdev)
if (drvdata->flags & FSL_EDMA_DRV_HAS_DMACLK) {
fsl_edma->dmaclk = devm_clk_get_enabled(&pdev->dev, "dma");
if (IS_ERR(fsl_edma->dmaclk)) {
dev_err(&pdev->dev, "Missing DMA block clock.\n");
return PTR_ERR(fsl_edma->dmaclk);
}
if (IS_ERR(fsl_edma->dmaclk))
return dev_err_probe(&pdev->dev,
PTR_ERR(fsl_edma->dmaclk),
"Missing DMA block clock.\n");
}
ret = of_property_read_variable_u32_array(np, "dma-channel-mask", chan_mask, 1, 2);
@@ -765,11 +763,10 @@ static int fsl_edma_probe(struct platform_device *pdev)
sprintf(clkname, "dmamux%d", i);
fsl_edma->muxclk[i] = devm_clk_get_enabled(&pdev->dev, clkname);
if (IS_ERR(fsl_edma->muxclk[i])) {
dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
/* on error: disable all previously enabled clks */
return PTR_ERR(fsl_edma->muxclk[i]);
}
if (IS_ERR(fsl_edma->muxclk[i]))
return dev_err_probe(&pdev->dev,
PTR_ERR(fsl_edma->muxclk[i]),
"Missing DMAMUX block clock.\n");
}
fsl_edma->big_endian = of_property_read_bool(np, "big-endian");
@@ -878,22 +875,17 @@ static int fsl_edma_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fsl_edma);
ret = dma_async_device_register(&fsl_edma->dma_dev);
if (ret) {
dev_err(&pdev->dev,
"Can't register Freescale eDMA engine. (%d)\n", ret);
return ret;
}
ret = dmaenginem_async_device_register(&fsl_edma->dma_dev);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Can't register Freescale eDMA engine.\n");
ret = of_dma_controller_register(np,
ret = devm_of_dma_controller_register(&pdev->dev, np,
drvdata->dmamuxs ? fsl_edma_xlate : fsl_edma3_xlate,
fsl_edma);
if (ret) {
dev_err(&pdev->dev,
"Can't register Freescale eDMA of_dma. (%d)\n", ret);
dma_async_device_unregister(&fsl_edma->dma_dev);
return ret;
}
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Can't register Freescale eDMA of_dma.\n");
/* enable round robin arbitration */
if (!(drvdata->flags & FSL_EDMA_DRV_SPLIT_REG))
@@ -904,12 +896,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
static void fsl_edma_remove(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
fsl_edma_irq_exit(pdev, fsl_edma);
of_dma_controller_free(np);
dma_async_device_unregister(&fsl_edma->dma_dev);
fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
}

Some files were not shown because too many files have changed in this diff Show More