mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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:
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user