mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
Merge 328141e51e ("Merge tag 'mmc-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc") into android-mainline
Steps on the way to 6.0-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I6c72d9c0c9d6d83ec087eadc92610f22a2267a18
This commit is contained in:
80
Documentation/devicetree/bindings/dma/apple,admac.yaml
Normal file
80
Documentation/devicetree/bindings/dma/apple,admac.yaml
Normal file
@@ -0,0 +1,80 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/apple,admac.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple Audio DMA Controller (ADMAC)
|
||||
|
||||
description: |
|
||||
Apple's Audio DMA Controller (ADMAC) is used to fetch and store audio samples
|
||||
on SoCs from the "Apple Silicon" family.
|
||||
|
||||
The controller has been seen with up to 24 channels. Even-numbered channels
|
||||
are TX-only, odd-numbered are RX-only. Individual channels are coupled to
|
||||
fixed device endpoints.
|
||||
|
||||
maintainers:
|
||||
- Martin Povišer <povik+lin@cutebit.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t6000-admac
|
||||
- apple,t8103-admac
|
||||
- const: apple,admac
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#dma-cells':
|
||||
const: 1
|
||||
description:
|
||||
Clients specify a single cell with channel number.
|
||||
|
||||
dma-channels:
|
||||
maximum: 24
|
||||
|
||||
interrupts:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
description:
|
||||
Interrupts that correspond to the 4 IRQ outputs of the controller. Usually
|
||||
only one of the controller outputs will be connected as an usable interrupt
|
||||
source. The remaining interrupts will be left without a valid value, e.g.
|
||||
in an interrupts-extended list the disconnected positions will contain
|
||||
an empty phandle reference <0>.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#dma-cells'
|
||||
- dma-channels
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
aic: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
};
|
||||
|
||||
admac: dma-controller@238200000 {
|
||||
compatible = "apple,t8103-admac", "apple,admac";
|
||||
reg = <0x38200000 0x34000>;
|
||||
dma-channels = <24>;
|
||||
interrupts-extended = <0>,
|
||||
<&aic AIC_IRQ 626 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0>,
|
||||
<0>;
|
||||
#dma-cells = <1>;
|
||||
};
|
||||
155
Documentation/devicetree/bindings/dma/fsl,edma.yaml
Normal file
155
Documentation/devicetree/bindings/dma/fsl,edma.yaml
Normal file
@@ -0,0 +1,155 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/fsl,edma.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale enhanced Direct Memory Access(eDMA) Controller
|
||||
|
||||
description: |
|
||||
The eDMA channels have multiplex capability by programmable
|
||||
memory-mapped registers. channels are split into two groups, called
|
||||
DMAMUX0 and DMAMUX1, specific DMA request source can only be multiplexed
|
||||
by any channel of certain group, DMAMUX0 or DMAMUX1, but not both.
|
||||
|
||||
maintainers:
|
||||
- Peng Fan <peng.fan@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,vf610-edma
|
||||
- fsl,imx7ulp-edma
|
||||
- items:
|
||||
- const: fsl,ls1028a-edma
|
||||
- const: fsl,vf610-edma
|
||||
|
||||
reg:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
interrupts:
|
||||
minItems: 2
|
||||
maxItems: 17
|
||||
|
||||
interrupt-names:
|
||||
minItems: 2
|
||||
maxItems: 17
|
||||
|
||||
"#dma-cells":
|
||||
const: 2
|
||||
|
||||
dma-channels:
|
||||
const: 32
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
maxItems: 2
|
||||
|
||||
big-endian:
|
||||
description: |
|
||||
If present registers and hardware scatter/gather descriptors of the
|
||||
eDMA are implemented in big endian mode, otherwise in little mode.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- "#dma-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- dma-channels
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,vf610-edma
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: dmamux0
|
||||
- const: dmamux1
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: edma-tx
|
||||
- const: edma-err
|
||||
reg:
|
||||
maxItems: 3
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,imx7ulp-edma
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: dma
|
||||
- const: dmamux0
|
||||
interrupts:
|
||||
maxItems: 17
|
||||
reg:
|
||||
maxItems: 2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/vf610-clock.h>
|
||||
|
||||
edma0: dma-controller@40018000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,vf610-edma";
|
||||
reg = <0x40018000 0x2000>,
|
||||
<0x40024000 0x1000>,
|
||||
<0x40025000 0x1000>;
|
||||
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "edma-tx", "edma-err";
|
||||
dma-channels = <32>;
|
||||
clock-names = "dmamux0", "dmamux1";
|
||||
clocks = <&clks VF610_CLK_DMAMUX0>, <&clks VF610_CLK_DMAMUX1>;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/imx7ulp-clock.h>
|
||||
|
||||
edma1: dma-controller@40080000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,imx7ulp-edma";
|
||||
reg = <0x40080000 0x2000>,
|
||||
<0x40210000 0x1000>;
|
||||
dma-channels = <32>;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
|
||||
/* last is eDMA2-ERR interrupt */
|
||||
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "dma", "dmamux0";
|
||||
clocks = <&pcc2 IMX7ULP_CLK_DMA1>, <&pcc2 IMX7ULP_CLK_DMA_MUX1>;
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
* Freescale enhanced Direct Memory Access(eDMA) Controller
|
||||
|
||||
The eDMA channels have multiplex capability by programmble memory-mapped
|
||||
registers. channels are split into two groups, called DMAMUX0 and DMAMUX1,
|
||||
specific DMA request source can only be multiplexed by any channel of certain
|
||||
group, DMAMUX0 or DMAMUX1, but not both.
|
||||
|
||||
* eDMA Controller
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC
|
||||
- "fsl,imx7ulp-edma" for eDMA2 used similar to that on i.mx7ulp
|
||||
- "fsl,ls1028a-edma" followed by "fsl,vf610-edma" for eDMA used on the
|
||||
LS1028A SoC.
|
||||
- reg : Specifies base physical address(s) and size of the eDMA registers.
|
||||
The 1st region is eDMA control register's address and size.
|
||||
The 2nd and the 3rd regions are programmable channel multiplexing
|
||||
control register's address and size.
|
||||
- interrupts : A list of interrupt-specifiers, one for each entry in
|
||||
interrupt-names on vf610 similar SoC. But for i.mx7ulp per channel
|
||||
per transmission interrupt, total 16 channel interrupt and 1
|
||||
error interrupt(located in the last), no interrupt-names list on
|
||||
i.mx7ulp for clean on dts.
|
||||
- #dma-cells : Must be <2>.
|
||||
The 1st cell specifies the DMAMUX(0 for DMAMUX0 and 1 for DMAMUX1).
|
||||
Specific request source can only be multiplexed by specific channels
|
||||
group called DMAMUX.
|
||||
The 2nd cell specifies the request source(slot) ID.
|
||||
See the SoC's reference manual for all the supported request sources.
|
||||
- dma-channels : Number of channels supported by the controller
|
||||
- clock-names : A list of channel group clock names. Should contain:
|
||||
"dmamux0" - clock name of mux0 group
|
||||
"dmamux1" - clock name of mux1 group
|
||||
Note: No dmamux0 on i.mx7ulp, but another 'dma' clk added on i.mx7ulp.
|
||||
- clocks : A list of phandle and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
|
||||
Optional properties:
|
||||
- big-endian: If present registers and hardware scatter/gather descriptors
|
||||
of the eDMA are implemented in big endian mode, otherwise in little
|
||||
mode.
|
||||
- interrupt-names : Should contain the below on vf610 similar SoC but not used
|
||||
on i.mx7ulp similar SoC:
|
||||
"edma-tx" - the transmission interrupt
|
||||
"edma-err" - the error interrupt
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
edma0: dma-controller@40018000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,vf610-edma";
|
||||
reg = <0x40018000 0x2000>,
|
||||
<0x40024000 0x1000>,
|
||||
<0x40025000 0x1000>;
|
||||
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "edma-tx", "edma-err";
|
||||
dma-channels = <32>;
|
||||
clock-names = "dmamux0", "dmamux1";
|
||||
clocks = <&clks VF610_CLK_DMAMUX0>,
|
||||
<&clks VF610_CLK_DMAMUX1>;
|
||||
}; /* vf610 */
|
||||
|
||||
edma1: dma-controller@40080000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,imx7ulp-edma";
|
||||
reg = <0x40080000 0x2000>,
|
||||
<0x40210000 0x1000>;
|
||||
dma-channels = <32>;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
|
||||
/* last is eDMA2-ERR interrupt */
|
||||
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "dma", "dmamux0";
|
||||
clocks = <&pcc2 IMX7ULP_CLK_DMA1>,
|
||||
<&pcc2 IMX7ULP_CLK_DMA_MUX1>;
|
||||
}; /* i.mx7ulp */
|
||||
|
||||
* DMA clients
|
||||
DMA client drivers that uses the DMA function must use the format described
|
||||
in the dma.txt file, using a two-cell specifier for each channel: the 1st
|
||||
specifies the channel group(DMAMUX) in which this request can be multiplexed,
|
||||
and the 2nd specifies the request source.
|
||||
|
||||
Examples:
|
||||
|
||||
sai2: sai@40031000 {
|
||||
compatible = "fsl,vf610-sai";
|
||||
reg = <0x40031000 0x1000>;
|
||||
interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "sai";
|
||||
clocks = <&clks VF610_CLK_SAI2>;
|
||||
dma-names = "tx", "rx";
|
||||
dmas = <&edma0 0 21>,
|
||||
<&edma0 0 20>;
|
||||
};
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt2712-uart-dma
|
||||
- mediatek,mt8365-uart-dma
|
||||
- mediatek,mt8516-uart-dma
|
||||
- const: mediatek,mt6577-uart-dma
|
||||
- enum:
|
||||
|
||||
@@ -23,7 +23,9 @@ properties:
|
||||
oneOf:
|
||||
- const: nvidia,tegra186-gpcdma
|
||||
- items:
|
||||
- const: nvidia,tegra194-gpcdma
|
||||
- enum:
|
||||
- nvidia,tegra234-gpcdma
|
||||
- nvidia,tegra194-gpcdma
|
||||
- const: nvidia,tegra186-gpcdma
|
||||
|
||||
"#dma-cells":
|
||||
|
||||
@@ -34,7 +34,12 @@ properties:
|
||||
- const: axidma_apb_regs
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
If the IP-core synthesis parameter DMAX_INTR_IO_TYPE is set to 1, this
|
||||
will be per-channel interrupts. Otherwise, this is a single combined IRQ
|
||||
for all channels.
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
clocks:
|
||||
items:
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
* DMA40 DMA Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "stericsson,dma40"
|
||||
- reg: Address range of the DMAC registers
|
||||
- reg-names: Names of the above areas to use during resource look-up
|
||||
- interrupt: Should contain the DMAC interrupt number
|
||||
- #dma-cells: must be <3>
|
||||
- memcpy-channels: Channels to be used for memcpy
|
||||
|
||||
Optional properties:
|
||||
- dma-channels: Number of channels supported by hardware - if not present
|
||||
the driver will attempt to obtain the information from H/W
|
||||
- disabled-channels: Channels which can not be used
|
||||
|
||||
Example:
|
||||
|
||||
dma: dma-controller@801c0000 {
|
||||
compatible = "stericsson,db8500-dma40", "stericsson,dma40";
|
||||
reg = <0x801C0000 0x1000 0x40010000 0x800>;
|
||||
reg-names = "base", "lcpa";
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 25 0x4>;
|
||||
|
||||
#dma-cells = <2>;
|
||||
memcpy-channels = <56 57 58 59 60>;
|
||||
disabled-channels = <12>;
|
||||
dma-channels = <8>;
|
||||
};
|
||||
|
||||
Clients
|
||||
Required properties:
|
||||
- dmas: Comma separated list of dma channel requests
|
||||
- dma-names: Names of the aforementioned requested channels
|
||||
|
||||
Each dmas request consists of 4 cells:
|
||||
1. A phandle pointing to the DMA controller
|
||||
2. Device signal number, the signal line for single and burst requests
|
||||
connected from the device to the DMA40 engine
|
||||
3. The DMA request line number (only when 'use fixed channel' is set)
|
||||
4. A 32bit mask specifying; mode, direction and endianness
|
||||
[NB: This list will grow]
|
||||
0x00000001: Mode:
|
||||
Logical channel when unset
|
||||
Physical channel when set
|
||||
0x00000002: Direction:
|
||||
Memory to Device when unset
|
||||
Device to Memory when set
|
||||
0x00000004: Endianness:
|
||||
Little endian when unset
|
||||
Big endian when set
|
||||
0x00000008: Use fixed channel:
|
||||
Use automatic channel selection when unset
|
||||
Use DMA request line number when set
|
||||
0x00000010: Set channel as high priority:
|
||||
Normal priority when unset
|
||||
High priority when set
|
||||
|
||||
Existing signal numbers for the DB8500 ASIC. Unless specified, the signals are
|
||||
bidirectional, i.e. the same for RX and TX operations:
|
||||
|
||||
0: SPI controller 0
|
||||
1: SD/MMC controller 0 (unused)
|
||||
2: SD/MMC controller 1 (unused)
|
||||
3: SD/MMC controller 2 (unused)
|
||||
4: I2C port 1
|
||||
5: I2C port 3
|
||||
6: I2C port 2
|
||||
7: I2C port 4
|
||||
8: Synchronous Serial Port SSP0
|
||||
9: Synchronous Serial Port SSP1
|
||||
10: Multi-Channel Display Engine MCDE RX
|
||||
11: UART port 2
|
||||
12: UART port 1
|
||||
13: UART port 0
|
||||
14: Multirate Serial Port MSP2
|
||||
15: I2C port 0
|
||||
16: USB OTG in/out endpoints 7 & 15
|
||||
17: USB OTG in/out endpoints 6 & 14
|
||||
18: USB OTG in/out endpoints 5 & 13
|
||||
19: USB OTG in/out endpoints 4 & 12
|
||||
20: SLIMbus or HSI channel 0
|
||||
21: SLIMbus or HSI channel 1
|
||||
22: SLIMbus or HSI channel 2
|
||||
23: SLIMbus or HSI channel 3
|
||||
24: Multimedia DSP SXA0
|
||||
25: Multimedia DSP SXA1
|
||||
26: Multimedia DSP SXA2
|
||||
27: Multimedia DSP SXA3
|
||||
28: SD/MM controller 2
|
||||
29: SD/MM controller 0
|
||||
30: MSP port 1 on DB8500 v1, MSP port 3 on DB8500 v2
|
||||
31: MSP port 0 or SLIMbus channel 0
|
||||
32: SD/MM controller 1
|
||||
33: SPI controller 2
|
||||
34: i2c3 RX2 TX2
|
||||
35: SPI controller 1
|
||||
36: USB OTG in/out endpoints 3 & 11
|
||||
37: USB OTG in/out endpoints 2 & 10
|
||||
38: USB OTG in/out endpoints 1 & 9
|
||||
39: USB OTG in/out endpoints 8
|
||||
40: SPI controller 3
|
||||
41: SD/MM controller 3
|
||||
42: SD/MM controller 4
|
||||
43: SD/MM controller 5
|
||||
44: Multimedia DSP SXA4
|
||||
45: Multimedia DSP SXA5
|
||||
46: SLIMbus channel 8 or Multimedia DSP SXA6
|
||||
47: SLIMbus channel 9 or Multimedia DSP SXA7
|
||||
48: Crypto Accelerator 1
|
||||
49: Crypto Accelerator 1 TX or Hash Accelerator 1 TX
|
||||
50: Hash Accelerator 1 TX
|
||||
51: memcpy TX (to be used by the DMA driver for memcpy operations)
|
||||
52: SLIMbus or HSI channel 4
|
||||
53: SLIMbus or HSI channel 5
|
||||
54: SLIMbus or HSI channel 6
|
||||
55: SLIMbus or HSI channel 7
|
||||
56: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
57: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
58: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
59: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
60: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
61: Crypto Accelerator 0
|
||||
62: Crypto Accelerator 0 TX or Hash Accelerator 0 TX
|
||||
63: Hash Accelerator 0 TX
|
||||
|
||||
Example:
|
||||
|
||||
uart@80120000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x80120000 0x1000>;
|
||||
interrupts = <0 11 0x4>;
|
||||
|
||||
dmas = <&dma 13 0 0x2>, /* Logical - DevToMem */
|
||||
<&dma 13 0 0x0>; /* Logical - MemToDev */
|
||||
dma-names = "rx", "rx";
|
||||
|
||||
};
|
||||
159
Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
Normal file
159
Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
Normal file
@@ -0,0 +1,159 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dma/stericsson,dma40.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ST-Ericsson DMA40 DMA Engine
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "dma-controller.yaml#"
|
||||
|
||||
properties:
|
||||
"#dma-cells":
|
||||
const: 3
|
||||
description: |
|
||||
The first cell is the unique device channel number as indicated by this
|
||||
table for DB8500 which is the only ASIC known to use DMA40:
|
||||
|
||||
0: SPI controller 0
|
||||
1: SD/MMC controller 0 (unused)
|
||||
2: SD/MMC controller 1 (unused)
|
||||
3: SD/MMC controller 2 (unused)
|
||||
4: I2C port 1
|
||||
5: I2C port 3
|
||||
6: I2C port 2
|
||||
7: I2C port 4
|
||||
8: Synchronous Serial Port SSP0
|
||||
9: Synchronous Serial Port SSP1
|
||||
10: Multi-Channel Display Engine MCDE RX
|
||||
11: UART port 2
|
||||
12: UART port 1
|
||||
13: UART port 0
|
||||
14: Multirate Serial Port MSP2
|
||||
15: I2C port 0
|
||||
16: USB OTG in/out endpoints 7 & 15
|
||||
17: USB OTG in/out endpoints 6 & 14
|
||||
18: USB OTG in/out endpoints 5 & 13
|
||||
19: USB OTG in/out endpoints 4 & 12
|
||||
20: SLIMbus or HSI channel 0
|
||||
21: SLIMbus or HSI channel 1
|
||||
22: SLIMbus or HSI channel 2
|
||||
23: SLIMbus or HSI channel 3
|
||||
24: Multimedia DSP SXA0
|
||||
25: Multimedia DSP SXA1
|
||||
26: Multimedia DSP SXA2
|
||||
27: Multimedia DSP SXA3
|
||||
28: SD/MMC controller 2
|
||||
29: SD/MMC controller 0
|
||||
30: MSP port 1 on DB8500 v1, MSP port 3 on DB8500 v2
|
||||
31: MSP port 0 or SLIMbus channel 0
|
||||
32: SD/MMC controller 1
|
||||
33: SPI controller 2
|
||||
34: i2c3 RX2 TX2
|
||||
35: SPI controller 1
|
||||
36: USB OTG in/out endpoints 3 & 11
|
||||
37: USB OTG in/out endpoints 2 & 10
|
||||
38: USB OTG in/out endpoints 1 & 9
|
||||
39: USB OTG in/out endpoints 8
|
||||
40: SPI controller 3
|
||||
41: SD/MMC controller 3
|
||||
42: SD/MMC controller 4
|
||||
43: SD/MMC controller 5
|
||||
44: Multimedia DSP SXA4
|
||||
45: Multimedia DSP SXA5
|
||||
46: SLIMbus channel 8 or Multimedia DSP SXA6
|
||||
47: SLIMbus channel 9 or Multimedia DSP SXA7
|
||||
48: Crypto Accelerator 1
|
||||
49: Crypto Accelerator 1 TX or Hash Accelerator 1 TX
|
||||
50: Hash Accelerator 1 TX
|
||||
51: memcpy TX (to be used by the DMA driver for memcpy operations)
|
||||
52: SLIMbus or HSI channel 4
|
||||
53: SLIMbus or HSI channel 5
|
||||
54: SLIMbus or HSI channel 6
|
||||
55: SLIMbus or HSI channel 7
|
||||
56: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
57: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
58: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
59: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
60: memcpy (to be used by the DMA driver for memcpy operations)
|
||||
61: Crypto Accelerator 0
|
||||
62: Crypto Accelerator 0 TX or Hash Accelerator 0 TX
|
||||
63: Hash Accelerator 0 TX
|
||||
|
||||
The second cell is the DMA request line number. This is only used when
|
||||
a fixed channel is allocated, and indicated by setting bit 3 in the
|
||||
flags field (see below).
|
||||
|
||||
The third cell is a 32bit flags bitfield with the following possible
|
||||
bits set:
|
||||
0x00000001 (bit 0) - mode:
|
||||
Logical channel when unset
|
||||
Physical channel when set
|
||||
0x00000002 (bit 1) - direction:
|
||||
Memory to Device when unset
|
||||
Device to Memory when set
|
||||
0x00000004 (bit 2) - endianness:
|
||||
Little endian when unset
|
||||
Big endian when set
|
||||
0x00000008 (bit 3) - use fixed channel:
|
||||
Use automatic channel selection when unset
|
||||
Use DMA request line number when set
|
||||
0x00000010 (bit 4) - set channel as high priority:
|
||||
Normal priority when unset
|
||||
High priority when set
|
||||
|
||||
compatible:
|
||||
items:
|
||||
- const: stericsson,db8500-dma40
|
||||
- const: stericsson,dma40
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: DMA40 memory base
|
||||
- description: LCPA memory base
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: base
|
||||
- const: lcpa
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
memcpy-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: Array of u32 elements indicating which channels on the DMA
|
||||
engine are elegible for memcpy transfers
|
||||
|
||||
required:
|
||||
- "#dma-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- memcpy-channels
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/mfd/dbx500-prcmu.h>
|
||||
dma-controller@801C0000 {
|
||||
compatible = "stericsson,db8500-dma40", "stericsson,dma40";
|
||||
reg = <0x801C0000 0x1000>, <0x40010000 0x800>;
|
||||
reg-names = "base", "lcpa";
|
||||
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#dma-cells = <3>;
|
||||
memcpy-channels = <56 57 58 59 60>;
|
||||
clocks = <&prcmu_clk PRCMU_DMACLK>;
|
||||
};
|
||||
...
|
||||
@@ -10,9 +10,6 @@ maintainers:
|
||||
- Al Cooper <alcooperx@gmail.com>
|
||||
- Florian Fainelli <f.fainelli@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@@ -42,23 +39,46 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description:
|
||||
handle to core clock for the sdhci controller.
|
||||
minItems: 1
|
||||
items:
|
||||
- description: handle to core clock for the sdhci controller
|
||||
- description: handle to improved 150Mhz clock for sdhci controller (Optional clock)
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: sw_sdio
|
||||
- const: sdio_freq # Optional clock
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Maximum operating frequency of sdio_freq sdhci controller clock
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 100000000
|
||||
maximum: 150000000
|
||||
|
||||
sdhci,auto-cmd12:
|
||||
type: boolean
|
||||
description: Specifies that controller should use auto CMD12
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
clock-names:
|
||||
contains:
|
||||
const: sdio_freq
|
||||
|
||||
then:
|
||||
required:
|
||||
- clock-frequency
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
* Samsung Exynos specific extensions to the Synopsys Designware Mobile
|
||||
Storage Host Controller
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsys dw mshc controller properties described
|
||||
by synopsys-dw-mshc.txt and the properties used by the Samsung Exynos specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
|
||||
specific extensions.
|
||||
- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
|
||||
specific extensions.
|
||||
- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
|
||||
specific extensions.
|
||||
- "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420
|
||||
specific extensions.
|
||||
- "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7
|
||||
specific extensions.
|
||||
- "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7
|
||||
specific extensions having an SMU.
|
||||
- "axis,artpec8-dw-mshc": for controllers with ARTPEC-8 specific
|
||||
extensions.
|
||||
|
||||
* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
|
||||
unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
|
||||
ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
|
||||
|
||||
* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
|
||||
in transmit mode and CIU clock phase shift value in receive mode for single
|
||||
data rate mode operation. Refer notes below for the order of the cells and the
|
||||
valid values.
|
||||
|
||||
* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
|
||||
in transmit mode and CIU clock phase shift value in receive mode for double
|
||||
data rate mode operation. Refer notes below for the order of the cells and the
|
||||
valid values.
|
||||
* samsung,dw-mshc-hs400-timing: Specifies the value of CIU TX and RX clock phase
|
||||
shift value for hs400 mode operation.
|
||||
|
||||
Notes for the sdr-timing and ddr-timing values:
|
||||
|
||||
The order of the cells should be
|
||||
- First Cell: CIU clock phase shift value for tx mode.
|
||||
- Second Cell: CIU clock phase shift value for rx mode.
|
||||
|
||||
Valid values for SDR and DDR CIU clock timing for Exynos5250:
|
||||
- valid value for tx phase shift and rx phase shift is 0 to 7.
|
||||
- when CIU clock divider value is set to 3, all possible 8 phase shift
|
||||
values can be used.
|
||||
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
||||
phase shift clocks should be 0.
|
||||
|
||||
* samsung,read-strobe-delay: RCLK (Data strobe) delay to control HS400 mode
|
||||
(Latency value for delay line in Read path)
|
||||
|
||||
Required properties for a slot (Deprecated - Recommend to use one slot per host):
|
||||
|
||||
* gpios: specifies a list of gpios used for command, clock and data bus. The
|
||||
first gpio is the command line and the second gpio is the clock line. The
|
||||
rest of the gpios (depending on the bus-width property) are the data lines in
|
||||
no particular order. The format of the gpio specifier depends on the gpio
|
||||
controller.
|
||||
(Deprecated - Refer to Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt)
|
||||
|
||||
Example:
|
||||
|
||||
The MSHC controller node can be split into two portions, SoC specific and
|
||||
board specific portions as listed below.
|
||||
|
||||
dwmmc0@12200000 {
|
||||
compatible = "samsung,exynos5250-dw-mshc";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
dwmmc0@12200000 {
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <2 3>;
|
||||
samsung,dw-mshc-ddr-timing = <1 2>;
|
||||
samsung,dw-mshc-hs400-timing = <0 2>;
|
||||
samsung,read-strobe-delay = <90>;
|
||||
bus-width = <8>;
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
MMC/SD/SDIO slot directly connected to a SPI bus
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the mmc_spi driver.
|
||||
|
||||
Required properties:
|
||||
- spi-max-frequency : maximum frequency for this device (Hz).
|
||||
|
||||
Optional properties:
|
||||
- voltage-ranges : two cells are required, first cell specifies minimum
|
||||
slot voltage (mV), second cell specifies maximum slot voltage (mV).
|
||||
Several ranges could be specified. If not provided, 3.2v..3.4v is assumed.
|
||||
- gpios : may specify GPIOs in this order: Card-Detect GPIO,
|
||||
Write-Protect GPIO. Note that this does not follow the
|
||||
binding from mmc.txt, for historical reasons.
|
||||
|
||||
Example:
|
||||
|
||||
mmc-slot@0 {
|
||||
compatible = "fsl,mpc8323rdb-mmc-slot",
|
||||
"mmc-spi-slot";
|
||||
reg = <0>;
|
||||
gpios = <&qe_pio_d 14 1
|
||||
&qe_pio_d 15 0>;
|
||||
voltage-ranges = <3300 3300>;
|
||||
spi-max-frequency = <50000000>;
|
||||
interrupts = <42>;
|
||||
interrupt-parent = <&PIC>;
|
||||
};
|
||||
77
Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
Normal file
77
Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
Normal file
@@ -0,0 +1,77 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/mmc-spi-slot.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MMC/SD/SDIO slot directly connected to a SPI bus
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "mmc-controller.yaml"
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml
|
||||
|
||||
description: |
|
||||
The extra properties used by an mmc connected via SPI.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mmc-spi-slot
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
voltage-ranges:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: |
|
||||
Two cells are required, first cell specifies minimum slot voltage (mV),
|
||||
second cell specifies maximum slot voltage (mV).
|
||||
items:
|
||||
- description: |
|
||||
value for minimum slot voltage in mV
|
||||
default: 3200
|
||||
- description: |
|
||||
value for maximum slot voltage in mV
|
||||
default: 3400
|
||||
|
||||
gpios:
|
||||
description: |
|
||||
For historical reasons, this does not follow the generic mmc-controller
|
||||
binding.
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Card-Detect GPIO
|
||||
- description: Write-Protect GPIO
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-max-frequency
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
mmc@0 {
|
||||
compatible = "mmc-spi-slot";
|
||||
reg = <0>;
|
||||
gpios = <&gpio 14 GPIO_ACTIVE_LOW>, <&gpio 15 GPIO_ACTIVE_HIGH>;
|
||||
voltage-ranges = <3300 3300>;
|
||||
spi-max-frequency = <50000000>;
|
||||
interrupts = <42>;
|
||||
interrupt-parent = <&PIC>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -30,13 +30,11 @@ properties:
|
||||
- const: mediatek,mt7623-mmc
|
||||
- const: mediatek,mt2701-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8186-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8192-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8195-mmc
|
||||
- enum:
|
||||
- mediatek,mt8186-mmc
|
||||
- mediatek,mt8188-mmc
|
||||
- mediatek,mt8192-mmc
|
||||
- mediatek,mt8195-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
|
||||
reg:
|
||||
@@ -72,12 +70,27 @@ properties:
|
||||
- const: ahb_cg
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
Should at least contain MSDC GIC interrupt. To support SDIO in-band wakeup, an extended
|
||||
interrupt is required and be configured as wakeup source irq.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: msdc
|
||||
- const: sdio_wakeup
|
||||
|
||||
pinctrl-names:
|
||||
description:
|
||||
Should at least contain default and state_uhs. To support SDIO in-band wakeup, dat1 pin
|
||||
will be switched between GPIO mode and SDIO DAT1 mode, state_eint is mandatory in this
|
||||
scenario.
|
||||
minItems: 2
|
||||
items:
|
||||
- const: default
|
||||
- const: state_uhs
|
||||
- const: state_eint
|
||||
|
||||
pinctrl-0:
|
||||
description:
|
||||
@@ -89,6 +102,11 @@ properties:
|
||||
should contain uhs mode pin ctrl.
|
||||
maxItems: 1
|
||||
|
||||
pinctrl-2:
|
||||
description:
|
||||
should switch dat1 pin to GPIO mode.
|
||||
maxItems: 1
|
||||
|
||||
assigned-clocks:
|
||||
description:
|
||||
PLL of the source clock.
|
||||
@@ -208,4 +226,32 @@ examples:
|
||||
mediatek,hs400-cmd-resp-sel-rising;
|
||||
};
|
||||
|
||||
mmc3: mmc@11260000 {
|
||||
compatible = "mediatek,mt8173-mmc";
|
||||
reg = <0x11260000 0x1000>;
|
||||
clock-names = "source", "hclk";
|
||||
clocks = <&pericfg CLK_PERI_MSDC30_3>,
|
||||
<&topckgen CLK_TOP_MSDC50_2_H_SEL>;
|
||||
interrupt-names = "msdc", "sdio_wakeup";
|
||||
interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_LOW 0>,
|
||||
<&pio 23 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-names = "default", "state_uhs", "state_eint";
|
||||
pinctrl-0 = <&mmc2_pins_default>;
|
||||
pinctrl-1 = <&mmc2_pins_uhs>;
|
||||
pinctrl-2 = <&mmc2_pins_eint>;
|
||||
bus-width = <4>;
|
||||
max-frequency = <200000000>;
|
||||
cap-sd-highspeed;
|
||||
sd-uhs-sdr104;
|
||||
keep-power-in-suspend;
|
||||
wakeup-source;
|
||||
cap-sdio-irq;
|
||||
no-mmc;
|
||||
no-sd;
|
||||
non-removable;
|
||||
vmmc-supply = <&sdio_fixed_3v3>;
|
||||
vqmmc-supply = <&mt6397_vgp3_reg>;
|
||||
mmc-pwrseq = <&wifi_pwrseq>;
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
@@ -56,11 +56,15 @@ properties:
|
||||
- renesas,sdhi-r8a77980 # R-Car V3H
|
||||
- renesas,sdhi-r8a77990 # R-Car E3
|
||||
- renesas,sdhi-r8a77995 # R-Car D3
|
||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||
- renesas,sdhi-r9a07g043 # RZ/G2UL
|
||||
- renesas,sdhi-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,sdhi-r9a07g054 # RZ/V2L
|
||||
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||
- renesas,sdhi-r8a779f0 # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-sdhi # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -141,6 +145,7 @@ allOf:
|
||||
enum:
|
||||
- renesas,rcar-gen2-sdhi
|
||||
- renesas,rcar-gen3-sdhi
|
||||
- renesas,rcar-gen4-sdhi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
||||
@@ -39,6 +39,7 @@ properties:
|
||||
- rockchip,rk3399-dw-mshc
|
||||
- rockchip,rk3568-dw-mshc
|
||||
- rockchip,rv1108-dw-mshc
|
||||
- rockchip,rv1126-dw-mshc
|
||||
- const: rockchip,rk3288-dw-mshc
|
||||
|
||||
reg:
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/samsung,exynos-dw-mshc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title:
|
||||
Samsung Exynos SoC specific extensions to the Synopsys Designware Mobile
|
||||
Storage Host Controller
|
||||
|
||||
maintainers:
|
||||
- Jaehoon Chung <jh80.chung@samsung.com>
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,exynos4210-dw-mshc
|
||||
- samsung,exynos4412-dw-mshc
|
||||
- samsung,exynos5250-dw-mshc
|
||||
- samsung,exynos5420-dw-mshc
|
||||
- samsung,exynos5420-dw-mshc-smu
|
||||
- samsung,exynos7-dw-mshc
|
||||
- samsung,exynos7-dw-mshc-smu
|
||||
- axis,artpec8-dw-mshc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
description:
|
||||
Handle to "biu" and "ciu" clocks for the
|
||||
bus interface unit clock and the card interface unit clock.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: biu
|
||||
- const: ciu
|
||||
|
||||
samsung,dw-mshc-ciu-div:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description:
|
||||
The divider value for the card interface unit (ciu) clock.
|
||||
|
||||
samsung,dw-mshc-ddr-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: CIU clock phase shift value for tx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
- description: CIU clock phase shift value for rx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description:
|
||||
The value of CUI clock phase shift value in transmit mode and CIU clock
|
||||
phase shift value in receive mode for double data rate mode operation.
|
||||
See also samsung,dw-mshc-hs400-timing property.
|
||||
|
||||
samsung,dw-mshc-hs400-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: CIU clock phase shift value for tx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
- description: CIU clock phase shift value for rx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description: |
|
||||
The value of CIU TX and RX clock phase shift value for HS400 mode
|
||||
operation.
|
||||
Valid values for SDR and DDR CIU clock timing::
|
||||
- valid value for tx phase shift and rx phase shift is 0 to 7.
|
||||
- when CIU clock divider value is set to 3, all possible 8 phase shift
|
||||
values can be used.
|
||||
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
||||
phase shift clocks should be 0.
|
||||
If missing, values from samsung,dw-mshc-ddr-timing property are used.
|
||||
|
||||
samsung,dw-mshc-sdr-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: CIU clock phase shift value for tx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
- description: CIU clock phase shift value for rx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description:
|
||||
The value of CIU clock phase shift value in transmit mode and CIU clock
|
||||
phase shift value in receive mode for single data rate mode operation.
|
||||
See also samsung,dw-mshc-hs400-timing property.
|
||||
|
||||
samsung,read-strobe-delay:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
RCLK (Data strobe) delay to control HS400 mode (Latency value for delay
|
||||
line in Read path). If missing, default from hardware is used.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- samsung,dw-mshc-ddr-timing
|
||||
- samsung,dw-mshc-sdr-timing
|
||||
|
||||
allOf:
|
||||
- $ref: "synopsys-dw-mshc-common.yaml#"
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos5250-dw-mshc
|
||||
- samsung,exynos5420-dw-mshc
|
||||
- samsung,exynos7-dw-mshc
|
||||
- samsung,exynos7-dw-mshc-smu
|
||||
- axis,artpec8-dw-mshc
|
||||
then:
|
||||
required:
|
||||
- samsung,dw-mshc-ciu-div
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
mmc@12220000 {
|
||||
compatible = "samsung,exynos5420-dw-mshc";
|
||||
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x12220000 0x1000>;
|
||||
clocks = <&clock CLK_MMC2>, <&clock CLK_SCLK_MMC2>;
|
||||
clock-names = "biu", "ciu";
|
||||
fifo-depth = <0x40>;
|
||||
card-detect-delay = <200>;
|
||||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <0 4>;
|
||||
samsung,dw-mshc-ddr-timing = <0 2>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_wp &sd2_bus1 &sd2_bus4>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
max-frequency = <200000000>;
|
||||
vmmc-supply = <&ldo19_reg>;
|
||||
vqmmc-supply = <&ldo13_reg>;
|
||||
sd-uhs-sdr50;
|
||||
sd-uhs-sdr104;
|
||||
sd-uhs-ddr50;
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/samsung,s3c6410-sdhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung SoC SDHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Jaehoon Chung <jh80.chung@samsung.com>
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,s3c6410-sdhci
|
||||
- samsung,exynos4210-sdhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: hsmmc
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos4210-sdhci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: hsmmc
|
||||
- const: mmc_busclk.2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos4.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
mmc@12510000 {
|
||||
compatible = "samsung,exynos4210-sdhci";
|
||||
reg = <0x12510000 0x100>;
|
||||
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>;
|
||||
clock-names = "hsmmc", "mmc_busclk.2";
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpx3 4 GPIO_ACTIVE_LOW>;
|
||||
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus4 &sdhci2_cd>;
|
||||
pinctrl-names = "default";
|
||||
vmmc-supply = <&ldo21_reg>;
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
* Samsung's SDHCI Controller device tree bindings
|
||||
|
||||
Samsung's SDHCI controller is used as a connectivity interface with external
|
||||
MMC, SD and eMMC storage mediums. This file documents differences between the
|
||||
core mmc properties described by mmc.txt and the properties used by the
|
||||
Samsung implementation of the SDHCI controller.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be one of the following
|
||||
- "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci
|
||||
controller.
|
||||
- "samsung,exynos4210-sdhci": For controllers compatible with Exynos4 sdhci
|
||||
controller.
|
||||
|
||||
Required Board Specific Properties:
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Example:
|
||||
sdhci@12530000 {
|
||||
compatible = "samsung,exynos4210-sdhci";
|
||||
reg = <0x12530000 0x100>;
|
||||
interrupts = <0 75 0>;
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpk2 2 0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>;
|
||||
};
|
||||
|
||||
Note: This example shows both SoC specific and board specific properties
|
||||
in a single device node. The properties can be actually be separated
|
||||
into SoC specific node and board specific node.
|
||||
@@ -17,6 +17,9 @@ description:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,sdhci-msm-v4
|
||||
deprecated: true
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,apq8084-sdhci
|
||||
@@ -27,6 +30,10 @@ properties:
|
||||
- qcom,msm8992-sdhci
|
||||
- qcom,msm8994-sdhci
|
||||
- qcom,msm8996-sdhci
|
||||
- qcom,msm8998-sdhci
|
||||
- const: qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,qcs404-sdhci
|
||||
- qcom,sc7180-sdhci
|
||||
- qcom,sc7280-sdhci
|
||||
@@ -38,20 +45,16 @@ properties:
|
||||
- qcom,sm6350-sdhci
|
||||
- qcom,sm8150-sdhci
|
||||
- qcom,sm8250-sdhci
|
||||
- enum:
|
||||
- qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
|
||||
- qcom,sdhci-msm-v5 # for sdcc version 5.0
|
||||
- items:
|
||||
- const: qcom,sdhci-msm-v4 # Deprecated (only for backward compatibility)
|
||||
# for sdcc versions less than 5.0
|
||||
- qcom,sm8450-sdhci
|
||||
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Host controller register map
|
||||
- description: SD Core register map
|
||||
- description: CQE register map
|
||||
- description: Inline Crypto Engine register map
|
||||
maxItems: 4
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
clocks:
|
||||
minItems: 3
|
||||
@@ -93,6 +96,9 @@ properties:
|
||||
description:
|
||||
Should specify pin control groups used for this controller.
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
qcom,ddr-config:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: platform specific settings for DDR_CONFIG reg.
|
||||
@@ -121,6 +127,16 @@ properties:
|
||||
description: A phandle to sdhci power domain node
|
||||
maxItems: 1
|
||||
|
||||
mmc-ddr-1_8v: true
|
||||
|
||||
mmc-hs200-1_8v: true
|
||||
|
||||
mmc-hs400-1_8v: true
|
||||
|
||||
bus-width: true
|
||||
|
||||
max-frequency: true
|
||||
|
||||
patternProperties:
|
||||
'^opp-table(-[a-z0-9]+)?$':
|
||||
if:
|
||||
@@ -140,7 +156,47 @@ required:
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
additionalProperties: true
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sdhci-msm-v4
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
items:
|
||||
- description: Host controller register map
|
||||
- description: SD Core register map
|
||||
- description: CQE register map
|
||||
- description: Inline Crypto Engine register map
|
||||
reg-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: hc
|
||||
- const: core
|
||||
- const: cqhci
|
||||
- const: ice
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Host controller register map
|
||||
- description: CQE register map
|
||||
- description: Inline Crypto Engine register map
|
||||
reg-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: hc
|
||||
- const: cqhci
|
||||
- const: ice
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
@@ -149,7 +205,7 @@ examples:
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
sdhc_2: sdhci@8804000 {
|
||||
sdhc_2: mmc@8804000 {
|
||||
compatible = "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5";
|
||||
reg = <0 0x08804000 0 0x1000>;
|
||||
|
||||
|
||||
@@ -162,16 +162,6 @@ Currently, the types available are:
|
||||
|
||||
- The device is able to do memory to memory copies
|
||||
|
||||
- - DMA_MEMCPY_SG
|
||||
|
||||
- The device supports memory to memory scatter-gather transfers.
|
||||
|
||||
- Even though a plain memcpy can look like a particular case of a
|
||||
scatter-gather transfer, with a single chunk to copy, it's a distinct
|
||||
transaction type in the mem2mem transfer case. This is because some very
|
||||
simple devices might be able to do contiguous single-chunk memory copies,
|
||||
but have no support for more complex SG transfers.
|
||||
|
||||
- No matter what the overall size of the combined chunks for source and
|
||||
destination is, only as many bytes as the smallest of the two will be
|
||||
transmitted. That means the number and size of the scatter-gather buffers in
|
||||
|
||||
@@ -1851,6 +1851,7 @@ T: git https://github.com/AsahiLinux/linux.git
|
||||
F: Documentation/devicetree/bindings/arm/apple.yaml
|
||||
F: Documentation/devicetree/bindings/arm/apple/*
|
||||
F: Documentation/devicetree/bindings/clock/apple,nco.yaml
|
||||
F: Documentation/devicetree/bindings/dma/apple,admac.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/apple,*
|
||||
F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
|
||||
@@ -1864,6 +1865,7 @@ F: Documentation/devicetree/bindings/power/apple*
|
||||
F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
|
||||
F: arch/arm64/boot/dts/apple/
|
||||
F: drivers/clk/clk-apple-nco.c
|
||||
F: drivers/dma/apple-admac.c
|
||||
F: drivers/i2c/busses/i2c-pasemi-core.c
|
||||
F: drivers/i2c/busses/i2c-pasemi-platform.c
|
||||
F: drivers/iommu/apple-dart.c
|
||||
@@ -6102,6 +6104,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git
|
||||
F: Documentation/devicetree/bindings/dma/
|
||||
F: Documentation/driver-api/dmaengine/
|
||||
F: drivers/dma/
|
||||
F: include/dt-bindings/dma/
|
||||
F: include/linux/dma/
|
||||
F: include/linux/dmaengine.h
|
||||
F: include/linux/of_dma.h
|
||||
@@ -10195,7 +10198,8 @@ S: Supported
|
||||
Q: https://patchwork.kernel.org/project/linux-dmaengine/list/
|
||||
F: drivers/dma/ioat*
|
||||
|
||||
INTEL IADX DRIVER
|
||||
INTEL IDXD DRIVER
|
||||
M: Fenghua Yu <fenghua.yu@intel.com>
|
||||
M: Dave Jiang <dave.jiang@intel.com>
|
||||
L: dmaengine@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
@@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA
|
||||
help
|
||||
Enable support for the AMCC PPC440SPe RAID engines.
|
||||
|
||||
config APPLE_ADMAC
|
||||
tristate "Apple ADMAC support"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
select DMA_ENGINE
|
||||
default ARCH_APPLE
|
||||
help
|
||||
Enable support for Audio DMA Controller found on Apple Silicon SoCs.
|
||||
|
||||
config AT_HDMAC
|
||||
tristate "Atmel AHB DMA support"
|
||||
depends on ARCH_AT91
|
||||
|
||||
@@ -17,6 +17,7 @@ obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o
|
||||
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
|
||||
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
|
||||
obj-$(CONFIG_AMD_PTDMA) += ptdma/
|
||||
obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o
|
||||
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
|
||||
obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
|
||||
obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
|
||||
|
||||
@@ -749,7 +749,7 @@ static irqreturn_t msgdma_irq_handler(int irq, void *data)
|
||||
}
|
||||
|
||||
/**
|
||||
* msgdma_chan_remove - Channel remove function
|
||||
* msgdma_dev_remove() - Device remove function
|
||||
* @mdev: Pointer to the Altera mSGDMA device structure
|
||||
*/
|
||||
static void msgdma_dev_remove(struct msgdma_device *mdev)
|
||||
@@ -918,7 +918,7 @@ fail:
|
||||
}
|
||||
|
||||
/**
|
||||
* msgdma_dma_remove - Driver remove function
|
||||
* msgdma_remove() - Driver remove function
|
||||
* @pdev: Pointer to the platform_device structure
|
||||
*
|
||||
* Return: Always '0'
|
||||
|
||||
@@ -231,7 +231,7 @@ enum pl08x_dma_chan_state {
|
||||
|
||||
/**
|
||||
* struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
|
||||
* @vc: wrappped virtual channel
|
||||
* @vc: wrapped virtual channel
|
||||
* @phychan: the physical channel utilized by this channel, if there is one
|
||||
* @name: name of channel
|
||||
* @cd: channel platform data
|
||||
|
||||
818
drivers/dma/apple-admac.c
Normal file
818
drivers/dma/apple-admac.c
Normal file
@@ -0,0 +1,818 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for Audio DMA Controller (ADMAC) on t8103 (M1) and other Apple chips
|
||||
*
|
||||
* Copyright (C) The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
|
||||
#define NCHANNELS_MAX 64
|
||||
#define IRQ_NOUTPUTS 4
|
||||
|
||||
#define RING_WRITE_SLOT GENMASK(1, 0)
|
||||
#define RING_READ_SLOT GENMASK(5, 4)
|
||||
#define RING_FULL BIT(9)
|
||||
#define RING_EMPTY BIT(8)
|
||||
#define RING_ERR BIT(10)
|
||||
|
||||
#define STATUS_DESC_DONE BIT(0)
|
||||
#define STATUS_ERR BIT(6)
|
||||
|
||||
#define FLAG_DESC_NOTIFY BIT(16)
|
||||
|
||||
#define REG_TX_START 0x0000
|
||||
#define REG_TX_STOP 0x0004
|
||||
#define REG_RX_START 0x0008
|
||||
#define REG_RX_STOP 0x000c
|
||||
|
||||
#define REG_CHAN_CTL(ch) (0x8000 + (ch) * 0x200)
|
||||
#define REG_CHAN_CTL_RST_RINGS BIT(0)
|
||||
|
||||
#define REG_DESC_RING(ch) (0x8070 + (ch) * 0x200)
|
||||
#define REG_REPORT_RING(ch) (0x8074 + (ch) * 0x200)
|
||||
|
||||
#define REG_RESIDUE(ch) (0x8064 + (ch) * 0x200)
|
||||
|
||||
#define REG_BUS_WIDTH(ch) (0x8040 + (ch) * 0x200)
|
||||
|
||||
#define BUS_WIDTH_8BIT 0x00
|
||||
#define BUS_WIDTH_16BIT 0x01
|
||||
#define BUS_WIDTH_32BIT 0x02
|
||||
#define BUS_WIDTH_FRAME_2_WORDS 0x10
|
||||
#define BUS_WIDTH_FRAME_4_WORDS 0x20
|
||||
|
||||
#define CHAN_BUFSIZE 0x8000
|
||||
|
||||
#define REG_CHAN_FIFOCTL(ch) (0x8054 + (ch) * 0x200)
|
||||
#define CHAN_FIFOCTL_LIMIT GENMASK(31, 16)
|
||||
#define CHAN_FIFOCTL_THRESHOLD GENMASK(15, 0)
|
||||
|
||||
#define REG_DESC_WRITE(ch) (0x10000 + ((ch) / 2) * 0x4 + ((ch) & 1) * 0x4000)
|
||||
#define REG_REPORT_READ(ch) (0x10100 + ((ch) / 2) * 0x4 + ((ch) & 1) * 0x4000)
|
||||
|
||||
#define REG_TX_INTSTATE(idx) (0x0030 + (idx) * 4)
|
||||
#define REG_RX_INTSTATE(idx) (0x0040 + (idx) * 4)
|
||||
#define REG_CHAN_INTSTATUS(ch, idx) (0x8010 + (ch) * 0x200 + (idx) * 4)
|
||||
#define REG_CHAN_INTMASK(ch, idx) (0x8020 + (ch) * 0x200 + (idx) * 4)
|
||||
|
||||
struct admac_data;
|
||||
struct admac_tx;
|
||||
|
||||
struct admac_chan {
|
||||
unsigned int no;
|
||||
struct admac_data *host;
|
||||
struct dma_chan chan;
|
||||
struct tasklet_struct tasklet;
|
||||
|
||||
spinlock_t lock;
|
||||
struct admac_tx *current_tx;
|
||||
int nperiod_acks;
|
||||
|
||||
/*
|
||||
* We maintain a 'submitted' and 'issued' list mainly for interface
|
||||
* correctness. Typical use of the driver (per channel) will be
|
||||
* prepping, submitting and issuing a single cyclic transaction which
|
||||
* will stay current until terminate_all is called.
|
||||
*/
|
||||
struct list_head submitted;
|
||||
struct list_head issued;
|
||||
|
||||
struct list_head to_free;
|
||||
};
|
||||
|
||||
struct admac_data {
|
||||
struct dma_device dma;
|
||||
struct device *dev;
|
||||
__iomem void *base;
|
||||
|
||||
int irq_index;
|
||||
int nchannels;
|
||||
struct admac_chan channels[];
|
||||
};
|
||||
|
||||
struct admac_tx {
|
||||
struct dma_async_tx_descriptor tx;
|
||||
bool cyclic;
|
||||
dma_addr_t buf_addr;
|
||||
dma_addr_t buf_end;
|
||||
size_t buf_len;
|
||||
size_t period_len;
|
||||
|
||||
size_t submitted_pos;
|
||||
size_t reclaimed_pos;
|
||||
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static void admac_modify(struct admac_data *ad, int reg, u32 mask, u32 val)
|
||||
{
|
||||
void __iomem *addr = ad->base + reg;
|
||||
u32 curr = readl_relaxed(addr);
|
||||
|
||||
writel_relaxed((curr & ~mask) | (val & mask), addr);
|
||||
}
|
||||
|
||||
static struct admac_chan *to_admac_chan(struct dma_chan *chan)
|
||||
{
|
||||
return container_of(chan, struct admac_chan, chan);
|
||||
}
|
||||
|
||||
static struct admac_tx *to_admac_tx(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
return container_of(tx, struct admac_tx, tx);
|
||||
}
|
||||
|
||||
static enum dma_transfer_direction admac_chan_direction(int channo)
|
||||
{
|
||||
/* Channel directions are hardwired */
|
||||
return (channo & 1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
|
||||
}
|
||||
|
||||
static dma_cookie_t admac_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
struct admac_tx *adtx = to_admac_tx(tx);
|
||||
struct admac_chan *adchan = to_admac_chan(tx->chan);
|
||||
unsigned long flags;
|
||||
dma_cookie_t cookie;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
cookie = dma_cookie_assign(tx);
|
||||
list_add_tail(&adtx->node, &adchan->submitted);
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static int admac_desc_free(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
kfree(to_admac_tx(tx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *admac_prep_dma_cyclic(
|
||||
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
|
||||
size_t period_len, enum dma_transfer_direction direction,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct admac_chan *adchan = container_of(chan, struct admac_chan, chan);
|
||||
struct admac_tx *adtx;
|
||||
|
||||
if (direction != admac_chan_direction(adchan->no))
|
||||
return NULL;
|
||||
|
||||
adtx = kzalloc(sizeof(*adtx), GFP_NOWAIT);
|
||||
if (!adtx)
|
||||
return NULL;
|
||||
|
||||
adtx->cyclic = true;
|
||||
|
||||
adtx->buf_addr = buf_addr;
|
||||
adtx->buf_len = buf_len;
|
||||
adtx->buf_end = buf_addr + buf_len;
|
||||
adtx->period_len = period_len;
|
||||
|
||||
adtx->submitted_pos = 0;
|
||||
adtx->reclaimed_pos = 0;
|
||||
|
||||
dma_async_tx_descriptor_init(&adtx->tx, chan);
|
||||
adtx->tx.tx_submit = admac_tx_submit;
|
||||
adtx->tx.desc_free = admac_desc_free;
|
||||
|
||||
return &adtx->tx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write one hardware descriptor for a dmaengine cyclic transaction.
|
||||
*/
|
||||
static void admac_cyclic_write_one_desc(struct admac_data *ad, int channo,
|
||||
struct admac_tx *tx)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
|
||||
addr = tx->buf_addr + (tx->submitted_pos % tx->buf_len);
|
||||
|
||||
/* If happens means we have buggy code */
|
||||
WARN_ON_ONCE(addr + tx->period_len > tx->buf_end);
|
||||
|
||||
dev_dbg(ad->dev, "ch%d descriptor: addr=0x%pad len=0x%zx flags=0x%lx\n",
|
||||
channo, &addr, tx->period_len, FLAG_DESC_NOTIFY);
|
||||
|
||||
writel_relaxed(lower_32_bits(addr), ad->base + REG_DESC_WRITE(channo));
|
||||
writel_relaxed(upper_32_bits(addr), ad->base + REG_DESC_WRITE(channo));
|
||||
writel_relaxed(tx->period_len, ad->base + REG_DESC_WRITE(channo));
|
||||
writel_relaxed(FLAG_DESC_NOTIFY, ad->base + REG_DESC_WRITE(channo));
|
||||
|
||||
tx->submitted_pos += tx->period_len;
|
||||
tx->submitted_pos %= 2 * tx->buf_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write all the hardware descriptors for a dmaengine cyclic
|
||||
* transaction there is space for.
|
||||
*/
|
||||
static void admac_cyclic_write_desc(struct admac_data *ad, int channo,
|
||||
struct admac_tx *tx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (readl_relaxed(ad->base + REG_DESC_RING(channo)) & RING_FULL)
|
||||
break;
|
||||
admac_cyclic_write_one_desc(ad, channo, tx);
|
||||
}
|
||||
}
|
||||
|
||||
static int admac_ring_noccupied_slots(int ringval)
|
||||
{
|
||||
int wrslot = FIELD_GET(RING_WRITE_SLOT, ringval);
|
||||
int rdslot = FIELD_GET(RING_READ_SLOT, ringval);
|
||||
|
||||
if (wrslot != rdslot) {
|
||||
return (wrslot + 4 - rdslot) % 4;
|
||||
} else {
|
||||
WARN_ON((ringval & (RING_FULL | RING_EMPTY)) == 0);
|
||||
|
||||
if (ringval & RING_FULL)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from hardware the residue of a cyclic dmaengine transaction.
|
||||
*/
|
||||
static u32 admac_cyclic_read_residue(struct admac_data *ad, int channo,
|
||||
struct admac_tx *adtx)
|
||||
{
|
||||
u32 ring1, ring2;
|
||||
u32 residue1, residue2;
|
||||
int nreports;
|
||||
size_t pos;
|
||||
|
||||
ring1 = readl_relaxed(ad->base + REG_REPORT_RING(channo));
|
||||
residue1 = readl_relaxed(ad->base + REG_RESIDUE(channo));
|
||||
ring2 = readl_relaxed(ad->base + REG_REPORT_RING(channo));
|
||||
residue2 = readl_relaxed(ad->base + REG_RESIDUE(channo));
|
||||
|
||||
if (residue2 > residue1) {
|
||||
/*
|
||||
* Controller must have loaded next descriptor between
|
||||
* the two residue reads
|
||||
*/
|
||||
nreports = admac_ring_noccupied_slots(ring1) + 1;
|
||||
} else {
|
||||
/* No descriptor load between the two reads, ring2 is safe to use */
|
||||
nreports = admac_ring_noccupied_slots(ring2);
|
||||
}
|
||||
|
||||
pos = adtx->reclaimed_pos + adtx->period_len * (nreports + 1) - residue2;
|
||||
|
||||
return adtx->buf_len - pos % adtx->buf_len;
|
||||
}
|
||||
|
||||
static enum dma_status admac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
||||
struct dma_tx_state *txstate)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_data *ad = adchan->host;
|
||||
struct admac_tx *adtx;
|
||||
|
||||
enum dma_status ret;
|
||||
size_t residue;
|
||||
unsigned long flags;
|
||||
|
||||
ret = dma_cookie_status(chan, cookie, txstate);
|
||||
if (ret == DMA_COMPLETE || !txstate)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
adtx = adchan->current_tx;
|
||||
|
||||
if (adtx && adtx->tx.cookie == cookie) {
|
||||
ret = DMA_IN_PROGRESS;
|
||||
residue = admac_cyclic_read_residue(ad, adchan->no, adtx);
|
||||
} else {
|
||||
ret = DMA_IN_PROGRESS;
|
||||
residue = 0;
|
||||
list_for_each_entry(adtx, &adchan->issued, node) {
|
||||
if (adtx->tx.cookie == cookie) {
|
||||
residue = adtx->buf_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
dma_set_residue(txstate, residue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void admac_start_chan(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
u32 startbit = 1 << (adchan->no / 2);
|
||||
|
||||
writel_relaxed(STATUS_DESC_DONE | STATUS_ERR,
|
||||
ad->base + REG_CHAN_INTSTATUS(adchan->no, ad->irq_index));
|
||||
writel_relaxed(STATUS_DESC_DONE | STATUS_ERR,
|
||||
ad->base + REG_CHAN_INTMASK(adchan->no, ad->irq_index));
|
||||
|
||||
switch (admac_chan_direction(adchan->no)) {
|
||||
case DMA_MEM_TO_DEV:
|
||||
writel_relaxed(startbit, ad->base + REG_TX_START);
|
||||
break;
|
||||
case DMA_DEV_TO_MEM:
|
||||
writel_relaxed(startbit, ad->base + REG_RX_START);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev_dbg(adchan->host->dev, "ch%d start\n", adchan->no);
|
||||
}
|
||||
|
||||
static void admac_stop_chan(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
u32 stopbit = 1 << (adchan->no / 2);
|
||||
|
||||
switch (admac_chan_direction(adchan->no)) {
|
||||
case DMA_MEM_TO_DEV:
|
||||
writel_relaxed(stopbit, ad->base + REG_TX_STOP);
|
||||
break;
|
||||
case DMA_DEV_TO_MEM:
|
||||
writel_relaxed(stopbit, ad->base + REG_RX_STOP);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev_dbg(adchan->host->dev, "ch%d stop\n", adchan->no);
|
||||
}
|
||||
|
||||
static void admac_reset_rings(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
|
||||
writel_relaxed(REG_CHAN_CTL_RST_RINGS,
|
||||
ad->base + REG_CHAN_CTL(adchan->no));
|
||||
writel_relaxed(0, ad->base + REG_CHAN_CTL(adchan->no));
|
||||
}
|
||||
|
||||
static void admac_start_current_tx(struct admac_chan *adchan)
|
||||
{
|
||||
struct admac_data *ad = adchan->host;
|
||||
int ch = adchan->no;
|
||||
|
||||
admac_reset_rings(adchan);
|
||||
writel_relaxed(0, ad->base + REG_CHAN_CTL(ch));
|
||||
|
||||
admac_cyclic_write_one_desc(ad, ch, adchan->current_tx);
|
||||
admac_start_chan(adchan);
|
||||
admac_cyclic_write_desc(ad, ch, adchan->current_tx);
|
||||
}
|
||||
|
||||
static void admac_issue_pending(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_tx *tx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
list_splice_tail_init(&adchan->submitted, &adchan->issued);
|
||||
if (!list_empty(&adchan->issued) && !adchan->current_tx) {
|
||||
tx = list_first_entry(&adchan->issued, struct admac_tx, node);
|
||||
list_del(&tx->node);
|
||||
|
||||
adchan->current_tx = tx;
|
||||
adchan->nperiod_acks = 0;
|
||||
admac_start_current_tx(adchan);
|
||||
}
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
}
|
||||
|
||||
static int admac_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
|
||||
admac_stop_chan(adchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_resume(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
|
||||
admac_start_chan(adchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_terminate_all(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
admac_stop_chan(adchan);
|
||||
admac_reset_rings(adchan);
|
||||
|
||||
adchan->current_tx = NULL;
|
||||
/*
|
||||
* Descriptors can only be freed after the tasklet
|
||||
* has been killed (in admac_synchronize).
|
||||
*/
|
||||
list_splice_tail_init(&adchan->submitted, &adchan->to_free);
|
||||
list_splice_tail_init(&adchan->issued, &adchan->to_free);
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void admac_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_tx *adtx, *_adtx;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(head);
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
list_splice_tail_init(&adchan->to_free, &head);
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
|
||||
tasklet_kill(&adchan->tasklet);
|
||||
|
||||
list_for_each_entry_safe(adtx, _adtx, &head, node) {
|
||||
list_del(&adtx->node);
|
||||
admac_desc_free(&adtx->tx);
|
||||
}
|
||||
}
|
||||
|
||||
static int admac_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
|
||||
dma_cookie_init(&adchan->chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void admac_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
admac_terminate_all(chan);
|
||||
admac_synchronize(chan);
|
||||
}
|
||||
|
||||
static struct dma_chan *admac_dma_of_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
struct admac_data *ad = (struct admac_data *) ofdma->of_dma_data;
|
||||
unsigned int index;
|
||||
|
||||
if (dma_spec->args_count != 1)
|
||||
return NULL;
|
||||
|
||||
index = dma_spec->args[0];
|
||||
|
||||
if (index >= ad->nchannels) {
|
||||
dev_err(ad->dev, "channel index %u out of bounds\n", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &ad->channels[index].chan;
|
||||
}
|
||||
|
||||
static int admac_drain_reports(struct admac_data *ad, int channo)
|
||||
{
|
||||
int count;
|
||||
|
||||
for (count = 0; count < 4; count++) {
|
||||
u32 countval_hi, countval_lo, unk1, flags;
|
||||
|
||||
if (readl_relaxed(ad->base + REG_REPORT_RING(channo)) & RING_EMPTY)
|
||||
break;
|
||||
|
||||
countval_lo = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
countval_hi = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
unk1 = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
flags = readl_relaxed(ad->base + REG_REPORT_READ(channo));
|
||||
|
||||
dev_dbg(ad->dev, "ch%d report: countval=0x%llx unk1=0x%x flags=0x%x\n",
|
||||
channo, ((u64) countval_hi) << 32 | countval_lo, unk1, flags);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void admac_handle_status_err(struct admac_data *ad, int channo)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if (readl_relaxed(ad->base + REG_DESC_RING(channo)) & RING_ERR) {
|
||||
writel_relaxed(RING_ERR, ad->base + REG_DESC_RING(channo));
|
||||
dev_err_ratelimited(ad->dev, "ch%d descriptor ring error\n", channo);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (readl_relaxed(ad->base + REG_REPORT_RING(channo)) & RING_ERR) {
|
||||
writel_relaxed(RING_ERR, ad->base + REG_REPORT_RING(channo));
|
||||
dev_err_ratelimited(ad->dev, "ch%d report ring error\n", channo);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (unlikely(!handled)) {
|
||||
dev_err(ad->dev, "ch%d unknown error, masking errors as cause of IRQs\n", channo);
|
||||
admac_modify(ad, REG_CHAN_INTMASK(channo, ad->irq_index),
|
||||
STATUS_ERR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void admac_handle_status_desc_done(struct admac_data *ad, int channo)
|
||||
{
|
||||
struct admac_chan *adchan = &ad->channels[channo];
|
||||
unsigned long flags;
|
||||
int nreports;
|
||||
|
||||
writel_relaxed(STATUS_DESC_DONE,
|
||||
ad->base + REG_CHAN_INTSTATUS(channo, ad->irq_index));
|
||||
|
||||
spin_lock_irqsave(&adchan->lock, flags);
|
||||
nreports = admac_drain_reports(ad, channo);
|
||||
|
||||
if (adchan->current_tx) {
|
||||
struct admac_tx *tx = adchan->current_tx;
|
||||
|
||||
adchan->nperiod_acks += nreports;
|
||||
tx->reclaimed_pos += nreports * tx->period_len;
|
||||
tx->reclaimed_pos %= 2 * tx->buf_len;
|
||||
|
||||
admac_cyclic_write_desc(ad, channo, tx);
|
||||
tasklet_schedule(&adchan->tasklet);
|
||||
}
|
||||
spin_unlock_irqrestore(&adchan->lock, flags);
|
||||
}
|
||||
|
||||
static void admac_handle_chan_int(struct admac_data *ad, int no)
|
||||
{
|
||||
u32 cause = readl_relaxed(ad->base + REG_CHAN_INTSTATUS(no, ad->irq_index));
|
||||
|
||||
if (cause & STATUS_ERR)
|
||||
admac_handle_status_err(ad, no);
|
||||
|
||||
if (cause & STATUS_DESC_DONE)
|
||||
admac_handle_status_desc_done(ad, no);
|
||||
}
|
||||
|
||||
static irqreturn_t admac_interrupt(int irq, void *devid)
|
||||
{
|
||||
struct admac_data *ad = devid;
|
||||
u32 rx_intstate, tx_intstate;
|
||||
int i;
|
||||
|
||||
rx_intstate = readl_relaxed(ad->base + REG_RX_INTSTATE(ad->irq_index));
|
||||
tx_intstate = readl_relaxed(ad->base + REG_TX_INTSTATE(ad->irq_index));
|
||||
|
||||
if (!tx_intstate && !rx_intstate)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ad->nchannels; i += 2) {
|
||||
if (tx_intstate & 1)
|
||||
admac_handle_chan_int(ad, i);
|
||||
tx_intstate >>= 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < ad->nchannels; i += 2) {
|
||||
if (rx_intstate & 1)
|
||||
admac_handle_chan_int(ad, i);
|
||||
rx_intstate >>= 1;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void admac_chan_tasklet(struct tasklet_struct *t)
|
||||
{
|
||||
struct admac_chan *adchan = from_tasklet(adchan, t, tasklet);
|
||||
struct admac_tx *adtx;
|
||||
struct dmaengine_desc_callback cb;
|
||||
struct dmaengine_result tx_result;
|
||||
int nacks;
|
||||
|
||||
spin_lock_irq(&adchan->lock);
|
||||
adtx = adchan->current_tx;
|
||||
nacks = adchan->nperiod_acks;
|
||||
adchan->nperiod_acks = 0;
|
||||
spin_unlock_irq(&adchan->lock);
|
||||
|
||||
if (!adtx || !nacks)
|
||||
return;
|
||||
|
||||
tx_result.result = DMA_TRANS_NOERROR;
|
||||
tx_result.residue = 0;
|
||||
|
||||
dmaengine_desc_get_callback(&adtx->tx, &cb);
|
||||
while (nacks--)
|
||||
dmaengine_desc_callback_invoke(&cb, &tx_result);
|
||||
}
|
||||
|
||||
static int admac_device_config(struct dma_chan *chan,
|
||||
struct dma_slave_config *config)
|
||||
{
|
||||
struct admac_chan *adchan = to_admac_chan(chan);
|
||||
struct admac_data *ad = adchan->host;
|
||||
bool is_tx = admac_chan_direction(adchan->no) == DMA_MEM_TO_DEV;
|
||||
int wordsize = 0;
|
||||
u32 bus_width = 0;
|
||||
|
||||
switch (is_tx ? config->dst_addr_width : config->src_addr_width) {
|
||||
case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
||||
wordsize = 1;
|
||||
bus_width |= BUS_WIDTH_8BIT;
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
||||
wordsize = 2;
|
||||
bus_width |= BUS_WIDTH_16BIT;
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
||||
wordsize = 4;
|
||||
bus_width |= BUS_WIDTH_32BIT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We take port_window_size to be the number of words in a frame.
|
||||
*
|
||||
* The controller has some means of out-of-band signalling, to the peripheral,
|
||||
* of words position in a frame. That's where the importance of this control
|
||||
* comes from.
|
||||
*/
|
||||
switch (is_tx ? config->dst_port_window_size : config->src_port_window_size) {
|
||||
case 0 ... 1:
|
||||
break;
|
||||
case 2:
|
||||
bus_width |= BUS_WIDTH_FRAME_2_WORDS;
|
||||
break;
|
||||
case 4:
|
||||
bus_width |= BUS_WIDTH_FRAME_4_WORDS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel_relaxed(bus_width, ad->base + REG_BUS_WIDTH(adchan->no));
|
||||
|
||||
/*
|
||||
* By FIFOCTL_LIMIT we seem to set the maximal number of bytes allowed to be
|
||||
* held in controller's per-channel FIFO. Transfers seem to be triggered
|
||||
* around the time FIFO occupancy touches FIFOCTL_THRESHOLD.
|
||||
*
|
||||
* The numbers we set are more or less arbitrary.
|
||||
*/
|
||||
writel_relaxed(FIELD_PREP(CHAN_FIFOCTL_LIMIT, 0x30 * wordsize)
|
||||
| FIELD_PREP(CHAN_FIFOCTL_THRESHOLD, 0x18 * wordsize),
|
||||
ad->base + REG_CHAN_FIFOCTL(adchan->no));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct admac_data *ad;
|
||||
struct dma_device *dma;
|
||||
int nchannels;
|
||||
int err, irq, i;
|
||||
|
||||
err = of_property_read_u32(np, "dma-channels", &nchannels);
|
||||
if (err || nchannels > NCHANNELS_MAX) {
|
||||
dev_err(&pdev->dev, "missing or invalid dma-channels property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ad = devm_kzalloc(&pdev->dev, struct_size(ad, channels, nchannels), GFP_KERNEL);
|
||||
if (!ad)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ad);
|
||||
ad->dev = &pdev->dev;
|
||||
ad->nchannels = nchannels;
|
||||
|
||||
/*
|
||||
* The controller has 4 IRQ outputs. Try them all until
|
||||
* we find one we can use.
|
||||
*/
|
||||
for (i = 0; i < IRQ_NOUTPUTS; i++) {
|
||||
irq = platform_get_irq_optional(pdev, i);
|
||||
if (irq >= 0) {
|
||||
ad->irq_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq < 0)
|
||||
return dev_err_probe(&pdev->dev, irq, "no usable interrupt\n");
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, admac_interrupt,
|
||||
0, dev_name(&pdev->dev), ad);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err,
|
||||
"unable to register interrupt\n");
|
||||
|
||||
ad->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ad->base))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(ad->base),
|
||||
"unable to obtain MMIO resource\n");
|
||||
|
||||
dma = &ad->dma;
|
||||
|
||||
dma_cap_set(DMA_PRIVATE, dma->cap_mask);
|
||||
dma_cap_set(DMA_CYCLIC, dma->cap_mask);
|
||||
|
||||
dma->dev = &pdev->dev;
|
||||
dma->device_alloc_chan_resources = admac_alloc_chan_resources;
|
||||
dma->device_free_chan_resources = admac_free_chan_resources;
|
||||
dma->device_tx_status = admac_tx_status;
|
||||
dma->device_issue_pending = admac_issue_pending;
|
||||
dma->device_terminate_all = admac_terminate_all;
|
||||
dma->device_synchronize = admac_synchronize;
|
||||
dma->device_prep_dma_cyclic = admac_prep_dma_cyclic;
|
||||
dma->device_config = admac_device_config;
|
||||
dma->device_pause = admac_pause;
|
||||
dma->device_resume = admac_resume;
|
||||
|
||||
dma->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
|
||||
dma->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
|
||||
|
||||
INIT_LIST_HEAD(&dma->channels);
|
||||
for (i = 0; i < nchannels; i++) {
|
||||
struct admac_chan *adchan = &ad->channels[i];
|
||||
|
||||
adchan->host = ad;
|
||||
adchan->no = i;
|
||||
adchan->chan.device = &ad->dma;
|
||||
spin_lock_init(&adchan->lock);
|
||||
INIT_LIST_HEAD(&adchan->submitted);
|
||||
INIT_LIST_HEAD(&adchan->issued);
|
||||
INIT_LIST_HEAD(&adchan->to_free);
|
||||
list_add_tail(&adchan->chan.device_node, &dma->channels);
|
||||
tasklet_setup(&adchan->tasklet, admac_chan_tasklet);
|
||||
}
|
||||
|
||||
err = dma_async_device_register(&ad->dma);
|
||||
if (err)
|
||||
return dev_err_probe(&pdev->dev, err, "failed to register DMA device\n");
|
||||
|
||||
err = of_dma_controller_register(pdev->dev.of_node, admac_dma_of_xlate, ad);
|
||||
if (err) {
|
||||
dma_async_device_unregister(&ad->dma);
|
||||
return dev_err_probe(&pdev->dev, err, "failed to register with OF\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int admac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct admac_data *ad = platform_get_drvdata(pdev);
|
||||
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
dma_async_device_unregister(&ad->dma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id admac_of_match[] = {
|
||||
{ .compatible = "apple,admac", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, admac_of_match);
|
||||
|
||||
static struct platform_driver apple_admac_driver = {
|
||||
.driver = {
|
||||
.name = "apple-admac",
|
||||
.of_match_table = admac_of_match,
|
||||
},
|
||||
.probe = admac_probe,
|
||||
.remove = admac_remove,
|
||||
};
|
||||
module_platform_driver(apple_admac_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
|
||||
MODULE_DESCRIPTION("Driver for Audio DMA Controller (ADMAC) on Apple SoCs");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -649,7 +649,7 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
|
||||
}
|
||||
|
||||
/*
|
||||
* Only check that maxburst and addr width values are supported by the
|
||||
* Only check that maxburst and addr width values are supported by
|
||||
* the controller but not that the configuration is good to perform the
|
||||
* transfer since we don't know the direction at this stage.
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -55,6 +56,9 @@
|
||||
#define AXI_DMAC_DMA_DST_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_TYPE_MSK, x)
|
||||
#define AXI_DMAC_DMA_DST_WIDTH_MSK GENMASK(3, 0)
|
||||
#define AXI_DMAC_DMA_DST_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_WIDTH_MSK, x)
|
||||
#define AXI_DMAC_REG_COHERENCY_DESC 0x14
|
||||
#define AXI_DMAC_DST_COHERENT_MSK BIT(0)
|
||||
#define AXI_DMAC_DST_COHERENT_GET(x) FIELD_GET(AXI_DMAC_DST_COHERENT_MSK, x)
|
||||
|
||||
#define AXI_DMAC_REG_IRQ_MASK 0x80
|
||||
#define AXI_DMAC_REG_IRQ_PENDING 0x84
|
||||
@@ -979,6 +983,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
|
||||
|
||||
axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x00);
|
||||
|
||||
if (of_dma_is_coherent(pdev->dev.of_node)) {
|
||||
ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC);
|
||||
|
||||
if (version < ADI_AXI_PCORE_VER(4, 4, 'a') ||
|
||||
!AXI_DMAC_DST_COHERENT_GET(ret)) {
|
||||
dev_err(dmac->dma_dev.dev,
|
||||
"Coherent DMA not supported in hardware");
|
||||
ret = -EINVAL;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dma_async_device_register(dma_dev);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
@@ -388,7 +388,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg(
|
||||
|
||||
if (i != (sg_len - 1) &&
|
||||
!(jzdma->soc_data->flags & JZ_SOC_DATA_BREAK_LINKS)) {
|
||||
/* Automatically proceeed to the next descriptor. */
|
||||
/* Automatically proceed to the next descriptor. */
|
||||
desc->desc[i].dcm |= JZ_DMA_DCM_LINK;
|
||||
|
||||
/*
|
||||
|
||||
@@ -1153,13 +1153,6 @@ int dma_async_device_register(struct dma_device *device)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dma_has_cap(DMA_MEMCPY_SG, device->cap_mask) && !device->device_prep_dma_memcpy_sg) {
|
||||
dev_err(device->dev,
|
||||
"Device claims capability %s, but op is not defined\n",
|
||||
"DMA_MEMCPY_SG");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dma_has_cap(DMA_XOR, device->cap_mask) && !device->device_prep_dma_xor) {
|
||||
dev_err(device->dev,
|
||||
"Device claims capability %s, but op is not defined\n",
|
||||
|
||||
@@ -22,51 +22,50 @@
|
||||
#include <linux/wait.h>
|
||||
|
||||
static unsigned int test_buf_size = 16384;
|
||||
module_param(test_buf_size, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(test_buf_size, uint, 0644);
|
||||
MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
|
||||
|
||||
static char test_device[32];
|
||||
module_param_string(device, test_device, sizeof(test_device),
|
||||
S_IRUGO | S_IWUSR);
|
||||
module_param_string(device, test_device, sizeof(test_device), 0644);
|
||||
MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)");
|
||||
|
||||
static unsigned int threads_per_chan = 1;
|
||||
module_param(threads_per_chan, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(threads_per_chan, uint, 0644);
|
||||
MODULE_PARM_DESC(threads_per_chan,
|
||||
"Number of threads to start per channel (default: 1)");
|
||||
|
||||
static unsigned int max_channels;
|
||||
module_param(max_channels, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(max_channels, uint, 0644);
|
||||
MODULE_PARM_DESC(max_channels,
|
||||
"Maximum number of channels to use (default: all)");
|
||||
|
||||
static unsigned int iterations;
|
||||
module_param(iterations, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(iterations, uint, 0644);
|
||||
MODULE_PARM_DESC(iterations,
|
||||
"Iterations before stopping test (default: infinite)");
|
||||
|
||||
static unsigned int dmatest;
|
||||
module_param(dmatest, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(dmatest, uint, 0644);
|
||||
MODULE_PARM_DESC(dmatest,
|
||||
"dmatest 0-memcpy 1-memset (default: 0)");
|
||||
|
||||
static unsigned int xor_sources = 3;
|
||||
module_param(xor_sources, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(xor_sources, uint, 0644);
|
||||
MODULE_PARM_DESC(xor_sources,
|
||||
"Number of xor source buffers (default: 3)");
|
||||
|
||||
static unsigned int pq_sources = 3;
|
||||
module_param(pq_sources, uint, S_IRUGO | S_IWUSR);
|
||||
module_param(pq_sources, uint, 0644);
|
||||
MODULE_PARM_DESC(pq_sources,
|
||||
"Number of p+q source buffers (default: 3)");
|
||||
|
||||
static int timeout = 3000;
|
||||
module_param(timeout, int, S_IRUGO | S_IWUSR);
|
||||
module_param(timeout, int, 0644);
|
||||
MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
|
||||
"Pass -1 for infinite timeout");
|
||||
|
||||
static bool noverify;
|
||||
module_param(noverify, bool, S_IRUGO | S_IWUSR);
|
||||
module_param(noverify, bool, 0644);
|
||||
MODULE_PARM_DESC(noverify, "Disable data verification (default: verify)");
|
||||
|
||||
static bool norandom;
|
||||
@@ -74,7 +73,7 @@ module_param(norandom, bool, 0644);
|
||||
MODULE_PARM_DESC(norandom, "Disable random offset setup (default: random)");
|
||||
|
||||
static bool verbose;
|
||||
module_param(verbose, bool, S_IRUGO | S_IWUSR);
|
||||
module_param(verbose, bool, 0644);
|
||||
MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)");
|
||||
|
||||
static int alignment = -1;
|
||||
@@ -86,7 +85,7 @@ module_param(transfer_size, uint, 0644);
|
||||
MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))");
|
||||
|
||||
static bool polled;
|
||||
module_param(polled, bool, S_IRUGO | S_IWUSR);
|
||||
module_param(polled, bool, 0644);
|
||||
MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts");
|
||||
|
||||
/**
|
||||
@@ -154,7 +153,7 @@ static const struct kernel_param_ops run_ops = {
|
||||
.get = dmatest_run_get,
|
||||
};
|
||||
static bool dmatest_run;
|
||||
module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR);
|
||||
module_param_cb(run, &run_ops, &dmatest_run, 0644);
|
||||
MODULE_PARM_DESC(run, "Run the test (default: false)");
|
||||
|
||||
static int dmatest_chan_set(const char *val, const struct kernel_param *kp);
|
||||
@@ -290,7 +289,7 @@ static const struct kernel_param_ops wait_ops = {
|
||||
.get = dmatest_wait_get,
|
||||
.set = param_set_bool,
|
||||
};
|
||||
module_param_cb(wait, &wait_ops, &wait, S_IRUGO);
|
||||
module_param_cb(wait, &wait_ops, &wait, 0444);
|
||||
MODULE_PARM_DESC(wait, "Wait for tests to complete (default: false)");
|
||||
|
||||
static bool dmatest_match_channel(struct dmatest_params *params,
|
||||
@@ -579,10 +578,10 @@ static int dmatest_func(void *data)
|
||||
unsigned int total_tests = 0;
|
||||
dma_cookie_t cookie;
|
||||
enum dma_status status;
|
||||
enum dma_ctrl_flags flags;
|
||||
enum dma_ctrl_flags flags;
|
||||
u8 *pq_coefs = NULL;
|
||||
int ret;
|
||||
unsigned int buf_size;
|
||||
unsigned int buf_size;
|
||||
struct dmatest_data *src;
|
||||
struct dmatest_data *dst;
|
||||
int i;
|
||||
@@ -1095,8 +1094,8 @@ static void add_threaded_test(struct dmatest_info *info)
|
||||
|
||||
/* Copy test parameters */
|
||||
params->buf_size = test_buf_size;
|
||||
strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
|
||||
strlcpy(params->device, strim(test_device), sizeof(params->device));
|
||||
strscpy(params->channel, strim(test_channel), sizeof(params->channel));
|
||||
strscpy(params->device, strim(test_device), sizeof(params->device));
|
||||
params->threads_per_chan = threads_per_chan;
|
||||
params->max_channels = max_channels;
|
||||
params->iterations = iterations;
|
||||
@@ -1240,7 +1239,7 @@ static int dmatest_chan_set(const char *val, const struct kernel_param *kp)
|
||||
dtc = list_last_entry(&info->channels,
|
||||
struct dmatest_chan,
|
||||
node);
|
||||
strlcpy(chan_reset_val,
|
||||
strscpy(chan_reset_val,
|
||||
dma_chan_name(dtc->chan),
|
||||
sizeof(chan_reset_val));
|
||||
ret = -EBUSY;
|
||||
@@ -1263,14 +1262,14 @@ static int dmatest_chan_set(const char *val, const struct kernel_param *kp)
|
||||
if ((strcmp(dma_chan_name(dtc->chan), strim(test_channel)) != 0)
|
||||
&& (strcmp("", strim(test_channel)) != 0)) {
|
||||
ret = -EINVAL;
|
||||
strlcpy(chan_reset_val, dma_chan_name(dtc->chan),
|
||||
strscpy(chan_reset_val, dma_chan_name(dtc->chan),
|
||||
sizeof(chan_reset_val));
|
||||
goto add_chan_err;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Clear test_channel if no channels were added successfully */
|
||||
strlcpy(chan_reset_val, "", sizeof(chan_reset_val));
|
||||
strscpy(chan_reset_val, "", sizeof(chan_reset_val));
|
||||
ret = -EBUSY;
|
||||
goto add_chan_err;
|
||||
}
|
||||
@@ -1295,7 +1294,7 @@ static int dmatest_chan_get(char *val, const struct kernel_param *kp)
|
||||
mutex_lock(&info->lock);
|
||||
if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) {
|
||||
stop_threaded_test(info);
|
||||
strlcpy(test_channel, "", sizeof(test_channel));
|
||||
strscpy(test_channel, "", sizeof(test_channel));
|
||||
}
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
|
||||
@@ -982,6 +982,11 @@ static int dw_axi_dma_chan_slave_config(struct dma_chan *dchan,
|
||||
static void axi_chan_dump_lli(struct axi_dma_chan *chan,
|
||||
struct axi_dma_hw_desc *desc)
|
||||
{
|
||||
if (!desc->lli) {
|
||||
dev_err(dchan2dev(&chan->vc.chan), "NULL LLI\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_err(dchan2dev(&chan->vc.chan),
|
||||
"SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x",
|
||||
le64_to_cpu(desc->lli->sar),
|
||||
@@ -1049,6 +1054,11 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
|
||||
|
||||
/* The completed descriptor currently is in the head of vc list */
|
||||
vd = vchan_next_desc(&chan->vc);
|
||||
if (!vd) {
|
||||
dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n",
|
||||
axi_chan_name(chan));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chan->cyclic) {
|
||||
desc = vd_to_axi_desc(vd);
|
||||
@@ -1078,6 +1088,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
|
||||
axi_chan_start_first_queued(chan);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -417,19 +417,11 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
|
||||
SET_CH_32(dw, chan->dir, chan->id, ch_control1,
|
||||
(DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
|
||||
/* Linked list */
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* llp is not aligned on 64bit -> keep 32bit accesses */
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
|
||||
lower_32_bits(chunk->ll_region.paddr));
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
|
||||
upper_32_bits(chunk->ll_region.paddr));
|
||||
#else /* CONFIG_64BIT */
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
|
||||
lower_32_bits(chunk->ll_region.paddr));
|
||||
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
|
||||
upper_32_bits(chunk->ll_region.paddr));
|
||||
#endif /* CONFIG_64BIT */
|
||||
}
|
||||
/* Doorbell */
|
||||
SET_RW_32(dw, chan->dir, doorbell,
|
||||
|
||||
@@ -102,10 +102,12 @@ free_map:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id rzn1_dmac_match[] = {
|
||||
{ .compatible = "renesas,rzn1-dma" },
|
||||
{}
|
||||
};
|
||||
#endif
|
||||
|
||||
static int rzn1_dmamux_probe(struct platform_device *pdev)
|
||||
{
|
||||
@@ -140,6 +142,7 @@ static const struct of_device_id rzn1_dmamux_match[] = {
|
||||
{ .compatible = "renesas,rzn1-dmamux" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzn1_dmamux_match);
|
||||
|
||||
static struct platform_driver rzn1_dmamux_driver = {
|
||||
.driver = {
|
||||
|
||||
@@ -1183,7 +1183,7 @@ fail:
|
||||
*
|
||||
* Synchronizes the DMA channel termination to the current context. When this
|
||||
* function returns it is guaranteed that all transfers for previously issued
|
||||
* descriptors have stopped and and it is safe to free the memory associated
|
||||
* descriptors have stopped and it is safe to free the memory associated
|
||||
* with them. Furthermore it is guaranteed that all complete callback functions
|
||||
* for a previously submitted descriptor have finished running and it is safe to
|
||||
* free resources accessed from within the complete callbacks.
|
||||
|
||||
@@ -559,9 +559,6 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
|
||||
}
|
||||
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
/* get next sg's physical address */
|
||||
last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
|
||||
|
||||
if (direction == DMA_MEM_TO_DEV) {
|
||||
src_addr = sg_dma_address(sg);
|
||||
dst_addr = fsl_chan->dma_dev_addr;
|
||||
|
||||
@@ -1047,7 +1047,7 @@ static int __init imxdma_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
imxdma->dev = &pdev->dev;
|
||||
imxdma->devtype = (enum imx_dma_type)of_device_get_match_data(&pdev->dev);
|
||||
imxdma->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
imxdma->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
||||
@@ -183,12 +183,14 @@
|
||||
BIT(DMA_DEV_TO_DEV))
|
||||
|
||||
#define SDMA_WATERMARK_LEVEL_N_FIFOS GENMASK(15, 12)
|
||||
#define SDMA_WATERMARK_LEVEL_OFF_FIFOS GENMASK(19, 16)
|
||||
#define SDMA_WATERMARK_LEVEL_WORDS_PER_FIFO GENMASK(31, 28)
|
||||
#define SDMA_WATERMARK_LEVEL_SW_DONE BIT(23)
|
||||
|
||||
#define SDMA_DONE0_CONFIG_DONE_SEL BIT(7)
|
||||
#define SDMA_DONE0_CONFIG_DONE_DIS BIT(6)
|
||||
|
||||
/**
|
||||
/*
|
||||
* struct sdma_script_start_addrs - SDMA script start pointers
|
||||
*
|
||||
* start addresses of the different functions in the physical
|
||||
@@ -424,6 +426,14 @@ struct sdma_desc {
|
||||
* @data: specific sdma interface structure
|
||||
* @bd_pool: dma_pool for bd
|
||||
* @terminate_worker: used to call back into terminate work function
|
||||
* @terminated: terminated list
|
||||
* @is_ram_script: flag for script in ram
|
||||
* @n_fifos_src: number of source device fifos
|
||||
* @n_fifos_dst: number of destination device fifos
|
||||
* @sw_done: software done flag
|
||||
* @stride_fifos_src: stride for source device FIFOs
|
||||
* @stride_fifos_dst: stride for destination device FIFOs
|
||||
* @words_per_fifo: copy number of words one time for one FIFO
|
||||
*/
|
||||
struct sdma_channel {
|
||||
struct virt_dma_chan vc;
|
||||
@@ -451,6 +461,9 @@ struct sdma_channel {
|
||||
bool is_ram_script;
|
||||
unsigned int n_fifos_src;
|
||||
unsigned int n_fifos_dst;
|
||||
unsigned int stride_fifos_src;
|
||||
unsigned int stride_fifos_dst;
|
||||
unsigned int words_per_fifo;
|
||||
bool sw_done;
|
||||
};
|
||||
|
||||
@@ -1240,17 +1253,29 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
|
||||
static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac)
|
||||
{
|
||||
unsigned int n_fifos;
|
||||
unsigned int stride_fifos;
|
||||
unsigned int words_per_fifo;
|
||||
|
||||
if (sdmac->sw_done)
|
||||
sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE;
|
||||
|
||||
if (sdmac->direction == DMA_DEV_TO_MEM)
|
||||
if (sdmac->direction == DMA_DEV_TO_MEM) {
|
||||
n_fifos = sdmac->n_fifos_src;
|
||||
else
|
||||
stride_fifos = sdmac->stride_fifos_src;
|
||||
} else {
|
||||
n_fifos = sdmac->n_fifos_dst;
|
||||
stride_fifos = sdmac->stride_fifos_dst;
|
||||
}
|
||||
|
||||
words_per_fifo = sdmac->words_per_fifo;
|
||||
|
||||
sdmac->watermark_level |=
|
||||
FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos);
|
||||
sdmac->watermark_level |=
|
||||
FIELD_PREP(SDMA_WATERMARK_LEVEL_OFF_FIFOS, stride_fifos);
|
||||
if (words_per_fifo)
|
||||
sdmac->watermark_level |=
|
||||
FIELD_PREP(SDMA_WATERMARK_LEVEL_WORDS_PER_FIFO, (words_per_fifo - 1));
|
||||
}
|
||||
|
||||
static int sdma_config_channel(struct dma_chan *chan)
|
||||
@@ -1764,6 +1789,9 @@ static int sdma_config(struct dma_chan *chan,
|
||||
}
|
||||
sdmac->n_fifos_src = sdmacfg->n_fifos_src;
|
||||
sdmac->n_fifos_dst = sdmacfg->n_fifos_dst;
|
||||
sdmac->stride_fifos_src = sdmacfg->stride_fifos_src;
|
||||
sdmac->stride_fifos_dst = sdmacfg->stride_fifos_dst;
|
||||
sdmac->words_per_fifo = sdmacfg->words_per_fifo;
|
||||
sdmac->sw_done = sdmacfg->sw_done;
|
||||
}
|
||||
|
||||
@@ -2183,8 +2211,8 @@ static int sdma_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, "sdma",
|
||||
sdma);
|
||||
ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0,
|
||||
dev_name(&pdev->dev), sdma);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
|
||||
@@ -373,7 +373,7 @@ static void mtk_cqdma_tasklet_cb(struct tasklet_struct *t)
|
||||
|
||||
/*
|
||||
* free child CVD after completion.
|
||||
* the parent CVD would be freeed with desc_free by user.
|
||||
* the parent CVD would be freed with desc_free by user.
|
||||
*/
|
||||
if (cvd->parent != cvd)
|
||||
kfree(cvd);
|
||||
|
||||
@@ -138,7 +138,7 @@ struct mtk_hsdma_vdesc {
|
||||
|
||||
/**
|
||||
* struct mtk_hsdma_cb - This is the struct holding extra info required for RX
|
||||
* ring to know what relevant VD the the PD is being
|
||||
* ring to know what relevant VD the PD is being
|
||||
* mapped to.
|
||||
* @vd: Pointer to the relevant VD.
|
||||
* @flag: Flag indicating what action should be taken when VD
|
||||
@@ -761,7 +761,7 @@ static void mtk_hsdma_free_active_desc(struct dma_chan *c)
|
||||
/*
|
||||
* Once issue_synchronize is being set, which means once the hardware
|
||||
* consumes all descriptors for the channel in the ring, the
|
||||
* synchronization must be be notified immediately it is completed.
|
||||
* synchronization must be notified immediately it is completed.
|
||||
*/
|
||||
spin_lock(&hvc->vc.lock);
|
||||
if (!list_empty(&hvc->desc_hw_processing)) {
|
||||
|
||||
@@ -313,7 +313,7 @@ mv_xor_v2_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
"%s sw_desc %p: async_tx %p\n",
|
||||
__func__, sw_desc, &sw_desc->async_tx);
|
||||
|
||||
/* assign coookie */
|
||||
/* assign cookie */
|
||||
spin_lock_bh(&xor_dev->lock);
|
||||
cookie = dma_cookie_assign(tx);
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ struct owl_dma_pchan {
|
||||
|
||||
/**
|
||||
* struct owl_dma_pchan - Wrapper for DMA ENGINE channel
|
||||
* @vc: wrappped virtual channel
|
||||
* @vc: wrapped virtual channel
|
||||
* @pchan: the physical channel utilized by this channel
|
||||
* @txd: active transaction on this channel
|
||||
* @cfg: slave configuration for this channel
|
||||
|
||||
@@ -202,7 +202,7 @@ struct s3c24xx_dma_phy {
|
||||
* struct s3c24xx_dma_chan - this structure wraps a DMA ENGINE channel
|
||||
* @id: the id of the channel
|
||||
* @name: name of the channel
|
||||
* @vc: wrappped virtual channel
|
||||
* @vc: wrapped virtual channel
|
||||
* @phy: the physical channel utilized by this channel, if there is one
|
||||
* @runtime_addr: address for RX/TX according to the runtime config
|
||||
* @at: active transaction on this channel
|
||||
|
||||
@@ -52,16 +52,6 @@ static inline struct sf_pdma_desc *to_sf_pdma_desc(struct virt_dma_desc *vd)
|
||||
static struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan)
|
||||
{
|
||||
struct sf_pdma_desc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
if (chan->desc && !chan->desc->in_use) {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
return chan->desc;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
|
||||
if (!desc)
|
||||
@@ -111,7 +101,6 @@ sf_pdma_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest, dma_addr_t src,
|
||||
desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, iflags);
|
||||
chan->desc = desc;
|
||||
sf_pdma_fill_desc(desc, dest, src, len);
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, iflags);
|
||||
|
||||
@@ -170,11 +159,17 @@ static size_t sf_pdma_desc_residue(struct sf_pdma_chan *chan,
|
||||
unsigned long flags;
|
||||
u64 residue = 0;
|
||||
struct sf_pdma_desc *desc;
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct dma_async_tx_descriptor *tx = NULL;
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
|
||||
tx = &chan->desc->vdesc.tx;
|
||||
list_for_each_entry(vd, &chan->vchan.desc_submitted, node)
|
||||
if (vd->tx.cookie == cookie)
|
||||
tx = &vd->tx;
|
||||
|
||||
if (!tx)
|
||||
goto out;
|
||||
|
||||
if (cookie == tx->chan->completed_cookie)
|
||||
goto out;
|
||||
|
||||
@@ -241,6 +236,19 @@ static void sf_pdma_enable_request(struct sf_pdma_chan *chan)
|
||||
writel(v, regs->ctrl);
|
||||
}
|
||||
|
||||
static struct sf_pdma_desc *sf_pdma_get_first_pending_desc(struct sf_pdma_chan *chan)
|
||||
{
|
||||
struct virt_dma_chan *vchan = &chan->vchan;
|
||||
struct virt_dma_desc *vdesc;
|
||||
|
||||
if (list_empty(&vchan->desc_issued))
|
||||
return NULL;
|
||||
|
||||
vdesc = list_first_entry(&vchan->desc_issued, struct virt_dma_desc, node);
|
||||
|
||||
return container_of(vdesc, struct sf_pdma_desc, vdesc);
|
||||
}
|
||||
|
||||
static void sf_pdma_xfer_desc(struct sf_pdma_chan *chan)
|
||||
{
|
||||
struct sf_pdma_desc *desc = chan->desc;
|
||||
@@ -268,8 +276,11 @@ static void sf_pdma_issue_pending(struct dma_chan *dchan)
|
||||
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
|
||||
if (vchan_issue_pending(&chan->vchan) && chan->desc)
|
||||
if (!chan->desc && vchan_issue_pending(&chan->vchan)) {
|
||||
/* vchan_issue_pending has made a check that desc in not NULL */
|
||||
chan->desc = sf_pdma_get_first_pending_desc(chan);
|
||||
sf_pdma_xfer_desc(chan);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
}
|
||||
@@ -298,6 +309,11 @@ static void sf_pdma_donebh_tasklet(struct tasklet_struct *t)
|
||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
list_del(&chan->desc->vdesc.node);
|
||||
vchan_cookie_complete(&chan->desc->vdesc);
|
||||
|
||||
chan->desc = sf_pdma_get_first_pending_desc(chan);
|
||||
if (chan->desc)
|
||||
sf_pdma_xfer_desc(chan);
|
||||
|
||||
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@@ -630,6 +631,21 @@ static void rz_dmac_virt_desc_free(struct virt_dma_desc *vd)
|
||||
*/
|
||||
}
|
||||
|
||||
static void rz_dmac_device_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
|
||||
struct rz_dmac *dmac = to_rz_dmac(chan->device);
|
||||
u32 chstat;
|
||||
int ret;
|
||||
|
||||
ret = read_poll_timeout(rz_dmac_ch_readl, chstat, !(chstat & CHSTAT_EN),
|
||||
100, 100000, false, channel, CHSTAT, 1);
|
||||
if (ret < 0)
|
||||
dev_warn(dmac->dev, "DMA Timeout");
|
||||
|
||||
rz_dmac_set_dmars_register(dmac, channel->index, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* IRQ handling
|
||||
@@ -909,6 +925,7 @@ static int rz_dmac_probe(struct platform_device *pdev)
|
||||
engine->device_config = rz_dmac_config;
|
||||
engine->device_terminate_all = rz_dmac_terminate_all;
|
||||
engine->device_issue_pending = rz_dmac_issue_pending;
|
||||
engine->device_synchronize = rz_dmac_device_synchronize;
|
||||
|
||||
engine->copy_align = DMAENGINE_ALIGN_1_BYTE;
|
||||
dma_set_max_seg_size(engine->dev, U32_MAX);
|
||||
|
||||
@@ -1237,11 +1237,8 @@ static int sprd_dma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sprd_dma_dev *sdev = platform_get_drvdata(pdev);
|
||||
struct sprd_dma_chn *c, *cn;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
/* explicitly free the irq */
|
||||
if (sdev->irq > 0)
|
||||
|
||||
@@ -1970,7 +1970,7 @@ static int d40_config_memcpy(struct d40_chan *d40c)
|
||||
dma_has_cap(DMA_SLAVE, cap)) {
|
||||
d40c->dma_cfg = dma40_memcpy_conf_phy;
|
||||
|
||||
/* Generate interrrupt at end of transfer or relink. */
|
||||
/* Generate interrupt at end of transfer or relink. */
|
||||
d40c->dst_def_cfg |= BIT(D40_SREG_CFG_TIM_POS);
|
||||
|
||||
/* Generate interrupt on error. */
|
||||
|
||||
@@ -1328,12 +1328,7 @@ static irqreturn_t stm32_mdma_irq_handler(int irq, void *devid)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
id = __ffs(status);
|
||||
|
||||
chan = &dmadev->chan[id];
|
||||
if (!chan) {
|
||||
dev_warn(mdma2dev(dmadev), "MDMA channel not initialized\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Handle interrupt for the channel */
|
||||
spin_lock(&chan->vchan.lock);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -122,6 +123,15 @@
|
||||
SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) | \
|
||||
SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2))
|
||||
|
||||
/*
|
||||
* Normal DMA supports individual transfers (segments) up to 128k.
|
||||
* Dedicated DMA supports transfers up to 16M. We can only report
|
||||
* one size limit, so we have to use the smaller value.
|
||||
*/
|
||||
#define SUN4I_NDMA_MAX_SEG_SIZE SZ_128K
|
||||
#define SUN4I_DDMA_MAX_SEG_SIZE SZ_16M
|
||||
#define SUN4I_DMA_MAX_SEG_SIZE SUN4I_NDMA_MAX_SEG_SIZE
|
||||
|
||||
struct sun4i_dma_pchan {
|
||||
/* Register base of channel */
|
||||
void __iomem *base;
|
||||
@@ -155,7 +165,8 @@ struct sun4i_dma_contract {
|
||||
struct virt_dma_desc vd;
|
||||
struct list_head demands;
|
||||
struct list_head completed_demands;
|
||||
int is_cyclic;
|
||||
bool is_cyclic : 1;
|
||||
bool use_half_int : 1;
|
||||
};
|
||||
|
||||
struct sun4i_dma_dev {
|
||||
@@ -372,7 +383,7 @@ static int __execute_vchan_pending(struct sun4i_dma_dev *priv,
|
||||
if (promise) {
|
||||
vchan->contract = contract;
|
||||
vchan->pchan = pchan;
|
||||
set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1);
|
||||
set_pchan_interrupt(priv, pchan, contract->use_half_int, 1);
|
||||
configure_pchan(pchan, promise);
|
||||
}
|
||||
|
||||
@@ -735,12 +746,21 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
|
||||
*
|
||||
* Which requires half the engine programming for the same
|
||||
* functionality.
|
||||
*
|
||||
* This only works if two periods fit in a single promise. That will
|
||||
* always be the case for dedicated DMA, where the hardware has a much
|
||||
* larger maximum transfer size than advertised to clients.
|
||||
*/
|
||||
nr_periods = DIV_ROUND_UP(len / period_len, 2);
|
||||
if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) {
|
||||
period_len *= 2;
|
||||
contract->use_half_int = 1;
|
||||
}
|
||||
|
||||
nr_periods = DIV_ROUND_UP(len, period_len);
|
||||
for (i = 0; i < nr_periods; i++) {
|
||||
/* Calculate the offset in the buffer and the length needed */
|
||||
offset = i * period_len * 2;
|
||||
plength = min((len - offset), (period_len * 2));
|
||||
offset = i * period_len;
|
||||
plength = min((len - offset), period_len);
|
||||
if (dir == DMA_MEM_TO_DEV)
|
||||
src = buf + offset;
|
||||
else
|
||||
@@ -1149,6 +1169,8 @@ static int sun4i_dma_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, priv);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE);
|
||||
|
||||
dma_cap_zero(priv->slave.cap_mask);
|
||||
dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);
|
||||
dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask);
|
||||
|
||||
@@ -157,8 +157,8 @@
|
||||
* If any burst is in flight and DMA paused then this is the time to complete
|
||||
* on-flight burst and update DMA status register.
|
||||
*/
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETE_TIME 20
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT 100
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETE_TIME 10
|
||||
#define TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT 5000 /* 5 msec */
|
||||
|
||||
/* Channel base address offset from GPCDMA base address */
|
||||
#define TEGRA_GPCDMA_CHANNEL_BASE_ADD_OFFSET 0x20000
|
||||
@@ -432,6 +432,17 @@ static int tegra_dma_device_resume(struct dma_chan *dc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tegra_dma_pause_noerr(struct tegra_dma_channel *tdc)
|
||||
{
|
||||
/* Return 0 irrespective of PAUSE status.
|
||||
* This is useful to recover channels that can exit out of flush
|
||||
* state when the channel is disabled.
|
||||
*/
|
||||
|
||||
tegra_dma_pause(tdc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_dma_disable(struct tegra_dma_channel *tdc)
|
||||
{
|
||||
u32 csr, status;
|
||||
@@ -1292,6 +1303,14 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
|
||||
.terminate = tegra_dma_pause,
|
||||
};
|
||||
|
||||
static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
|
||||
.nr_channels = 31,
|
||||
.channel_reg_size = SZ_64K,
|
||||
.max_dma_count = SZ_1G,
|
||||
.hw_support_pause = true,
|
||||
.terminate = tegra_dma_pause_noerr,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_dma_of_match[] = {
|
||||
{
|
||||
.compatible = "nvidia,tegra186-gpcdma",
|
||||
@@ -1299,6 +1318,9 @@ static const struct of_device_id tegra_dma_of_match[] = {
|
||||
}, {
|
||||
.compatible = "nvidia,tegra194-gpcdma",
|
||||
.data = &tegra194_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra234-gpcdma",
|
||||
.data = &tegra234_dma_chip_data,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
|
||||
@@ -112,6 +112,11 @@ static struct psil_ep j721s2_src_ep_map[] = {
|
||||
PSIL_PDMA_XY_PKT(0x4707),
|
||||
PSIL_PDMA_XY_PKT(0x4708),
|
||||
PSIL_PDMA_XY_PKT(0x4709),
|
||||
/* MAIN SA2UL */
|
||||
PSIL_SA2UL(0x4a40, 0),
|
||||
PSIL_SA2UL(0x4a41, 0),
|
||||
PSIL_SA2UL(0x4a42, 0),
|
||||
PSIL_SA2UL(0x4a43, 0),
|
||||
/* CPSW0 */
|
||||
PSIL_ETHERNET(0x7000),
|
||||
/* MCU_PDMA0 (MCU_PDMA_MISC_G0) - SPI0 */
|
||||
@@ -144,6 +149,9 @@ static struct psil_ep j721s2_src_ep_map[] = {
|
||||
|
||||
/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */
|
||||
static struct psil_ep j721s2_dst_ep_map[] = {
|
||||
/* MAIN SA2UL */
|
||||
PSIL_SA2UL(0xca40, 1),
|
||||
PSIL_SA2UL(0xca41, 1),
|
||||
/* CPSW0 */
|
||||
PSIL_ETHERNET(0xf000),
|
||||
PSIL_ETHERNET(0xf001),
|
||||
|
||||
@@ -2127,126 +2127,6 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_cdma_prep_memcpy_sg - prepare descriptors for a memcpy_sg transaction
|
||||
* @dchan: DMA channel
|
||||
* @dst_sg: Destination scatter list
|
||||
* @dst_sg_len: Number of entries in destination scatter list
|
||||
* @src_sg: Source scatter list
|
||||
* @src_sg_len: Number of entries in source scatter list
|
||||
* @flags: transfer ack flags
|
||||
*
|
||||
* Return: Async transaction descriptor on success and NULL on failure
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *xilinx_cdma_prep_memcpy_sg(
|
||||
struct dma_chan *dchan, struct scatterlist *dst_sg,
|
||||
unsigned int dst_sg_len, struct scatterlist *src_sg,
|
||||
unsigned int src_sg_len, unsigned long flags)
|
||||
{
|
||||
struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
|
||||
struct xilinx_dma_tx_descriptor *desc;
|
||||
struct xilinx_cdma_tx_segment *segment, *prev = NULL;
|
||||
struct xilinx_cdma_desc_hw *hw;
|
||||
size_t len, dst_avail, src_avail;
|
||||
dma_addr_t dma_dst, dma_src;
|
||||
|
||||
if (unlikely(dst_sg_len == 0 || src_sg_len == 0))
|
||||
return NULL;
|
||||
|
||||
if (unlikely(!dst_sg || !src_sg))
|
||||
return NULL;
|
||||
|
||||
desc = xilinx_dma_alloc_tx_descriptor(chan);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
|
||||
desc->async_tx.tx_submit = xilinx_dma_tx_submit;
|
||||
|
||||
dst_avail = sg_dma_len(dst_sg);
|
||||
src_avail = sg_dma_len(src_sg);
|
||||
/*
|
||||
* loop until there is either no more source or no more destination
|
||||
* scatterlist entry
|
||||
*/
|
||||
while (true) {
|
||||
len = min_t(size_t, src_avail, dst_avail);
|
||||
len = min_t(size_t, len, chan->xdev->max_buffer_len);
|
||||
if (len == 0)
|
||||
goto fetch;
|
||||
|
||||
/* Allocate the link descriptor from DMA pool */
|
||||
segment = xilinx_cdma_alloc_tx_segment(chan);
|
||||
if (!segment)
|
||||
goto error;
|
||||
|
||||
dma_dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) -
|
||||
dst_avail;
|
||||
dma_src = sg_dma_address(src_sg) + sg_dma_len(src_sg) -
|
||||
src_avail;
|
||||
hw = &segment->hw;
|
||||
hw->control = len;
|
||||
hw->src_addr = dma_src;
|
||||
hw->dest_addr = dma_dst;
|
||||
if (chan->ext_addr) {
|
||||
hw->src_addr_msb = upper_32_bits(dma_src);
|
||||
hw->dest_addr_msb = upper_32_bits(dma_dst);
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->hw.next_desc = segment->phys;
|
||||
if (chan->ext_addr)
|
||||
prev->hw.next_desc_msb =
|
||||
upper_32_bits(segment->phys);
|
||||
}
|
||||
|
||||
prev = segment;
|
||||
dst_avail -= len;
|
||||
src_avail -= len;
|
||||
list_add_tail(&segment->node, &desc->segments);
|
||||
|
||||
fetch:
|
||||
/* Fetch the next dst scatterlist entry */
|
||||
if (dst_avail == 0) {
|
||||
if (dst_sg_len == 0)
|
||||
break;
|
||||
dst_sg = sg_next(dst_sg);
|
||||
if (dst_sg == NULL)
|
||||
break;
|
||||
dst_sg_len--;
|
||||
dst_avail = sg_dma_len(dst_sg);
|
||||
}
|
||||
/* Fetch the next src scatterlist entry */
|
||||
if (src_avail == 0) {
|
||||
if (src_sg_len == 0)
|
||||
break;
|
||||
src_sg = sg_next(src_sg);
|
||||
if (src_sg == NULL)
|
||||
break;
|
||||
src_sg_len--;
|
||||
src_avail = sg_dma_len(src_sg);
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(&desc->segments)) {
|
||||
dev_err(chan->xdev->dev,
|
||||
"%s: Zero-size SG transfer requested\n", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Link the last hardware descriptor with the first. */
|
||||
segment = list_first_entry(&desc->segments,
|
||||
struct xilinx_cdma_tx_segment, node);
|
||||
desc->async_tx.phys = segment->phys;
|
||||
prev->hw.next_desc = segment->phys;
|
||||
|
||||
return &desc->async_tx;
|
||||
|
||||
error:
|
||||
xilinx_dma_free_tx_descriptor(chan, desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
|
||||
* @dchan: DMA channel
|
||||
@@ -3240,9 +3120,7 @@ static int xilinx_dma_probe(struct platform_device *pdev)
|
||||
DMA_RESIDUE_GRANULARITY_SEGMENT;
|
||||
} else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
|
||||
dma_cap_set(DMA_MEMCPY, xdev->common.cap_mask);
|
||||
dma_cap_set(DMA_MEMCPY_SG, xdev->common.cap_mask);
|
||||
xdev->common.device_prep_dma_memcpy = xilinx_cdma_prep_memcpy;
|
||||
xdev->common.device_prep_dma_memcpy_sg = xilinx_cdma_prep_memcpy_sg;
|
||||
/* Residue calculation is supported by only AXI DMA and CDMA */
|
||||
xdev->common.residue_granularity =
|
||||
DMA_RESIDUE_GRANULARITY_SEGMENT;
|
||||
|
||||
@@ -376,7 +376,7 @@ static ssize_t xilinx_dpdma_debugfs_read(struct file *f, char __user *buf,
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
} else {
|
||||
strlcpy(kern_buff, "No testcase executed",
|
||||
strscpy(kern_buff, "No testcase executed",
|
||||
XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE);
|
||||
}
|
||||
|
||||
@@ -1652,10 +1652,8 @@ static int xilinx_dpdma_probe(struct platform_device *pdev)
|
||||
dpdma_hw_init(xdev);
|
||||
|
||||
xdev->irq = platform_get_irq(pdev, 0);
|
||||
if (xdev->irq < 0) {
|
||||
dev_err(xdev->dev, "failed to get platform irq\n");
|
||||
if (xdev->irq < 0)
|
||||
return xdev->irq;
|
||||
}
|
||||
|
||||
ret = request_irq(xdev->irq, xilinx_dpdma_irq_handler, IRQF_SHARED,
|
||||
dev_name(xdev->dev), xdev);
|
||||
|
||||
@@ -1341,17 +1341,17 @@ static int msb_ftl_initialize(struct msb_data *msb)
|
||||
msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE;
|
||||
msb->logical_block_count = msb->zone_count * 496 - 2;
|
||||
|
||||
msb->used_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL);
|
||||
msb->erased_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL);
|
||||
msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL);
|
||||
msb->erased_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL);
|
||||
msb->lba_to_pba_table =
|
||||
kmalloc_array(msb->logical_block_count, sizeof(u16),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!msb->used_blocks_bitmap || !msb->lba_to_pba_table ||
|
||||
!msb->erased_blocks_bitmap) {
|
||||
kfree(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->erased_blocks_bitmap);
|
||||
kfree(msb->lba_to_pba_table);
|
||||
kfree(msb->erased_blocks_bitmap);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -1946,7 +1946,8 @@ static DEFINE_MUTEX(msb_disk_lock); /* protects against races in open/release */
|
||||
static void msb_data_clear(struct msb_data *msb)
|
||||
{
|
||||
kfree(msb->boot_page);
|
||||
kfree(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->erased_blocks_bitmap);
|
||||
kfree(msb->lba_to_pba_table);
|
||||
kfree(msb->cache);
|
||||
msb->card = NULL;
|
||||
@@ -2243,8 +2244,8 @@ static int msb_resume(struct memstick_dev *card)
|
||||
goto out;
|
||||
|
||||
if (msb->block_count != new_msb->block_count ||
|
||||
memcmp(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap,
|
||||
msb->block_count / 8))
|
||||
!bitmap_equal(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap,
|
||||
msb->block_count))
|
||||
goto out;
|
||||
|
||||
card_dead = false;
|
||||
|
||||
@@ -176,7 +176,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
unsigned int part_type);
|
||||
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_card *card,
|
||||
int disable_multi,
|
||||
int recovery_mode,
|
||||
struct mmc_queue *mq);
|
||||
static void mmc_blk_hsq_req_done(struct mmc_request *mrq);
|
||||
|
||||
@@ -1302,7 +1302,7 @@ static void mmc_blk_eval_resp_error(struct mmc_blk_request *brq)
|
||||
}
|
||||
|
||||
static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
int disable_multi, bool *do_rel_wr_p,
|
||||
int recovery_mode, bool *do_rel_wr_p,
|
||||
bool *do_data_tag_p)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
@@ -1368,12 +1368,12 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
brq->data.blocks--;
|
||||
|
||||
/*
|
||||
* After a read error, we redo the request one sector
|
||||
* After a read error, we redo the request one (native) sector
|
||||
* at a time in order to accurately determine which
|
||||
* sectors can be read successfully.
|
||||
*/
|
||||
if (disable_multi)
|
||||
brq->data.blocks = 1;
|
||||
if (recovery_mode)
|
||||
brq->data.blocks = queue_physical_block_size(mq->queue) >> 9;
|
||||
|
||||
/*
|
||||
* Some controllers have HW issues while operating
|
||||
@@ -1590,7 +1590,7 @@ static int mmc_blk_cqe_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
||||
|
||||
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_card *card,
|
||||
int disable_multi,
|
||||
int recovery_mode,
|
||||
struct mmc_queue *mq)
|
||||
{
|
||||
u32 readcmd, writecmd;
|
||||
@@ -1599,7 +1599,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
bool do_rel_wr, do_data_tag;
|
||||
|
||||
mmc_blk_data_prep(mq, mqrq, disable_multi, &do_rel_wr, &do_data_tag);
|
||||
mmc_blk_data_prep(mq, mqrq, recovery_mode, &do_rel_wr, &do_data_tag);
|
||||
|
||||
brq->mrq.cmd = &brq->cmd;
|
||||
|
||||
@@ -1690,7 +1690,7 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req)
|
||||
|
||||
#define MMC_READ_SINGLE_RETRIES 2
|
||||
|
||||
/* Single sector read during recovery */
|
||||
/* Single (native) sector read during recovery */
|
||||
static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
|
||||
@@ -1698,6 +1698,7 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
struct mmc_card *card = mq->card;
|
||||
struct mmc_host *host = card->host;
|
||||
blk_status_t error = BLK_STS_OK;
|
||||
size_t bytes_per_read = queue_physical_block_size(mq->queue);
|
||||
|
||||
do {
|
||||
u32 status;
|
||||
@@ -1732,13 +1733,13 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
else
|
||||
error = BLK_STS_OK;
|
||||
|
||||
} while (blk_update_request(req, error, 512));
|
||||
} while (blk_update_request(req, error, bytes_per_read));
|
||||
|
||||
return;
|
||||
|
||||
error_exit:
|
||||
mrq->data->bytes_xfered = 0;
|
||||
blk_update_request(req, BLK_STS_IOERR, 512);
|
||||
blk_update_request(req, BLK_STS_IOERR, bytes_per_read);
|
||||
/* Let it try the remaining request again */
|
||||
if (mqrq->retries > MMC_MAX_RETRIES - 1)
|
||||
mqrq->retries = MMC_MAX_RETRIES - 1;
|
||||
@@ -1879,10 +1880,9 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req)
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: Missing single sector read for large sector size */
|
||||
if (!mmc_large_sector(card) && rq_data_dir(req) == READ &&
|
||||
brq->data.blocks > 1) {
|
||||
/* Read one sector at a time */
|
||||
if (rq_data_dir(req) == READ && brq->data.blocks >
|
||||
queue_physical_block_size(mq->queue) >> 9) {
|
||||
/* Read one (native) sector at a time */
|
||||
mmc_blk_read_single(mq, req);
|
||||
return;
|
||||
}
|
||||
@@ -2988,7 +2988,7 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
* Don't enable runtime PM for SD-combo cards here. Leave that
|
||||
* decision to be taken during the SDIO init sequence instead.
|
||||
*/
|
||||
if (card->type != MMC_TYPE_SD_COMBO) {
|
||||
if (!mmc_card_sd_combo(card)) {
|
||||
pm_runtime_set_active(&card->dev);
|
||||
pm_runtime_enable(&card->dev);
|
||||
}
|
||||
@@ -3015,7 +3015,7 @@ static void mmc_blk_remove(struct mmc_card *card)
|
||||
mmc_blk_part_switch(card, md->part_type);
|
||||
mmc_release_host(card->host);
|
||||
}
|
||||
if (card->type != MMC_TYPE_SD_COMBO)
|
||||
if (!mmc_card_sd_combo(card))
|
||||
pm_runtime_disable(&card->dev);
|
||||
pm_runtime_put_noidle(&card->dev);
|
||||
mmc_blk_remove_req(md);
|
||||
|
||||
@@ -85,7 +85,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
|
||||
if (mmc_card_sdio(card) || mmc_card_sd_combo(card)) {
|
||||
retval = add_uevent_var(env, "SDIO_ID=%04X:%04X",
|
||||
card->cis.vendor, card->cis.device);
|
||||
if (retval)
|
||||
@@ -107,7 +107,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
* SDIO (non-combo) cards are not handled by mmc_block driver and do not
|
||||
* have accessible CID register which used by mmc_card_name() function.
|
||||
*/
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
if (mmc_card_sdio(card))
|
||||
return 0;
|
||||
|
||||
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
|
||||
|
||||
@@ -943,9 +943,11 @@ int mmc_execute_tuning(struct mmc_card *card)
|
||||
}
|
||||
|
||||
/* Only print error when we don't check for card removal */
|
||||
if (!host->detect_change)
|
||||
if (!host->detect_change) {
|
||||
pr_err("%s: tuning execution failed: %d\n",
|
||||
mmc_hostname(host), err);
|
||||
mmc_debugfs_err_stats_inc(host, MMC_ERR_TUNING);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -2244,6 +2246,12 @@ void mmc_rescan(struct work_struct *work)
|
||||
if (freqs[i] <= host->f_min)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the command timeout errors observed during
|
||||
* the card init as those are excepted.
|
||||
*/
|
||||
host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
|
||||
mmc_release_host(host);
|
||||
|
||||
out:
|
||||
|
||||
@@ -223,6 +223,81 @@ static int mmc_clock_opt_set(void *data, u64 val)
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
|
||||
"%llu\n");
|
||||
|
||||
static int mmc_err_state_get(void *data, u64 *val)
|
||||
{
|
||||
struct mmc_host *host = data;
|
||||
int i;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
*val = 0;
|
||||
for (i = 0; i < MMC_ERR_MAX; i++) {
|
||||
if (host->err_stats[i]) {
|
||||
*val = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(mmc_err_state, mmc_err_state_get, NULL, "%llu\n");
|
||||
|
||||
static int mmc_err_stats_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct mmc_host *host = (struct mmc_host *)file->private;
|
||||
const char *desc[MMC_ERR_MAX] = {
|
||||
[MMC_ERR_CMD_TIMEOUT] = "Command Timeout Occurred",
|
||||
[MMC_ERR_CMD_CRC] = "Command CRC Errors Occurred",
|
||||
[MMC_ERR_DAT_TIMEOUT] = "Data Timeout Occurred",
|
||||
[MMC_ERR_DAT_CRC] = "Data CRC Errors Occurred",
|
||||
[MMC_ERR_AUTO_CMD] = "Auto-Cmd Error Occurred",
|
||||
[MMC_ERR_ADMA] = "ADMA Error Occurred",
|
||||
[MMC_ERR_TUNING] = "Tuning Error Occurred",
|
||||
[MMC_ERR_CMDQ_RED] = "CMDQ RED Errors",
|
||||
[MMC_ERR_CMDQ_GCE] = "CMDQ GCE Errors",
|
||||
[MMC_ERR_CMDQ_ICCE] = "CMDQ ICCE Errors",
|
||||
[MMC_ERR_REQ_TIMEOUT] = "Request Timedout",
|
||||
[MMC_ERR_CMDQ_REQ_TIMEOUT] = "CMDQ Request Timedout",
|
||||
[MMC_ERR_ICE_CFG] = "ICE Config Errors",
|
||||
[MMC_ERR_CTRL_TIMEOUT] = "Controller Timedout errors",
|
||||
[MMC_ERR_UNEXPECTED_IRQ] = "Unexpected IRQ errors",
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MMC_ERR_MAX; i++) {
|
||||
if (desc[i])
|
||||
seq_printf(file, "# %s:\t %d\n",
|
||||
desc[i], host->err_stats[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_err_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mmc_err_stats_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t mmc_err_stats_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mmc_host *host = filp->f_mapping->host->i_private;
|
||||
|
||||
pr_debug("%s: Resetting MMC error statistics\n", __func__);
|
||||
memset(host->err_stats, 0, sizeof(host->err_stats));
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_err_stats_fops = {
|
||||
.open = mmc_err_stats_open,
|
||||
.read = seq_read,
|
||||
.write = mmc_err_stats_write,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
{
|
||||
struct dentry *root;
|
||||
@@ -236,6 +311,11 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
&mmc_clock_fops);
|
||||
|
||||
debugfs_create_file_unsafe("err_state", 0600, root, host,
|
||||
&mmc_err_state);
|
||||
debugfs_create_file("err_stats", 0600, root, host,
|
||||
&mmc_err_stats_fops);
|
||||
|
||||
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||
if (fail_request)
|
||||
setup_fault_attr(&fail_default_attr, fail_request);
|
||||
|
||||
@@ -599,7 +599,7 @@ static int mmc_validate_host_caps(struct mmc_host *host)
|
||||
}
|
||||
|
||||
if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) &&
|
||||
!(caps & MMC_CAP_8_BIT_DATA)) {
|
||||
!(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) {
|
||||
dev_warn(dev, "drop HS400 support since no 8-bit bus\n");
|
||||
host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400;
|
||||
}
|
||||
|
||||
@@ -163,8 +163,10 @@ static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card,
|
||||
struct device_node *np;
|
||||
|
||||
for_each_child_of_node(mmc_dev(card->host)->of_node, np) {
|
||||
if (of_device_is_compatible(np, compatible))
|
||||
if (of_device_is_compatible(np, compatible)) {
|
||||
of_node_put(np);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -793,7 +793,7 @@ static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
attr == &dev_attr_info2.attr ||
|
||||
attr == &dev_attr_info3.attr ||
|
||||
attr == &dev_attr_info4.attr
|
||||
) && card->type != MMC_TYPE_SD_COMBO)
|
||||
) &&!mmc_card_sd_combo(card))
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
@@ -870,7 +870,7 @@ try_again:
|
||||
* the CCS bit is set as well. We deliberately deviate from the spec in
|
||||
* regards to this, which allows UHS-I to be supported for SDSC cards.
|
||||
*/
|
||||
if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x01000000)) {
|
||||
if (!mmc_host_is_spi(host) && rocr && (*rocr & SD_ROCR_S18A)) {
|
||||
err = mmc_set_uhs_voltage(host, pocr);
|
||||
if (err == -EAGAIN) {
|
||||
retries--;
|
||||
|
||||
@@ -226,6 +226,20 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
|
||||
if (data & SDIO_DRIVE_SDTD)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
|
||||
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTERRUPT_EXT, 0, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (data & SDIO_INTERRUPT_EXT_SAI) {
|
||||
data |= SDIO_INTERRUPT_EXT_EAI;
|
||||
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_INTERRUPT_EXT,
|
||||
data, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
card->cccr.enable_async_irq = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no uhs mode ensure we check for high speed */
|
||||
@@ -335,7 +349,7 @@ static int sdio_disable_4bit_bus(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
if (mmc_card_sdio(card))
|
||||
goto out;
|
||||
|
||||
if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
|
||||
@@ -360,7 +374,7 @@ static int sdio_enable_4bit_bus(struct mmc_card *card)
|
||||
err = sdio_enable_wide(card);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
if (mmc_card_sdio(card))
|
||||
goto out;
|
||||
|
||||
if (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) {
|
||||
@@ -415,7 +429,7 @@ static int sdio_enable_hs(struct mmc_card *card)
|
||||
int ret;
|
||||
|
||||
ret = mmc_sdio_switch_hs(card, true);
|
||||
if (ret <= 0 || card->type == MMC_TYPE_SDIO)
|
||||
if (ret <= 0 || mmc_card_sdio(card))
|
||||
return ret;
|
||||
|
||||
ret = mmc_sd_switch_hs(card);
|
||||
@@ -441,7 +455,7 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
|
||||
max_dtr = card->cis.max_dtr;
|
||||
}
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO)
|
||||
if (mmc_card_sd_combo(card))
|
||||
max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
|
||||
|
||||
return max_dtr;
|
||||
@@ -689,7 +703,7 @@ try_again:
|
||||
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
|
||||
card->type = MMC_TYPE_SD_COMBO;
|
||||
|
||||
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||
if (oldcard && (!mmc_card_sd_combo(oldcard) ||
|
||||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
@@ -697,7 +711,7 @@ try_again:
|
||||
} else {
|
||||
card->type = MMC_TYPE_SDIO;
|
||||
|
||||
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
||||
if (oldcard && !mmc_card_sdio(oldcard)) {
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
}
|
||||
@@ -754,7 +768,7 @@ try_again:
|
||||
/*
|
||||
* Read CSD, before selecting the card
|
||||
*/
|
||||
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
||||
if (!oldcard && mmc_card_sd_combo(card)) {
|
||||
err = mmc_sd_get_csd(card);
|
||||
if (err)
|
||||
goto remove;
|
||||
@@ -827,7 +841,7 @@ try_again:
|
||||
|
||||
mmc_fixup_device(card, sdio_fixup_methods);
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
if (mmc_card_sd_combo(card)) {
|
||||
err = mmc_sd_setup_card(host, card, oldcard != NULL);
|
||||
/* handle as SDIO-only card if memory init failed */
|
||||
if (err) {
|
||||
|
||||
@@ -277,6 +277,7 @@ static int octeon_mmc_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error populating slots\n");
|
||||
octeon_mmc_set_shared_power(host, 0);
|
||||
of_node_put(cn);
|
||||
goto error;
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -142,8 +142,10 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
||||
continue;
|
||||
|
||||
ret = cvm_mmc_of_slot_probe(&host->slot_pdev[i]->dev, host);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(child_node);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -822,8 +822,15 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
|
||||
pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status);
|
||||
|
||||
if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) ||
|
||||
cmd_error || data_error)
|
||||
cmd_error || data_error) {
|
||||
if (status & CQHCI_IS_RED)
|
||||
mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_RED);
|
||||
if (status & CQHCI_IS_GCE)
|
||||
mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_GCE);
|
||||
if (status & CQHCI_IS_ICCE)
|
||||
mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_ICCE);
|
||||
cqhci_error_irq(mmc, status, cmd_error, data_error);
|
||||
}
|
||||
|
||||
if (status & CQHCI_IS_TCC) {
|
||||
/* read TCN and complete the request */
|
||||
|
||||
@@ -670,7 +670,9 @@ static int dw_mci_exynos_remove(struct platform_device *pdev)
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return dw_mci_pltfm_remove(pdev);
|
||||
dw_mci_pltfm_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_exynos_pmops = {
|
||||
|
||||
@@ -179,7 +179,9 @@ static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
|
||||
clk_disable_unprepare(priv->drive_clk);
|
||||
clk_disable_unprepare(priv->sample_clk);
|
||||
|
||||
return dw_mci_pltfm_remove(pdev);
|
||||
dw_mci_pltfm_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dw_mci_hi3798cv200_match[] = {
|
||||
|
||||
@@ -377,7 +377,9 @@ static int dw_mci_rockchip_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return dw_mci_pltfm_remove(pdev);
|
||||
dw_mci_pltfm_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = {
|
||||
|
||||
@@ -762,7 +762,7 @@ int mmci_dmae_setup(struct mmci_host *host)
|
||||
|
||||
/*
|
||||
* If only an RX channel is specified, the driver will
|
||||
* attempt to use it bidirectionally, however if it is
|
||||
* attempt to use it bidirectionally, however if it
|
||||
* is specified but cannot be located, DMA will be disabled.
|
||||
*/
|
||||
if (dmae->rx_channel && !dmae->tx_channel)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2015 MediaTek Inc.
|
||||
* Copyright (c) 2014-2015, 2022 MediaTek Inc.
|
||||
* Author: Chaotian.Jing <chaotian.jing@mediatek.com>
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -440,8 +441,10 @@ struct msdc_host {
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_uhs;
|
||||
struct pinctrl_state *pins_eint;
|
||||
struct delayed_work req_timeout;
|
||||
int irq; /* host interrupt */
|
||||
int eint_irq; /* interrupt from sdio device for waking up system */
|
||||
struct reset_control *reset;
|
||||
|
||||
struct clk *src_clk; /* msdc source clock */
|
||||
@@ -1521,17 +1524,46 @@ static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb)
|
||||
|
||||
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
__msdc_enable_sdio_irq(host, enb);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (enb)
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
else
|
||||
pm_runtime_put_noidle(host->dev);
|
||||
if (mmc_card_enable_async_irq(mmc->card) && host->pins_eint) {
|
||||
if (enb) {
|
||||
/*
|
||||
* In dev_pm_set_dedicated_wake_irq_reverse(), eint pin will be set to
|
||||
* GPIO mode. We need to restore it to SDIO DAT1 mode after that.
|
||||
* Since the current pinstate is pins_uhs, to ensure pinctrl select take
|
||||
* affect successfully, we change the pinstate to pins_eint firstly.
|
||||
*/
|
||||
pinctrl_select_state(host->pinctrl, host->pins_eint);
|
||||
ret = dev_pm_set_dedicated_wake_irq_reverse(host->dev, host->eint_irq);
|
||||
|
||||
if (ret) {
|
||||
dev_err(host->dev, "Failed to register SDIO wakeup irq!\n");
|
||||
host->pins_eint = NULL;
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
} else {
|
||||
dev_dbg(host->dev, "SDIO eint irq: %d!\n", host->eint_irq);
|
||||
}
|
||||
|
||||
pinctrl_select_state(host->pinctrl, host->pins_uhs);
|
||||
} else {
|
||||
dev_pm_clear_wake_irq(host->dev);
|
||||
}
|
||||
} else {
|
||||
if (enb) {
|
||||
/* Ensure host->pins_eint is NULL */
|
||||
host->pins_eint = NULL;
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
} else {
|
||||
pm_runtime_put_noidle(host->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
|
||||
@@ -2319,7 +2351,7 @@ static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card
|
||||
else
|
||||
val = readl(host->base + PAD_DS_TUNE);
|
||||
|
||||
dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val);
|
||||
dev_info(host->dev, "Final PAD_DS_TUNE: 0x%x\n", val);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -2635,6 +2667,20 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||
goto host_free;
|
||||
}
|
||||
|
||||
/* Support for SDIO eint irq ? */
|
||||
if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) {
|
||||
host->eint_irq = platform_get_irq_byname(pdev, "sdio_wakeup");
|
||||
if (host->eint_irq > 0) {
|
||||
host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint");
|
||||
if (IS_ERR(host->pins_eint)) {
|
||||
dev_err(&pdev->dev, "Cannot find pinctrl eint!\n");
|
||||
host->pins_eint = NULL;
|
||||
} else {
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msdc_of_property_parse(pdev, host);
|
||||
|
||||
host->dev = &pdev->dev;
|
||||
@@ -2849,6 +2895,15 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev)
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
msdc_save_reg(host);
|
||||
|
||||
if (sdio_irq_claimed(mmc)) {
|
||||
if (host->pins_eint) {
|
||||
disable_irq(host->irq);
|
||||
pinctrl_select_state(host->pinctrl, host->pins_eint);
|
||||
}
|
||||
|
||||
__msdc_enable_sdio_irq(host, 0);
|
||||
}
|
||||
msdc_gate_clock(host);
|
||||
return 0;
|
||||
}
|
||||
@@ -2864,12 +2919,18 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev)
|
||||
return ret;
|
||||
|
||||
msdc_restore_reg(host);
|
||||
|
||||
if (sdio_irq_claimed(mmc) && host->pins_eint) {
|
||||
pinctrl_select_state(host->pinctrl, host->pins_uhs);
|
||||
enable_irq(host->irq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused msdc_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
if (mmc->caps2 & MMC_CAP2_CQE) {
|
||||
@@ -2878,11 +2939,24 @@ static int __maybe_unused msdc_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bump up runtime PM usage counter otherwise dev->power.needs_force_resume will
|
||||
* not be marked as 1, pm_runtime_force_resume() will go out directly.
|
||||
*/
|
||||
if (sdio_irq_claimed(mmc) && host->pins_eint)
|
||||
pm_runtime_get_noresume(dev);
|
||||
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused msdc_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (sdio_irq_claimed(mmc) && host->pins_eint)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -923,7 +923,7 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card)
|
||||
* One way to prevent this is to only allow 1-bit transfers.
|
||||
*/
|
||||
|
||||
if (is_imx31_mmc(mxcmci) && card->type == MMC_TYPE_SDIO)
|
||||
if (is_imx31_mmc(mxcmci) && mmc_card_sdio(card))
|
||||
host->caps &= ~MMC_CAP_4_BIT_DATA;
|
||||
else
|
||||
host->caps |= MMC_CAP_4_BIT_DATA;
|
||||
@@ -1025,7 +1025,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
host->devtype = (enum mxcmci_type)of_device_get_match_data(&pdev->dev);
|
||||
host->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
/* adjust max_segs after devtype detection */
|
||||
if (!is_mpc512x_mmc(host))
|
||||
|
||||
@@ -43,6 +43,7 @@ struct renesas_sdhi_quirks {
|
||||
bool hs400_4taps;
|
||||
bool fixed_addr_mode;
|
||||
bool dma_one_rx_only;
|
||||
bool manual_tap_correction;
|
||||
u32 hs400_bad_taps;
|
||||
const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX];
|
||||
};
|
||||
|
||||
@@ -49,9 +49,6 @@
|
||||
#define HOST_MODE_GEN3_32BIT (HOST_MODE_GEN3_WMODE | HOST_MODE_GEN3_BUSWIDTH)
|
||||
#define HOST_MODE_GEN3_64BIT 0
|
||||
|
||||
#define CTL_SDIF_MODE 0xe6
|
||||
#define SDIF_MODE_HS400 BIT(0)
|
||||
|
||||
#define SDHI_VER_GEN2_SDR50 0x490c
|
||||
#define SDHI_VER_RZ_A1 0x820b
|
||||
/* very old datasheets said 0x490c for SDR104, too. They are wrong! */
|
||||
@@ -383,8 +380,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF,
|
||||
priv->scc_tappos_hs400);
|
||||
|
||||
/* Gen3 can't do automatic tap correction with HS400, so disable it */
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC)
|
||||
if (priv->quirks && priv->quirks->manual_tap_correction)
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
|
||||
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
|
||||
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
|
||||
@@ -562,23 +558,25 @@ static void renesas_sdhi_scc_reset(struct tmio_mmc_host *host, struct renesas_sd
|
||||
}
|
||||
|
||||
/* only populated for TMIO_MMC_MIN_RCAR2 */
|
||||
static void renesas_sdhi_reset(struct tmio_mmc_host *host)
|
||||
static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
if (priv->rstc) {
|
||||
reset_control_reset(priv->rstc);
|
||||
/* Unknown why but without polling reset status, it will hang */
|
||||
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
|
||||
false, priv->rstc);
|
||||
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||
priv->needs_adjust_hs400 = false;
|
||||
renesas_sdhi_set_clock(host, host->clk_cache);
|
||||
} else if (priv->scc_ctl) {
|
||||
renesas_sdhi_scc_reset(host, priv);
|
||||
if (!preserve) {
|
||||
if (priv->rstc) {
|
||||
reset_control_reset(priv->rstc);
|
||||
/* Unknown why but without polling reset status, it will hang */
|
||||
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
|
||||
false, priv->rstc);
|
||||
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||
priv->needs_adjust_hs400 = false;
|
||||
renesas_sdhi_set_clock(host, host->clk_cache);
|
||||
} else if (priv->scc_ctl) {
|
||||
renesas_sdhi_scc_reset(host, priv);
|
||||
}
|
||||
}
|
||||
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) >= SDHI_VER_GEN3_SD) {
|
||||
@@ -719,7 +717,7 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
|
||||
|
||||
/* Change TAP position according to correction status */
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
|
||||
if (priv->quirks && priv->quirks->manual_tap_correction &&
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
|
||||
/*
|
||||
@@ -938,6 +936,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
if (IS_ERR(priv->clk_cd))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock");
|
||||
|
||||
priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rstc))
|
||||
return PTR_ERR(priv->rstc);
|
||||
|
||||
priv->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
if (!IS_ERR(priv->pinctrl)) {
|
||||
priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
|
||||
@@ -1030,10 +1032,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
if (ret)
|
||||
goto efree;
|
||||
|
||||
priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rstc))
|
||||
return PTR_ERR(priv->rstc);
|
||||
|
||||
ver = sd_ctrl_read16(host, CTL_VERSION);
|
||||
/* GEN2_SDR104 is first known SDHI to use 32bit block count */
|
||||
if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
|
||||
|
||||
@@ -170,6 +170,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400_one_rx = {
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
|
||||
.hs400_4taps = true,
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
||||
@@ -182,25 +183,30 @@ static const struct renesas_sdhi_quirks sdhi_quirks_fixed_addr = {
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = {
|
||||
.hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7),
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = {
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r8a7796_es13 = {
|
||||
.hs400_4taps = true,
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.hs400_calib_table = r8a7796_es13_calib_table,
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r8a77965 = {
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.hs400_calib_table = r8a77965_calib_table,
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = {
|
||||
.hs400_calib_table = r8a77990_calib_table,
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -268,6 +274,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);
|
||||
@@ -321,7 +328,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
|
||||
}
|
||||
|
||||
/*
|
||||
* renesas_sdhi_internal_dmac_map() will be called with two difference
|
||||
* renesas_sdhi_internal_dmac_map() will be called with two different
|
||||
* sg pointers in two mmc_data by .pre_req(), but tmio host can have a single
|
||||
* sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg
|
||||
* pointer in a mmc_data instead of host->sg_ptr.
|
||||
@@ -355,7 +362,7 @@ renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host,
|
||||
|
||||
data->host_cookie = cookie;
|
||||
|
||||
/* This DMAC cannot handle if buffer is not 128-bytes alignment */
|
||||
/* This DMAC needs buffers to be 128-byte aligned */
|
||||
if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) {
|
||||
renesas_sdhi_internal_dmac_unmap(host, data, cookie);
|
||||
return false;
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
struct sdhci_brcmstb_priv {
|
||||
void __iomem *cfg_regs;
|
||||
unsigned int flags;
|
||||
struct clk *base_clk;
|
||||
u32 base_freq_hz;
|
||||
};
|
||||
|
||||
struct brcmstb_match_priv {
|
||||
@@ -250,9 +252,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
const struct of_device_id *match;
|
||||
struct sdhci_brcmstb_priv *priv;
|
||||
u32 actual_clock_mhz;
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
struct clk *clk;
|
||||
struct clk *base_clk = NULL;
|
||||
int res;
|
||||
|
||||
match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node);
|
||||
@@ -330,6 +334,35 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT)
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
/* Change the base clock frequency if the DT property exists */
|
||||
if (device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&priv->base_freq_hz) != 0)
|
||||
goto add_host;
|
||||
|
||||
base_clk = devm_clk_get_optional(&pdev->dev, "sdio_freq");
|
||||
if (IS_ERR(base_clk)) {
|
||||
dev_warn(&pdev->dev, "Clock for \"sdio_freq\" not found\n");
|
||||
goto add_host;
|
||||
}
|
||||
|
||||
res = clk_prepare_enable(base_clk);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
/* set improved clock rate */
|
||||
clk_set_rate(base_clk, priv->base_freq_hz);
|
||||
actual_clock_mhz = clk_get_rate(base_clk) / 1000000;
|
||||
|
||||
host->caps &= ~SDHCI_CLOCK_V3_BASE_MASK;
|
||||
host->caps |= (actual_clock_mhz << SDHCI_CLOCK_BASE_SHIFT);
|
||||
/* Disable presets because they are now incorrect */
|
||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||
|
||||
dev_dbg(&pdev->dev, "Base Clock Frequency changed to %dMHz\n",
|
||||
actual_clock_mhz);
|
||||
priv->base_clk = base_clk;
|
||||
|
||||
add_host:
|
||||
res = sdhci_brcmstb_add_host(host, priv);
|
||||
if (res)
|
||||
goto err;
|
||||
@@ -340,6 +373,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
err:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_clk:
|
||||
clk_disable_unprepare(base_clk);
|
||||
clk_disable_unprepare(clk);
|
||||
return res;
|
||||
}
|
||||
@@ -351,11 +385,51 @@ static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_brcmstb_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
clk_disable_unprepare(priv->base_clk);
|
||||
return sdhci_pltfm_suspend(dev);
|
||||
}
|
||||
|
||||
static int sdhci_brcmstb_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_resume(dev);
|
||||
if (!ret && priv->base_freq_hz) {
|
||||
ret = clk_prepare_enable(priv->base_clk);
|
||||
/*
|
||||
* Note: using clk_get_rate() below as clk_get_rate()
|
||||
* honors CLK_GET_RATE_NOCACHE attribute, but clk_set_rate()
|
||||
* may do implicit get_rate() calls that do not honor
|
||||
* CLK_GET_RATE_NOCACHE.
|
||||
*/
|
||||
if (!ret &&
|
||||
(clk_get_rate(priv->base_clk) != priv->base_freq_hz))
|
||||
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_brcmstb_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_brcmstb_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-brcmstb",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
.pm = &sdhci_brcmstb_pmops,
|
||||
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
|
||||
},
|
||||
.probe = sdhci_brcmstb_probe,
|
||||
|
||||
@@ -2435,33 +2435,12 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = {
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
/* Following two entries are deprecated (kept only for backward compatibility) */
|
||||
/*
|
||||
* Do not add new variants to the driver which are compatible with
|
||||
* generic ones, unless they need customization.
|
||||
*/
|
||||
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||
/* Add entries for sdcc versions less than 5.0 here */
|
||||
{.compatible = "qcom,apq8084-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8226-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8916-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8953-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8974-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8992-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8994-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8996-sdhci", .data = &sdhci_msm_mci_var},
|
||||
/*
|
||||
* Add entries for sdcc version 5.0 here. For SDCC version 5.0.0,
|
||||
* MCI registers are removed from SDCC interface and some registers
|
||||
* are moved to HC.
|
||||
*/
|
||||
{.compatible = "qcom,qcs404-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdx55-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdx65-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdm630-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm6125-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm6350-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm8150-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm8250-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sc7280-sdhci", .data = &sdhci_msm_v5_var},
|
||||
/* Add entries where soc specific handling is required, here */
|
||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||
{.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var},
|
||||
{},
|
||||
|
||||
@@ -1733,7 +1733,6 @@ err_pltfm_free:
|
||||
|
||||
static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
@@ -1747,11 +1746,11 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
|
||||
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(clk_ahb);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_arasan_driver = {
|
||||
|
||||
@@ -100,8 +100,13 @@ static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
if (timing == MMC_TIMING_MMC_DDR52)
|
||||
sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R);
|
||||
u8 mc1r;
|
||||
|
||||
if (timing == MMC_TIMING_MMC_DDR52) {
|
||||
mc1r = sdhci_readb(host, SDMMC_MC1R);
|
||||
mc1r |= SDMMC_MC1R_DDR;
|
||||
sdhci_writeb(host, mc1r, SDMMC_MC1R);
|
||||
}
|
||||
sdhci_set_uhs_signaling(host, timing);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
/* Offset inside the vendor area 1 */
|
||||
#define DWCMSHC_HOST_CTRL3 0x8
|
||||
#define DWCMSHC_EMMC_CONTROL 0x2c
|
||||
#define DWCMSHC_CARD_IS_EMMC BIT(0)
|
||||
#define DWCMSHC_ENHANCED_STROBE BIT(8)
|
||||
#define DWCMSHC_EMMC_ATCTRL 0x40
|
||||
|
||||
@@ -38,7 +40,7 @@
|
||||
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
|
||||
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
|
||||
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
|
||||
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
|
||||
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
|
||||
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
|
||||
#define DWCMSHC_EMMC_DLL_START BIT(0)
|
||||
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
|
||||
@@ -47,22 +49,39 @@
|
||||
#define DWCMSHC_EMMC_DLL_START_POINT 16
|
||||
#define DWCMSHC_EMMC_DLL_INC 8
|
||||
#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
|
||||
#define DLL_TXCLK_TAPNUM_DEFAULT 0x8
|
||||
#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
|
||||
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
|
||||
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
|
||||
#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
|
||||
#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
|
||||
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
|
||||
#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
|
||||
#define DLL_STRBIN_DELAY_NUM_OFFSET 16
|
||||
#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16
|
||||
#define DLL_RXCLK_NO_INVERTER 1
|
||||
#define DLL_RXCLK_INVERTER 0
|
||||
#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
|
||||
#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
|
||||
#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
|
||||
#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
|
||||
|
||||
#define DLL_LOCK_WO_TMOUT(x) \
|
||||
((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
|
||||
(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
|
||||
#define RK3568_MAX_CLKS 3
|
||||
#define RK35xx_MAX_CLKS 3
|
||||
|
||||
#define BOUNDARY_OK(addr, len) \
|
||||
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
|
||||
|
||||
struct rk3568_priv {
|
||||
enum dwcmshc_rk_type {
|
||||
DWCMSHC_RK3568,
|
||||
DWCMSHC_RK3588,
|
||||
};
|
||||
|
||||
struct rk35xx_priv {
|
||||
/* Rockchip specified optional clocks */
|
||||
struct clk_bulk_data rockchip_clks[RK3568_MAX_CLKS];
|
||||
struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
|
||||
struct reset_control *reset;
|
||||
enum dwcmshc_rk_type devtype;
|
||||
u8 txclk_tapnum;
|
||||
};
|
||||
|
||||
@@ -131,7 +150,9 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ctrl, ctrl_2;
|
||||
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
/* Select Bus Speed Mode for host */
|
||||
@@ -149,8 +170,15 @@ static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
|
||||
else if ((timing == MMC_TIMING_UHS_DDR50) ||
|
||||
(timing == MMC_TIMING_MMC_DDR52))
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
||||
else if (timing == MMC_TIMING_MMC_HS400)
|
||||
else if (timing == MMC_TIMING_MMC_HS400) {
|
||||
/* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */
|
||||
ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
ctrl |= DWCMSHC_CARD_IS_EMMC;
|
||||
sdhci_writew(host, ctrl, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
|
||||
ctrl_2 |= DWCMSHC_CTRL_HS400;
|
||||
}
|
||||
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
@@ -176,24 +204,18 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk3568_priv *priv = dwc_priv->priv;
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
|
||||
u32 extra, reg;
|
||||
int err;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
/*
|
||||
* DO NOT TOUCH THIS SETTING. RX clk inverter unit is enabled
|
||||
* by default, but it shouldn't be enabled. We should anyway
|
||||
* disable it before issuing any cmds.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
|
||||
if (clock == 0)
|
||||
if (clock == 0) {
|
||||
/* Disable interface clock at initial state. */
|
||||
sdhci_set_clock(host, clock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Rockchip platform only support 375KHz for identify mode */
|
||||
if (clock <= 400000)
|
||||
@@ -211,9 +233,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
extra &= ~BIT(0);
|
||||
sdhci_writel(host, extra, reg);
|
||||
|
||||
if (clock <= 400000) {
|
||||
/* Disable DLL to reset sample clock */
|
||||
if (clock <= 52000000) {
|
||||
/* Disable DLL and reset both of sample and drive clock */
|
||||
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
|
||||
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
|
||||
sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
|
||||
/*
|
||||
* Before switching to hs400es mode, the driver will enable
|
||||
* enhanced strobe first. PHY needs to configure the parameters
|
||||
* of enhanced strobe first.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_STRBIN_DELAY_NUM_SEL |
|
||||
DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -222,6 +256,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
udelay(1);
|
||||
sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL);
|
||||
|
||||
/*
|
||||
* We shouldn't set DLL_RXCLK_NO_INVERTER for identify mode but
|
||||
* we must set it in higher speed mode.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA;
|
||||
if (priv->devtype == DWCMSHC_RK3568)
|
||||
extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
|
||||
/* Init DLL settings */
|
||||
extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
|
||||
0x2 << DWCMSHC_EMMC_DLL_INC |
|
||||
@@ -244,8 +287,20 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
|
||||
txclk_tapnum = priv->txclk_tapnum;
|
||||
|
||||
if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES;
|
||||
|
||||
extra = DLL_CMDOUT_SRC_CLK_NEG |
|
||||
DLL_CMDOUT_EN_SRC_CLK_NEG |
|
||||
DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_CMDOUT_TAPNUM_90_DEGREES |
|
||||
DLL_CMDOUT_TAPNUM_FROM_SW;
|
||||
sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT);
|
||||
}
|
||||
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_TXCLK_TAPNUM_FROM_SW |
|
||||
DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL |
|
||||
txclk_tapnum;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);
|
||||
|
||||
@@ -255,6 +310,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
|
||||
}
|
||||
|
||||
static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
|
||||
if (mask & SDHCI_RESET_ALL && priv->reset) {
|
||||
reset_control_assert(priv->reset);
|
||||
udelay(1);
|
||||
reset_control_deassert(priv->reset);
|
||||
}
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
@@ -264,12 +334,12 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_rk3568_ops = {
|
||||
static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
|
||||
.set_clock = dwcmshc_rk3568_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.reset = sdhci_reset,
|
||||
.reset = rk35xx_sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
};
|
||||
|
||||
@@ -279,30 +349,37 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_dwcmshc_rk3568_pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk3568_ops,
|
||||
static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
};
|
||||
|
||||
static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
int err;
|
||||
struct rk3568_priv *priv = dwc_priv->priv;
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
|
||||
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
|
||||
if (IS_ERR(priv->reset)) {
|
||||
err = PTR_ERR(priv->reset);
|
||||
dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->rockchip_clks[0].id = "axi";
|
||||
priv->rockchip_clks[1].id = "block";
|
||||
priv->rockchip_clks[2].id = "timer";
|
||||
err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK3568_MAX_CLKS,
|
||||
err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS,
|
||||
priv->rockchip_clks);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_bulk_prepare_enable(RK3568_MAX_CLKS, priv->rockchip_clks);
|
||||
err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
|
||||
return err;
|
||||
@@ -321,10 +398,28 @@ static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
/*
|
||||
* Don't support highspeed bus mode with low clk speed as we
|
||||
* cannot use DLL for this condition.
|
||||
*/
|
||||
if (host->mmc->f_max <= 52000000) {
|
||||
dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n",
|
||||
host->mmc->f_max);
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
|
||||
host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3588-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_rk35xx_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3568-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_rk3568_pdata,
|
||||
.data = &sdhci_dwcmshc_rk35xx_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "snps,dwcmshc-sdhci",
|
||||
@@ -347,7 +442,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_host *host;
|
||||
struct dwcmshc_priv *priv;
|
||||
struct rk3568_priv *rk_priv = NULL;
|
||||
struct rk35xx_priv *rk_priv = NULL;
|
||||
const struct sdhci_pltfm_data *pltfm_data;
|
||||
int err;
|
||||
u32 extra;
|
||||
@@ -402,33 +497,47 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
host->mmc_host_ops.request = dwcmshc_request;
|
||||
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
|
||||
|
||||
if (pltfm_data == &sdhci_dwcmshc_rk3568_pdata) {
|
||||
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk3568_priv), GFP_KERNEL);
|
||||
if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) {
|
||||
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
|
||||
if (!rk_priv) {
|
||||
err = -ENOMEM;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "rockchip,rk3588-dwcmshc"))
|
||||
rk_priv->devtype = DWCMSHC_RK3588;
|
||||
else
|
||||
rk_priv->devtype = DWCMSHC_RK3568;
|
||||
|
||||
priv->priv = rk_priv;
|
||||
|
||||
err = dwcmshc_rk3568_init(host, priv);
|
||||
err = dwcmshc_rk35xx_init(host, priv);
|
||||
if (err)
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
err = sdhci_setup_host(host);
|
||||
if (err)
|
||||
goto err_clk;
|
||||
|
||||
if (rk_priv)
|
||||
dwcmshc_rk35xx_postinit(host, priv);
|
||||
|
||||
err = __sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_setup_host;
|
||||
|
||||
return 0;
|
||||
|
||||
err_setup_host:
|
||||
sdhci_cleanup_host(host);
|
||||
err_clk:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
if (rk_priv)
|
||||
clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
|
||||
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
free_pltfm:
|
||||
sdhci_pltfm_free(pdev);
|
||||
@@ -440,14 +549,14 @@ static int dwcmshc_remove(struct platform_device *pdev)
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk3568_priv *rk_priv = priv->priv;
|
||||
struct rk35xx_priv *rk_priv = priv->priv;
|
||||
|
||||
sdhci_remove_host(host, 0);
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
if (rk_priv)
|
||||
clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
|
||||
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
@@ -460,7 +569,7 @@ static int dwcmshc_suspend(struct device *dev)
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk3568_priv *rk_priv = priv->priv;
|
||||
struct rk35xx_priv *rk_priv = priv->priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
@@ -472,7 +581,7 @@ static int dwcmshc_suspend(struct device *dev)
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
|
||||
if (rk_priv)
|
||||
clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
|
||||
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
|
||||
return ret;
|
||||
@@ -483,7 +592,7 @@ static int dwcmshc_resume(struct device *dev)
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk3568_priv *rk_priv = priv->priv;
|
||||
struct rk35xx_priv *rk_priv = priv->priv;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
@@ -497,7 +606,7 @@ static int dwcmshc_resume(struct device *dev)
|
||||
}
|
||||
|
||||
if (rk_priv) {
|
||||
ret = clk_bulk_prepare_enable(RK3568_MAX_CLKS,
|
||||
ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -904,6 +904,7 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc,
|
||||
scfg_node = of_find_matching_node(NULL, scfg_device_ids);
|
||||
if (scfg_node)
|
||||
scfg_base = of_iomap(scfg_node, 0);
|
||||
of_node_put(scfg_node);
|
||||
if (scfg_base) {
|
||||
sdhciovselcr = SDHCIOVSELCR_TGLEN |
|
||||
SDHCIOVSELCR_VSELVAL;
|
||||
@@ -1418,7 +1419,7 @@ static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc)
|
||||
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct device_node *np;
|
||||
struct device_node *np, *tp;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_esdhc *esdhc;
|
||||
int ret;
|
||||
@@ -1463,7 +1464,9 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
if (esdhc->vendor_ver > VENDOR_V_22)
|
||||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
|
||||
if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) {
|
||||
tp = of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc");
|
||||
if (tp) {
|
||||
of_node_put(tp);
|
||||
host->quirks |= SDHCI_QUIRK_RESET_AFTER_REQUEST;
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,9 @@
|
||||
#define PCIE_GLI_9763E_SCR 0x8E0
|
||||
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
||||
|
||||
#define PCIE_GLI_9763E_CFG 0x8A0
|
||||
#define GLI_9763E_CFG_LPSN_DIS BIT(12)
|
||||
|
||||
#define PCIE_GLI_9763E_CFG2 0x8A4
|
||||
#define GLI_9763E_CFG2_L1DLY GENMASK(28, 19)
|
||||
#define GLI_9763E_CFG2_L1DLY_MID 0x54
|
||||
@@ -963,12 +966,40 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot, bool enable)
|
||||
{
|
||||
struct pci_dev *pdev = slot->chip->pdev;
|
||||
u32 value;
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
||||
value &= ~GLI_9763E_VHS_REV;
|
||||
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG, &value);
|
||||
|
||||
if (enable)
|
||||
value &= ~GLI_9763E_CFG_LPSN_DIS;
|
||||
else
|
||||
value |= GLI_9763E_CFG_LPSN_DIS;
|
||||
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
||||
value &= ~GLI_9763E_VHS_REV;
|
||||
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
|
||||
}
|
||||
|
||||
static int gl9763e_runtime_suspend(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = chip->slots[0];
|
||||
struct sdhci_host *host = slot->host;
|
||||
u16 clock;
|
||||
|
||||
/* Enable LPM negotiation to allow entering L1 state */
|
||||
gl9763e_set_low_power_negotiation(slot, true);
|
||||
|
||||
clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clock &= ~(SDHCI_CLOCK_PLL_EN | SDHCI_CLOCK_CARD_EN);
|
||||
sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
|
||||
@@ -1002,6 +1033,9 @@ static int gl9763e_runtime_resume(struct sdhci_pci_chip *chip)
|
||||
clock |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
/* Disable LPM negotiation to avoid entering L1 state. */
|
||||
gl9763e_set_low_power_negotiation(slot, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -440,15 +440,14 @@ static int sdhci_st_remove(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
struct reset_control *rstc = pdata->rstc;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(pdata->icnclk);
|
||||
|
||||
reset_control_assert(rstc);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
@@ -224,6 +224,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
if (timedout) {
|
||||
pr_err("%s: Reset 0x%x never completed.\n",
|
||||
mmc_hostname(host->mmc), (int)mask);
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
@@ -1716,6 +1717,7 @@ static bool sdhci_send_command_retry(struct sdhci_host *host,
|
||||
if (!timeout--) {
|
||||
pr_err("%s: Controller never released inhibit bit(s).\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
cmd->error = -EIO;
|
||||
return false;
|
||||
@@ -1965,6 +1967,7 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
@@ -1987,6 +1990,7 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
|
||||
if (timedout) {
|
||||
pr_err("%s: PLL clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
@@ -3161,6 +3165,7 @@ static void sdhci_timeout_timer(struct timer_list *t)
|
||||
if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
|
||||
pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, REQ_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
@@ -3183,6 +3188,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
|
||||
(host->cmd && sdhci_data_line_cmd(host->cmd))) {
|
||||
pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, REQ_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
|
||||
if (host->data) {
|
||||
@@ -3234,17 +3240,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
||||
return;
|
||||
pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n",
|
||||
mmc_hostname(host->mmc), (unsigned)intmask);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
|
||||
if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
|
||||
SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) {
|
||||
if (intmask & SDHCI_INT_TIMEOUT)
|
||||
if (intmask & SDHCI_INT_TIMEOUT) {
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
else
|
||||
sdhci_err_stats_inc(host, CMD_TIMEOUT);
|
||||
} else {
|
||||
host->cmd->error = -EILSEQ;
|
||||
|
||||
if (!mmc_op_tuning(host->cmd->opcode))
|
||||
sdhci_err_stats_inc(host, CMD_CRC);
|
||||
}
|
||||
/* Treat data command CRC error the same as data CRC error */
|
||||
if (host->cmd->data &&
|
||||
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
||||
@@ -3266,6 +3276,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
||||
-ETIMEDOUT :
|
||||
-EILSEQ;
|
||||
|
||||
sdhci_err_stats_inc(host, AUTO_CMD);
|
||||
|
||||
if (sdhci_auto_cmd23(host, mrq)) {
|
||||
mrq->sbc->error = err;
|
||||
__sdhci_finish_mrq(host, mrq);
|
||||
@@ -3342,6 +3354,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
||||
host->data_cmd = NULL;
|
||||
data_cmd->error = -ETIMEDOUT;
|
||||
sdhci_err_stats_inc(host, CMD_TIMEOUT);
|
||||
__sdhci_finish_mrq(host, data_cmd->mrq);
|
||||
return;
|
||||
}
|
||||
@@ -3370,23 +3383,30 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||
|
||||
pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
|
||||
mmc_hostname(host->mmc), (unsigned)intmask);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (intmask & SDHCI_INT_DATA_TIMEOUT)
|
||||
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
||||
host->data->error = -ETIMEDOUT;
|
||||
else if (intmask & SDHCI_INT_DATA_END_BIT)
|
||||
sdhci_err_stats_inc(host, DAT_TIMEOUT);
|
||||
} else if (intmask & SDHCI_INT_DATA_END_BIT) {
|
||||
host->data->error = -EILSEQ;
|
||||
else if ((intmask & SDHCI_INT_DATA_CRC) &&
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if ((intmask & SDHCI_INT_DATA_CRC) &&
|
||||
SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
|
||||
!= MMC_BUS_TEST_R)
|
||||
!= MMC_BUS_TEST_R) {
|
||||
host->data->error = -EILSEQ;
|
||||
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
pr_err("%s: ADMA error: 0x%08x\n", mmc_hostname(host->mmc),
|
||||
intmask);
|
||||
sdhci_adma_show_error(host);
|
||||
sdhci_err_stats_inc(host, ADMA);
|
||||
host->data->error = -EIO;
|
||||
if (host->ops->adma_workaround)
|
||||
host->ops->adma_workaround(host, intmask);
|
||||
@@ -3584,6 +3604,7 @@ out:
|
||||
if (unexpected) {
|
||||
pr_err("%s: Unexpected interrupt 0x%08x.\n",
|
||||
mmc_hostname(host->mmc), unexpected);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
}
|
||||
|
||||
@@ -3905,20 +3926,27 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
|
||||
if (!host->cqe_on)
|
||||
return false;
|
||||
|
||||
if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC))
|
||||
if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC)) {
|
||||
*cmd_error = -EILSEQ;
|
||||
else if (intmask & SDHCI_INT_TIMEOUT)
|
||||
if (!mmc_op_tuning(host->cmd->opcode))
|
||||
sdhci_err_stats_inc(host, CMD_CRC);
|
||||
} else if (intmask & SDHCI_INT_TIMEOUT) {
|
||||
*cmd_error = -ETIMEDOUT;
|
||||
else
|
||||
sdhci_err_stats_inc(host, CMD_TIMEOUT);
|
||||
} else
|
||||
*cmd_error = 0;
|
||||
|
||||
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
|
||||
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) {
|
||||
*data_error = -EILSEQ;
|
||||
else if (intmask & SDHCI_INT_DATA_TIMEOUT)
|
||||
if (!mmc_op_tuning(host->cmd->opcode))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
||||
*data_error = -ETIMEDOUT;
|
||||
else if (intmask & SDHCI_INT_ADMA_ERROR)
|
||||
sdhci_err_stats_inc(host, DAT_TIMEOUT);
|
||||
} else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
*data_error = -EIO;
|
||||
else
|
||||
sdhci_err_stats_inc(host, ADMA);
|
||||
} else
|
||||
*data_error = 0;
|
||||
|
||||
/* Clear selected interrupts. */
|
||||
@@ -3934,6 +3962,7 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
|
||||
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
|
||||
pr_err("%s: CQE: Unexpected interrupt 0x%08x.\n",
|
||||
mmc_hostname(host->mmc), intmask);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
}
|
||||
|
||||
|
||||
@@ -356,6 +356,9 @@ struct sdhci_adma2_64_desc {
|
||||
*/
|
||||
#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
|
||||
|
||||
#define sdhci_err_stats_inc(host, err_name) \
|
||||
mmc_debugfs_err_stats_inc((host)->mmc, MMC_ERR_##err_name)
|
||||
|
||||
enum sdhci_cookie {
|
||||
COOKIE_UNMAPPED,
|
||||
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
||||
|
||||
@@ -75,7 +75,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
|
||||
tmio_mmc_clk_start(host);
|
||||
}
|
||||
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
{
|
||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#define CTL_DMA_ENABLE 0xd8
|
||||
#define CTL_RESET_SD 0xe0
|
||||
#define CTL_VERSION 0xe2
|
||||
#define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
|
||||
|
||||
/* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
|
||||
#define TMIO_STOP_STP BIT(0)
|
||||
@@ -98,6 +99,9 @@
|
||||
/* Definitions for values the CTL_DMA_ENABLE register can take */
|
||||
#define DMA_ENABLE_DMASDRW BIT(1)
|
||||
|
||||
/* Definitions for values the CTL_SDIF_MODE register can take */
|
||||
#define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */
|
||||
|
||||
/* Define some IRQ masks */
|
||||
/* This is the mask used at reset by the chip */
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
@@ -181,7 +185,7 @@ struct tmio_mmc_host {
|
||||
int (*multi_io_quirk)(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size);
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
void (*reset)(struct tmio_mmc_host *host);
|
||||
void (*reset)(struct tmio_mmc_host *host, bool preserve);
|
||||
bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||
unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host);
|
||||
|
||||
@@ -179,8 +179,17 @@ static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
|
||||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, reg);
|
||||
}
|
||||
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
{
|
||||
u16 card_opt, clk_ctrl, sdif_mode;
|
||||
|
||||
if (preserve) {
|
||||
card_opt = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT);
|
||||
clk_ctrl = sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL);
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
sdif_mode = sd_ctrl_read16(host, CTL_SDIF_MODE);
|
||||
}
|
||||
|
||||
/* FIXME - should we set stop clock reg here */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
||||
usleep_range(10000, 11000);
|
||||
@@ -190,7 +199,7 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
tmio_mmc_abort_dma(host);
|
||||
|
||||
if (host->reset)
|
||||
host->reset(host);
|
||||
host->reset(host, preserve);
|
||||
|
||||
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all);
|
||||
host->sdcard_irq_mask = host->sdcard_irq_mask_all;
|
||||
@@ -206,6 +215,13 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
||||
}
|
||||
|
||||
if (preserve) {
|
||||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, card_opt);
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk_ctrl);
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
sd_ctrl_write16(host, CTL_SDIF_MODE, sdif_mode);
|
||||
}
|
||||
|
||||
if (host->mmc->card)
|
||||
mmc_retune_needed(host->mmc);
|
||||
}
|
||||
@@ -248,7 +264,7 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_reset(host, true);
|
||||
|
||||
/* Ready for new calls */
|
||||
host->mrq = NULL;
|
||||
@@ -961,7 +977,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
tmio_mmc_power_off(host);
|
||||
/* For R-Car Gen2+, we need to reset SDHI specific SCC */
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_reset(host, false);
|
||||
|
||||
host->set_clock(host, 0);
|
||||
break;
|
||||
@@ -1189,7 +1205,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
||||
_host->sdcard_irq_mask_all = TMIO_MASK_ALL;
|
||||
|
||||
_host->set_clock(_host, 0);
|
||||
tmio_mmc_reset(_host);
|
||||
tmio_mmc_reset(_host, false);
|
||||
|
||||
spin_lock_init(&_host->lock);
|
||||
mutex_init(&_host->ios_lock);
|
||||
@@ -1285,7 +1301,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
||||
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
tmio_mmc_clk_enable(host);
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_reset(host, false);
|
||||
|
||||
if (host->clk_cache)
|
||||
host->set_clock(host, host->clk_cache);
|
||||
|
||||
@@ -70,6 +70,16 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
|
||||
* struct sdma_peripheral_config - SDMA config for audio
|
||||
* @n_fifos_src: Number of FIFOs for recording
|
||||
* @n_fifos_dst: Number of FIFOs for playback
|
||||
* @stride_fifos_src: FIFO address stride for recording, 0 means all FIFOs are
|
||||
* continuous, 1 means 1 word stride between FIFOs. All stride
|
||||
* between FIFOs should be same.
|
||||
* @stride_fifos_dst: FIFO address stride for playback
|
||||
* @words_per_fifo: numbers of words per FIFO fetch/fill, 1 means
|
||||
* one channel per FIFO, 2 means 2 channels per FIFO..
|
||||
* If 'n_fifos_src = 4' and 'words_per_fifo = 2', it
|
||||
* means the first two words(channels) fetch from FIFO0
|
||||
* and then jump to FIFO1 for next two words, and so on
|
||||
* after the last FIFO3 fetched, roll back to FIFO0.
|
||||
* @sw_done: Use software done. Needed for PDM (micfil)
|
||||
*
|
||||
* Some i.MX Audio devices (SAI, micfil) have multiple successive FIFO
|
||||
@@ -82,6 +92,9 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
|
||||
struct sdma_peripheral_config {
|
||||
int n_fifos_src;
|
||||
int n_fifos_dst;
|
||||
int stride_fifos_src;
|
||||
int stride_fifos_dst;
|
||||
int words_per_fifo;
|
||||
bool sw_done;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ enum spi_transfer_cmd {
|
||||
* @clk_div: source clock divider
|
||||
* @clk_src: serial clock
|
||||
* @cmd: spi cmd
|
||||
* @fragmentation: keep CS assserted at end of sequence
|
||||
* @fragmentation: keep CS asserted at end of sequence
|
||||
* @cs: chip select toggle
|
||||
* @set_config: set peripheral config
|
||||
* @rx_len: receive length for buffer
|
||||
|
||||
@@ -50,7 +50,6 @@ enum dma_status {
|
||||
*/
|
||||
enum dma_transaction_type {
|
||||
DMA_MEMCPY,
|
||||
DMA_MEMCPY_SG,
|
||||
DMA_XOR,
|
||||
DMA_PQ,
|
||||
DMA_XOR_VAL,
|
||||
@@ -887,11 +886,6 @@ struct dma_device {
|
||||
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
|
||||
struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
|
||||
size_t len, unsigned long flags);
|
||||
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy_sg)(
|
||||
struct dma_chan *chan,
|
||||
struct scatterlist *dst_sg, unsigned int dst_nents,
|
||||
struct scatterlist *src_sg, unsigned int src_nents,
|
||||
unsigned long flags);
|
||||
struct dma_async_tx_descriptor *(*device_prep_dma_xor)(
|
||||
struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src,
|
||||
unsigned int src_cnt, size_t len, unsigned long flags);
|
||||
@@ -1060,20 +1054,6 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy(
|
||||
len, flags);
|
||||
}
|
||||
|
||||
static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy_sg(
|
||||
struct dma_chan *chan,
|
||||
struct scatterlist *dst_sg, unsigned int dst_nents,
|
||||
struct scatterlist *src_sg, unsigned int src_nents,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (!chan || !chan->device || !chan->device->device_prep_dma_memcpy_sg)
|
||||
return NULL;
|
||||
|
||||
return chan->device->device_prep_dma_memcpy_sg(chan, dst_sg, dst_nents,
|
||||
src_sg, src_nents,
|
||||
flags);
|
||||
}
|
||||
|
||||
static inline bool dmaengine_is_metadata_mode_supported(struct dma_chan *chan,
|
||||
enum dma_desc_metadata_mode mode)
|
||||
{
|
||||
|
||||
@@ -219,7 +219,8 @@ struct sdio_cccr {
|
||||
wide_bus:1,
|
||||
high_power:1,
|
||||
high_speed:1,
|
||||
disable_cd:1;
|
||||
disable_cd:1,
|
||||
enable_async_irq:1;
|
||||
};
|
||||
|
||||
struct sdio_cis {
|
||||
@@ -343,10 +344,16 @@ static inline bool mmc_large_sector(struct mmc_card *card)
|
||||
return card->ext_csd.data_sector_size == 4096;
|
||||
}
|
||||
|
||||
static inline int mmc_card_enable_async_irq(struct mmc_card *card)
|
||||
{
|
||||
return card->cccr.enable_async_irq;
|
||||
}
|
||||
|
||||
bool mmc_card_is_blockaddr(struct mmc_card *card);
|
||||
|
||||
#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
|
||||
#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
|
||||
#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
|
||||
#define mmc_card_sd_combo(c) ((c)->type == MMC_TYPE_SD_COMBO)
|
||||
|
||||
#endif /* LINUX_MMC_CARD_H */
|
||||
|
||||
@@ -93,6 +93,25 @@ struct mmc_clk_phase_map {
|
||||
|
||||
struct mmc_host;
|
||||
|
||||
enum mmc_err_stat {
|
||||
MMC_ERR_CMD_TIMEOUT,
|
||||
MMC_ERR_CMD_CRC,
|
||||
MMC_ERR_DAT_TIMEOUT,
|
||||
MMC_ERR_DAT_CRC,
|
||||
MMC_ERR_AUTO_CMD,
|
||||
MMC_ERR_ADMA,
|
||||
MMC_ERR_TUNING,
|
||||
MMC_ERR_CMDQ_RED,
|
||||
MMC_ERR_CMDQ_GCE,
|
||||
MMC_ERR_CMDQ_ICCE,
|
||||
MMC_ERR_REQ_TIMEOUT,
|
||||
MMC_ERR_CMDQ_REQ_TIMEOUT,
|
||||
MMC_ERR_ICE_CFG,
|
||||
MMC_ERR_CTRL_TIMEOUT,
|
||||
MMC_ERR_UNEXPECTED_IRQ,
|
||||
MMC_ERR_MAX,
|
||||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
/*
|
||||
* It is optional for the host to implement pre_req and post_req in
|
||||
@@ -501,6 +520,7 @@ struct mmc_host {
|
||||
/* Host Software Queue support */
|
||||
bool hsq_enabled;
|
||||
|
||||
u32 err_stats[MMC_ERR_MAX];
|
||||
unsigned long private[] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
@@ -635,6 +655,12 @@ static inline enum dma_data_direction mmc_get_dma_dir(struct mmc_data *data)
|
||||
return data->flags & MMC_DATA_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
||||
}
|
||||
|
||||
static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
|
||||
enum mmc_err_stat stat)
|
||||
{
|
||||
host->err_stats[stat] += 1;
|
||||
}
|
||||
|
||||
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
|
||||
int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode);
|
||||
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
|
||||
|
||||
@@ -99,6 +99,12 @@ static inline bool mmc_op_multi(u32 opcode)
|
||||
opcode == MMC_READ_MULTIPLE_BLOCK;
|
||||
}
|
||||
|
||||
static inline bool mmc_op_tuning(u32 opcode)
|
||||
{
|
||||
return opcode == MMC_SEND_TUNING_BLOCK ||
|
||||
opcode == MMC_SEND_TUNING_BLOCK_HS200;
|
||||
}
|
||||
|
||||
/*
|
||||
* MMC_SWITCH argument format:
|
||||
*
|
||||
|
||||
@@ -159,6 +159,11 @@
|
||||
#define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
|
||||
#define SDIO_CCCR_INTERRUPT_EXT 0x16
|
||||
#define SDIO_INTERRUPT_EXT_SAI (1 << 0)
|
||||
#define SDIO_INTERRUPT_EXT_EAI (1 << 1)
|
||||
|
||||
/*
|
||||
* Function Basic Registers (FBR)
|
||||
*/
|
||||
|
||||
@@ -89,14 +89,14 @@ enum iax_opcode {
|
||||
IAX_OPCODE_CRC64,
|
||||
IAX_OPCODE_ZERO_DECOMP_32 = 0x48,
|
||||
IAX_OPCODE_ZERO_DECOMP_16,
|
||||
IAX_OPCODE_DECOMP_32 = 0x4c,
|
||||
IAX_OPCODE_DECOMP_16,
|
||||
IAX_OPCODE_ZERO_COMP_32 = 0x4c,
|
||||
IAX_OPCODE_ZERO_COMP_16,
|
||||
IAX_OPCODE_SCAN = 0x50,
|
||||
IAX_OPCODE_SET_MEMBER,
|
||||
IAX_OPCODE_EXTRACT,
|
||||
IAX_OPCODE_SELECT,
|
||||
IAX_OPCODE_RLE_BURST,
|
||||
IAX_OPCDE_FIND_UNIQUE,
|
||||
IAX_OPCODE_FIND_UNIQUE,
|
||||
IAX_OPCODE_EXPAND,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user