mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 14:12:06 +00:00
Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma
Pull slave-dmaengine updates from Vinod Koul: - new drivers for: - Ingenic JZ4780 controller - APM X-Gene controller - Freescale RaidEngine device - Renesas USB Controller - remove device_alloc_chan_resources dummy handlers - sh driver cleanups for peri peri and related emmc and asoc patches as well - fixes and enhancements spread over the drivers * 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma: (59 commits) dmaengine: dw: don't prompt for DW_DMAC_CORE dmaengine: shdmac: avoid unused variable warnings dmaengine: fix platform_no_drv_owner.cocci warnings dmaengine: pch_dma: fix memory leak on failure path in pch_dma_probe() dmaengine: at_xdmac: unlock spin lock before return dmaengine: xgene: devm_ioremap() returns NULL on error dmaengine: xgene: buffer overflow in xgene_dma_init_channels() dmaengine: usb-dmac: Fix dereferencing freed memory 'desc' dmaengine: sa11x0: report slave capabilities to upper layers dmaengine: vdma: Fix compilation warnings dmaengine: fsl_raid: statify fsl_re_chan_probe dmaengine: Driver support for FSL RaidEngine device. dmaengine: xgene_dma_init_ring_mngr() can be static Documentation: dma: Add documentation for the APM X-Gene SoC DMA device DTS binding arm64: dts: Add APM X-Gene SoC DMA device and DMA clock DTS nodes dmaengine: Add support for APM X-Gene SoC DMA engine driver dmaengine: usb-dmac: Add Renesas USB DMA Controller (USB-DMAC) driver dmaengine: renesas,usb-dmac: Add device tree bindings documentation dmaengine: edma: fixed wrongly initialized data parameter to the edma callback dmaengine: ste_dma40: fix implicit conversion ...
This commit is contained in:
commit
d6a4c0e5d3
47
Documentation/devicetree/bindings/dma/apm-xgene-dma.txt
Normal file
47
Documentation/devicetree/bindings/dma/apm-xgene-dma.txt
Normal file
@ -0,0 +1,47 @@
|
||||
Applied Micro X-Gene SoC DMA nodes
|
||||
|
||||
DMA nodes are defined to describe on-chip DMA interfaces in
|
||||
APM X-Gene SoC.
|
||||
|
||||
Required properties for DMA interfaces:
|
||||
- compatible: Should be "apm,xgene-dma".
|
||||
- device_type: set to "dma".
|
||||
- reg: Address and length of the register set for the device.
|
||||
It contains the information of registers in the following order:
|
||||
1st - DMA control and status register address space.
|
||||
2nd - Descriptor ring control and status register address space.
|
||||
3rd - Descriptor ring command register address space.
|
||||
4th - Soc efuse register address space.
|
||||
- interrupts: DMA has 5 interrupts sources. 1st interrupt is
|
||||
DMA error reporting interrupt. 2nd, 3rd, 4th and 5th interrupts
|
||||
are completion interrupts for each DMA channels.
|
||||
- clocks: Reference to the clock entry.
|
||||
|
||||
Optional properties:
|
||||
- dma-coherent : Present if dma operations are coherent
|
||||
|
||||
Example:
|
||||
dmaclk: dmaclk@1f27c000 {
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f27c000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "dmaclk";
|
||||
};
|
||||
|
||||
dma: dma@1f270000 {
|
||||
compatible = "apm,xgene-storm-dma";
|
||||
device_type = "dma";
|
||||
reg = <0x0 0x1f270000 0x0 0x10000>,
|
||||
<0x0 0x1f200000 0x0 0x10000>,
|
||||
<0x0 0x1b008000 0x0 0x2000>,
|
||||
<0x0 0x1054a000 0x0 0x100>;
|
||||
interrupts = <0x0 0x82 0x4>,
|
||||
<0x0 0xb8 0x4>,
|
||||
<0x0 0xb9 0x4>,
|
||||
<0x0 0xba 0x4>,
|
||||
<0x0 0xbb 0x4>;
|
||||
dma-coherent;
|
||||
clocks = <&dmaclk 0>;
|
||||
};
|
56
Documentation/devicetree/bindings/dma/jz4780-dma.txt
Normal file
56
Documentation/devicetree/bindings/dma/jz4780-dma.txt
Normal file
@ -0,0 +1,56 @@
|
||||
* Ingenic JZ4780 DMA Controller
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be "ingenic,jz4780-dma"
|
||||
- reg: Should contain the DMA controller registers location and length.
|
||||
- interrupts: Should contain the interrupt specifier of the DMA controller.
|
||||
- interrupt-parent: Should be the phandle of the interrupt controller that
|
||||
- clocks: Should contain a clock specifier for the JZ4780 PDMA clock.
|
||||
- #dma-cells: Must be <2>. Number of integer cells in the dmas property of
|
||||
DMA clients (see below).
|
||||
|
||||
Optional properties:
|
||||
|
||||
- ingenic,reserved-channels: Bitmask of channels to reserve for devices that
|
||||
need a specific channel. These channels will only be assigned when explicitly
|
||||
requested by a client. The primary use for this is channels 0 and 1, which
|
||||
can be configured to have special behaviour for NAND/BCH when using
|
||||
programmable firmware.
|
||||
|
||||
Example:
|
||||
|
||||
dma: dma@13420000 {
|
||||
compatible = "ingenic,jz4780-dma";
|
||||
reg = <0x13420000 0x10000>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <10>;
|
||||
|
||||
clocks = <&cgu JZ4780_CLK_PDMA>;
|
||||
|
||||
#dma-cells = <2>;
|
||||
|
||||
ingenic,reserved-channels = <0x3>;
|
||||
};
|
||||
|
||||
DMA clients must use the format described in dma.txt, giving a phandle to the
|
||||
DMA controller plus the following 2 integer cells:
|
||||
|
||||
1. Request type: The DMA request type for transfers to/from the device on
|
||||
the allocated channel, as defined in the SoC documentation.
|
||||
|
||||
2. Channel: If set to 0xffffffff, any available channel will be allocated for
|
||||
the client. Otherwise, the exact channel specified will be used. The channel
|
||||
should be reserved on the DMA controller using the ingenic,reserved-channels
|
||||
property.
|
||||
|
||||
Example:
|
||||
|
||||
uart0: serial@10030000 {
|
||||
...
|
||||
dmas = <&dma 0x14 0xffffffff
|
||||
&dma 0x15 0xffffffff>;
|
||||
dma-names = "tx", "rx";
|
||||
...
|
||||
};
|
@ -4,6 +4,7 @@ Required properties:
|
||||
- compatible: must be one of the following:
|
||||
* "qcom,bam-v1.4.0" for MSM8974, APQ8074 and APQ8084
|
||||
* "qcom,bam-v1.3.0" for APQ8064, IPQ8064 and MSM8960
|
||||
* "qcom,bam-v1.7.0" for MSM8916
|
||||
- reg: Address range for DMA registers
|
||||
- interrupts: Should contain the one interrupt shared by all channels
|
||||
- #dma-cells: must be <1>, the cell in the dmas property of the client device
|
||||
|
@ -1,29 +0,0 @@
|
||||
* R-Car Audio DMAC peri peri Device Tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "renesas,rcar-audmapp"
|
||||
- #dma-cells: should be <1>, see "dmas" property below
|
||||
|
||||
Example:
|
||||
audmapp: audio-dma-pp@0xec740000 {
|
||||
compatible = "renesas,rcar-audmapp";
|
||||
#dma-cells = <1>;
|
||||
|
||||
reg = <0 0xec740000 0 0x200>;
|
||||
};
|
||||
|
||||
|
||||
* DMA client
|
||||
|
||||
Required properties:
|
||||
- dmas: a list of <[DMA multiplexer phandle] [SRS << 8 | DRS]> pairs.
|
||||
where SRS/DRS are specified in the SoC manual.
|
||||
It will be written into PDMACHCR as high 16-bit parts.
|
||||
- dma-names: a list of DMA channel names, one per "dmas" entry
|
||||
|
||||
Example:
|
||||
|
||||
dmas = <&audmapp 0x2d00
|
||||
&audmapp 0x3700>;
|
||||
dma-names = "src0_ssiu0",
|
||||
"dvc0_ssiu0";
|
37
Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
Normal file
37
Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
Normal file
@ -0,0 +1,37 @@
|
||||
* Renesas USB DMA Controller Device Tree bindings
|
||||
|
||||
Required Properties:
|
||||
- compatible: must contain "renesas,usb-dmac"
|
||||
- reg: base address and length of the registers block for the DMAC
|
||||
- interrupts: interrupt specifiers for the DMAC, one for each entry in
|
||||
interrupt-names.
|
||||
- interrupt-names: one entry per channel, named "ch%u", where %u is the
|
||||
channel number ranging from zero to the number of channels minus one.
|
||||
- clocks: a list of phandle + clock-specifier pairs.
|
||||
- #dma-cells: must be <1>, the cell specifies the channel number of the DMAC
|
||||
port connected to the DMA client.
|
||||
- dma-channels: number of DMA channels
|
||||
|
||||
Example: R8A7790 (R-Car H2) USB-DMACs
|
||||
|
||||
usb_dmac0: dma-controller@e65a0000 {
|
||||
compatible = "renesas,usb-dmac";
|
||||
reg = <0 0xe65a0000 0 0x100>;
|
||||
interrupts = <0 109 IRQ_TYPE_LEVEL_HIGH
|
||||
0 109 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "ch0", "ch1";
|
||||
clocks = <&mstp3_clks R8A7790_CLK_USBDMAC0>;
|
||||
#dma-cells = <1>;
|
||||
dma-channels = <2>;
|
||||
};
|
||||
|
||||
usb_dmac1: dma-controller@e65b0000 {
|
||||
compatible = "renesas,usb-dmac";
|
||||
reg = <0 0xe65b0000 0 0x100>;
|
||||
interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH
|
||||
0 110 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "ch0", "ch1";
|
||||
clocks = <&mstp3_clks R8A7790_CLK_USBDMAC1>;
|
||||
#dma-cells = <1>;
|
||||
dma-channels = <2>;
|
||||
};
|
@ -5009,6 +5009,11 @@ W: http://industrypack.sourceforge.net
|
||||
S: Maintained
|
||||
F: drivers/ipack/
|
||||
|
||||
INGENIC JZ4780 DMA Driver
|
||||
M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
|
||||
S: Maintained
|
||||
F: drivers/dma/dma-jz4780.c
|
||||
|
||||
INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
|
||||
M: Mimi Zohar <zohar@linux.vnet.ibm.com>
|
||||
M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
|
||||
|
@ -754,12 +754,12 @@ static struct platform_device vcc_sdhi1 = {
|
||||
};
|
||||
|
||||
/* SDHI0 */
|
||||
static struct sh_mobile_sdhi_info sdhi0_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
static struct tmio_mmc_data sdhi0_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_POWER_OFF_CARD,
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD,
|
||||
.flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD,
|
||||
.cd_gpio = 167,
|
||||
};
|
||||
|
||||
@ -796,12 +796,12 @@ static struct platform_device sdhi0_device = {
|
||||
};
|
||||
|
||||
/* SDHI1 */
|
||||
static struct sh_mobile_sdhi_info sdhi1_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI1_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
static struct tmio_mmc_data sdhi1_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_POWER_OFF_CARD,
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD,
|
||||
.flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_USE_GPIO_CD,
|
||||
/* Port72 cannot generate IRQs, will be used in polling mode. */
|
||||
.cd_gpio = 72,
|
||||
};
|
||||
|
@ -201,12 +201,12 @@ static struct rcar_phy_platform_data usb_phy_platform_data __initdata =
|
||||
|
||||
|
||||
/* SDHI */
|
||||
static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
|
||||
.dma_slave_tx = HPBDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = HPBDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED,
|
||||
.tmio_ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
|
||||
static struct tmio_mmc_data sdhi0_info __initdata = {
|
||||
.chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED,
|
||||
.ocr_mask = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
|
||||
.flags = TMIO_MMC_HAS_IDLE_WAIT,
|
||||
};
|
||||
|
||||
static struct resource sdhi0_resources[] __initdata = {
|
||||
@ -683,7 +683,7 @@ static void __init bockw_init(void)
|
||||
platform_device_register_resndata(
|
||||
NULL, "sh_mobile_sdhi", 0,
|
||||
sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
|
||||
&sdhi0_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
&sdhi0_info, sizeof(struct tmio_mmc_data));
|
||||
}
|
||||
|
||||
/* for Audio */
|
||||
|
@ -442,11 +442,11 @@ static struct platform_device vcc_sdhi2 = {
|
||||
};
|
||||
|
||||
/* SDHI */
|
||||
static struct sh_mobile_sdhi_info sdhi0_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
static struct tmio_mmc_data sdhi0_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||
.flags = TMIO_MMC_HAS_IDLE_WAIT,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_POWER_OFF_CARD,
|
||||
};
|
||||
|
||||
@ -484,13 +484,13 @@ static struct platform_device sdhi0_device = {
|
||||
};
|
||||
|
||||
/* Micro SD */
|
||||
static struct sh_mobile_sdhi_info sdhi2_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI2_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI2_RX,
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
|
||||
static struct tmio_mmc_data sdhi2_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI2_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI2_RX,
|
||||
.flags = TMIO_MMC_HAS_IDLE_WAIT |
|
||||
TMIO_MMC_USE_GPIO_CD |
|
||||
TMIO_MMC_WRPROTECT_DISABLE,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_POWER_OFF_CARD,
|
||||
.cd_gpio = 13,
|
||||
};
|
||||
|
||||
|
@ -122,11 +122,11 @@ static struct resource sdhi0_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sdhi0_platform_data = {
|
||||
.dma_slave_tx = HPBDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = HPBDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED,
|
||||
static struct tmio_mmc_data sdhi0_platform_data = {
|
||||
.chan_priv_tx = (void *)HPBDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)HPBDMA_SLAVE_SDHI0_RX,
|
||||
.flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED,
|
||||
};
|
||||
|
||||
static struct platform_device sdhi0_device = {
|
||||
|
@ -102,6 +102,7 @@
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
dma-ranges = <0x0 0x0 0x0 0x0 0x400 0x0>;
|
||||
|
||||
clocks {
|
||||
#address-cells = <2>;
|
||||
@ -362,6 +363,15 @@
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "pcie4clk";
|
||||
};
|
||||
|
||||
dmaclk: dmaclk@1f27c000 {
|
||||
compatible = "apm,xgene-device-clock";
|
||||
#clock-cells = <1>;
|
||||
clocks = <&socplldiv2 0>;
|
||||
reg = <0x0 0x1f27c000 0x0 0x1000>;
|
||||
reg-names = "csr-reg";
|
||||
clock-output-names = "dmaclk";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0: pcie@1f2b0000 {
|
||||
@ -684,5 +694,21 @@
|
||||
interrupts = <0x0 0x41 0x4>;
|
||||
clocks = <&rngpkaclk 0>;
|
||||
};
|
||||
|
||||
dma: dma@1f270000 {
|
||||
compatible = "apm,xgene-storm-dma";
|
||||
device_type = "dma";
|
||||
reg = <0x0 0x1f270000 0x0 0x10000>,
|
||||
<0x0 0x1f200000 0x0 0x10000>,
|
||||
<0x0 0x1b008000 0x0 0x2000>,
|
||||
<0x0 0x1054a000 0x0 0x100>;
|
||||
interrupts = <0x0 0x82 0x4>,
|
||||
<0x0 0xb8 0x4>,
|
||||
<0x0 0xb9 0x4>,
|
||||
<0x0 0xba 0x4>,
|
||||
<0x0 0xbb 0x4>;
|
||||
dma-coherent;
|
||||
clocks = <&dmaclk 0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sh_mmcif.h>
|
||||
#include <linux/mmc/sh_mobile_sdhi.h>
|
||||
@ -243,10 +244,10 @@ static struct platform_device sh_mmcif_device = {
|
||||
};
|
||||
|
||||
/* SDHI0 */
|
||||
static struct sh_mobile_sdhi_info sdhi_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI_RX,
|
||||
.tmio_caps = MMC_CAP_SD_HIGHSPEED,
|
||||
static struct tmio_mmc_data sdhi_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI_RX,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED,
|
||||
};
|
||||
|
||||
static struct resource sdhi_resources[] = {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/mmc/sh_mobile_sdhi.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/mtd/sh_flctl.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
@ -447,8 +448,8 @@ static struct resource sdhi0_cn3_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sdhi0_cn3_data = {
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ,
|
||||
static struct tmio_mmc_data sdhi0_cn3_data = {
|
||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static struct platform_device sdhi0_cn3_device = {
|
||||
@ -474,8 +475,8 @@ static struct resource sdhi1_cn7_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sdhi1_cn7_data = {
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ,
|
||||
static struct tmio_mmc_data sdhi1_cn7_data = {
|
||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static struct platform_device sdhi1_cn7_device = {
|
||||
|
@ -601,12 +601,12 @@ static struct platform_device sdhi0_power = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sdhi0_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
|
||||
static struct tmio_mmc_data sdhi0_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||
.capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
|
||||
MMC_CAP_NEEDS_POLL,
|
||||
.tmio_flags = TMIO_MMC_USE_GPIO_CD,
|
||||
.flags = TMIO_MMC_USE_GPIO_CD,
|
||||
.cd_gpio = GPIO_PTY7,
|
||||
};
|
||||
|
||||
@ -635,12 +635,12 @@ static struct platform_device sdhi0_device = {
|
||||
|
||||
#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
|
||||
/* SDHI1 */
|
||||
static struct sh_mobile_sdhi_info sdhi1_info = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI1_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
|
||||
static struct tmio_mmc_data sdhi1_info = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX,
|
||||
.capabilities = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
|
||||
MMC_CAP_NEEDS_POLL,
|
||||
.tmio_flags = TMIO_MMC_USE_GPIO_CD,
|
||||
.flags = TMIO_MMC_USE_GPIO_CD,
|
||||
.cd_gpio = GPIO_PTW7,
|
||||
};
|
||||
|
||||
|
@ -373,11 +373,11 @@ static struct resource kfr2r09_sh_sdhi0_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sh7724_sdhi0_data = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_flags = TMIO_MMC_WRPROTECT_DISABLE,
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ,
|
||||
static struct tmio_mmc_data sh7724_sdhi0_data = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||
.flags = TMIO_MMC_WRPROTECT_DISABLE,
|
||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static struct platform_device kfr2r09_sh_sdhi0_device = {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sh_mobile_sdhi.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
@ -408,10 +409,10 @@ static struct resource sdhi_cn9_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sh7724_sdhi_data = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ,
|
||||
static struct tmio_mmc_data sh7724_sdhi_data = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static struct platform_device sdhi_cn9_device = {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sh_mobile_sdhi.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
@ -468,10 +469,10 @@ static struct resource sdhi0_cn7_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sh7724_sdhi0_data = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ,
|
||||
static struct tmio_mmc_data sh7724_sdhi0_data = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static struct platform_device sdhi0_cn7_device = {
|
||||
@ -497,10 +498,10 @@ static struct resource sdhi1_cn8_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct sh_mobile_sdhi_info sh7724_sdhi1_data = {
|
||||
.dma_slave_tx = SHDMA_SLAVE_SDHI1_TX,
|
||||
.dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
|
||||
.tmio_caps = MMC_CAP_SDIO_IRQ,
|
||||
static struct tmio_mmc_data sh7724_sdhi1_data = {
|
||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI1_TX,
|
||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI1_RX,
|
||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||
};
|
||||
|
||||
static struct platform_device sdhi1_cn8_device = {
|
||||
|
@ -112,6 +112,17 @@ config FSL_DMA
|
||||
EloPlus is on mpc85xx and mpc86xx and Pxxx parts, and the Elo3 is on
|
||||
some Txxx and Bxxx parts.
|
||||
|
||||
config FSL_RAID
|
||||
tristate "Freescale RAID engine Support"
|
||||
depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH
|
||||
select DMA_ENGINE
|
||||
select DMA_ENGINE_RAID
|
||||
---help---
|
||||
Enable support for Freescale RAID Engine. RAID Engine is
|
||||
available on some QorIQ SoCs (like P5020/P5040). It has
|
||||
the capability to offload memcpy, xor and pq computation
|
||||
for raid5/6.
|
||||
|
||||
source "drivers/dma/hsu/Kconfig"
|
||||
|
||||
config MPC512X_DMA
|
||||
@ -347,6 +358,16 @@ config DMA_JZ4740
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
|
||||
config DMA_JZ4780
|
||||
tristate "JZ4780 DMA support"
|
||||
depends on MACH_JZ4780
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
help
|
||||
This selects support for the DMA controller in Ingenic JZ4780 SoCs.
|
||||
If you have a board based on such a SoC and wish to use DMA for
|
||||
devices which can use the DMA controller, say Y or M here.
|
||||
|
||||
config K3_DMA
|
||||
tristate "Hisilicon K3 DMA support"
|
||||
depends on ARCH_HI3xxx
|
||||
@ -414,6 +435,14 @@ config IMG_MDC_DMA
|
||||
help
|
||||
Enable support for the IMG multi-threaded DMA controller (MDC).
|
||||
|
||||
config XGENE_DMA
|
||||
tristate "APM X-Gene DMA support"
|
||||
select DMA_ENGINE
|
||||
select DMA_ENGINE_RAID
|
||||
select ASYNC_TX_ENABLE_CHANNEL_SWITCH
|
||||
help
|
||||
Enable support for the APM X-Gene SoC DMA engine.
|
||||
|
||||
config DMA_ENGINE
|
||||
bool
|
||||
|
||||
|
@ -41,9 +41,11 @@ obj-$(CONFIG_DMA_OMAP) += omap-dma.o
|
||||
obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
|
||||
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
|
||||
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
|
||||
obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o
|
||||
obj-$(CONFIG_TI_CPPI41) += cppi41.o
|
||||
obj-$(CONFIG_K3_DMA) += k3dma.o
|
||||
obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
|
||||
obj-$(CONFIG_FSL_RAID) += fsl_raid.o
|
||||
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
|
||||
obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
|
||||
obj-y += xilinx/
|
||||
@ -51,3 +53,4 @@ obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o
|
||||
obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o
|
||||
obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
|
||||
obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
|
||||
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
|
||||
|
@ -15,10 +15,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is in this distribution in the file
|
||||
* called COPYING.
|
||||
*
|
||||
@ -1195,11 +1191,6 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
|
||||
/*
|
||||
* The DMA ENGINE API
|
||||
*/
|
||||
static int pl08x_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pl08x_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
/* Ensure all queued descriptors are freed */
|
||||
@ -2066,7 +2057,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
/* Initialize memcpy engine */
|
||||
dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
|
||||
pl08x->memcpy.dev = &adev->dev;
|
||||
pl08x->memcpy.device_alloc_chan_resources = pl08x_alloc_chan_resources;
|
||||
pl08x->memcpy.device_free_chan_resources = pl08x_free_chan_resources;
|
||||
pl08x->memcpy.device_prep_dma_memcpy = pl08x_prep_dma_memcpy;
|
||||
pl08x->memcpy.device_prep_dma_interrupt = pl08x_prep_dma_interrupt;
|
||||
@ -2085,7 +2075,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask);
|
||||
dma_cap_set(DMA_CYCLIC, pl08x->slave.cap_mask);
|
||||
pl08x->slave.dev = &adev->dev;
|
||||
pl08x->slave.device_alloc_chan_resources = pl08x_alloc_chan_resources;
|
||||
pl08x->slave.device_free_chan_resources = pl08x_free_chan_resources;
|
||||
pl08x->slave.device_prep_dma_interrupt = pl08x_prep_dma_interrupt;
|
||||
pl08x->slave.device_tx_status = pl08x_dma_tx_status;
|
||||
|
@ -65,6 +65,21 @@ static void atc_issue_pending(struct dma_chan *chan);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static inline unsigned int atc_get_xfer_width(dma_addr_t src, dma_addr_t dst,
|
||||
size_t len)
|
||||
{
|
||||
unsigned int width;
|
||||
|
||||
if (!((src | dst | len) & 3))
|
||||
width = 2;
|
||||
else if (!((src | dst | len) & 1))
|
||||
width = 1;
|
||||
else
|
||||
width = 0;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static struct at_desc *atc_first_active(struct at_dma_chan *atchan)
|
||||
{
|
||||
return list_first_entry(&atchan->active_list,
|
||||
@ -659,16 +674,10 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
||||
* We can be a lot more clever here, but this should take care
|
||||
* of the most common optimization.
|
||||
*/
|
||||
if (!((src | dest | len) & 3)) {
|
||||
ctrla = ATC_SRC_WIDTH_WORD | ATC_DST_WIDTH_WORD;
|
||||
src_width = dst_width = 2;
|
||||
} else if (!((src | dest | len) & 1)) {
|
||||
ctrla = ATC_SRC_WIDTH_HALFWORD | ATC_DST_WIDTH_HALFWORD;
|
||||
src_width = dst_width = 1;
|
||||
} else {
|
||||
ctrla = ATC_SRC_WIDTH_BYTE | ATC_DST_WIDTH_BYTE;
|
||||
src_width = dst_width = 0;
|
||||
}
|
||||
src_width = dst_width = atc_get_xfer_width(src, dest, len);
|
||||
|
||||
ctrla = ATC_SRC_WIDTH(src_width) |
|
||||
ATC_DST_WIDTH(dst_width);
|
||||
|
||||
for (offset = 0; offset < len; offset += xfer_count << src_width) {
|
||||
xfer_count = min_t(size_t, (len - offset) >> src_width,
|
||||
@ -861,6 +870,144 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* atc_prep_dma_sg - prepare memory to memory scather-gather operation
|
||||
* @chan: the channel to prepare operation on
|
||||
* @dst_sg: destination scatterlist
|
||||
* @dst_nents: number of destination scatterlist entries
|
||||
* @src_sg: source scatterlist
|
||||
* @src_nents: number of source scatterlist entries
|
||||
* @flags: tx descriptor status flags
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *
|
||||
atc_prep_dma_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 at_dma_chan *atchan = to_at_dma_chan(chan);
|
||||
struct at_desc *desc = NULL;
|
||||
struct at_desc *first = NULL;
|
||||
struct at_desc *prev = NULL;
|
||||
unsigned int src_width;
|
||||
unsigned int dst_width;
|
||||
size_t xfer_count;
|
||||
u32 ctrla;
|
||||
u32 ctrlb;
|
||||
size_t dst_len = 0, src_len = 0;
|
||||
dma_addr_t dst = 0, src = 0;
|
||||
size_t len = 0, total_len = 0;
|
||||
|
||||
if (unlikely(dst_nents == 0 || src_nents == 0))
|
||||
return NULL;
|
||||
|
||||
if (unlikely(dst_sg == NULL || src_sg == NULL))
|
||||
return NULL;
|
||||
|
||||
ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN
|
||||
| ATC_SRC_ADDR_MODE_INCR
|
||||
| ATC_DST_ADDR_MODE_INCR
|
||||
| ATC_FC_MEM2MEM;
|
||||
|
||||
/*
|
||||
* loop until there is either no more source or no more destination
|
||||
* scatterlist entry
|
||||
*/
|
||||
while (true) {
|
||||
|
||||
/* prepare the next transfer */
|
||||
if (dst_len == 0) {
|
||||
|
||||
/* no more destination scatterlist entries */
|
||||
if (!dst_sg || !dst_nents)
|
||||
break;
|
||||
|
||||
dst = sg_dma_address(dst_sg);
|
||||
dst_len = sg_dma_len(dst_sg);
|
||||
|
||||
dst_sg = sg_next(dst_sg);
|
||||
dst_nents--;
|
||||
}
|
||||
|
||||
if (src_len == 0) {
|
||||
|
||||
/* no more source scatterlist entries */
|
||||
if (!src_sg || !src_nents)
|
||||
break;
|
||||
|
||||
src = sg_dma_address(src_sg);
|
||||
src_len = sg_dma_len(src_sg);
|
||||
|
||||
src_sg = sg_next(src_sg);
|
||||
src_nents--;
|
||||
}
|
||||
|
||||
len = min_t(size_t, src_len, dst_len);
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/* take care for the alignment */
|
||||
src_width = dst_width = atc_get_xfer_width(src, dst, len);
|
||||
|
||||
ctrla = ATC_SRC_WIDTH(src_width) |
|
||||
ATC_DST_WIDTH(dst_width);
|
||||
|
||||
/*
|
||||
* The number of transfers to set up refer to the source width
|
||||
* that depends on the alignment.
|
||||
*/
|
||||
xfer_count = len >> src_width;
|
||||
if (xfer_count > ATC_BTSIZE_MAX) {
|
||||
xfer_count = ATC_BTSIZE_MAX;
|
||||
len = ATC_BTSIZE_MAX << src_width;
|
||||
}
|
||||
|
||||
/* create the transfer */
|
||||
desc = atc_desc_get(atchan);
|
||||
if (!desc)
|
||||
goto err_desc_get;
|
||||
|
||||
desc->lli.saddr = src;
|
||||
desc->lli.daddr = dst;
|
||||
desc->lli.ctrla = ctrla | xfer_count;
|
||||
desc->lli.ctrlb = ctrlb;
|
||||
|
||||
desc->txd.cookie = 0;
|
||||
desc->len = len;
|
||||
|
||||
/*
|
||||
* Although we only need the transfer width for the first and
|
||||
* the last descriptor, its easier to set it to all descriptors.
|
||||
*/
|
||||
desc->tx_width = src_width;
|
||||
|
||||
atc_desc_chain(&first, &prev, desc);
|
||||
|
||||
/* update the lengths and addresses for the next loop cycle */
|
||||
dst_len -= len;
|
||||
src_len -= len;
|
||||
dst += len;
|
||||
src += len;
|
||||
|
||||
total_len += len;
|
||||
}
|
||||
|
||||
/* First descriptor of the chain embedds additional information */
|
||||
first->txd.cookie = -EBUSY;
|
||||
first->total_len = total_len;
|
||||
|
||||
/* set end-of-link to the last link descriptor of list*/
|
||||
set_desc_eol(desc);
|
||||
|
||||
first->txd.flags = flags; /* client is in control of this ack */
|
||||
|
||||
return &first->txd;
|
||||
|
||||
err_desc_get:
|
||||
atc_desc_put(atchan, first);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* atc_dma_cyclic_check_values
|
||||
* Check for too big/unaligned periods and unaligned DMA buffer
|
||||
@ -1461,8 +1608,10 @@ static int __init at_dma_probe(struct platform_device *pdev)
|
||||
|
||||
/* setup platform data for each SoC */
|
||||
dma_cap_set(DMA_MEMCPY, at91sam9rl_config.cap_mask);
|
||||
dma_cap_set(DMA_SG, at91sam9rl_config.cap_mask);
|
||||
dma_cap_set(DMA_MEMCPY, at91sam9g45_config.cap_mask);
|
||||
dma_cap_set(DMA_SLAVE, at91sam9g45_config.cap_mask);
|
||||
dma_cap_set(DMA_SG, at91sam9g45_config.cap_mask);
|
||||
|
||||
/* get DMA parameters from controller type */
|
||||
plat_dat = at_dma_get_driver_data(pdev);
|
||||
@ -1582,11 +1731,15 @@ static int __init at_dma_probe(struct platform_device *pdev)
|
||||
atdma->dma_common.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
}
|
||||
|
||||
if (dma_has_cap(DMA_SG, atdma->dma_common.cap_mask))
|
||||
atdma->dma_common.device_prep_dma_sg = atc_prep_dma_sg;
|
||||
|
||||
dma_writel(atdma, EN, AT_DMA_ENABLE);
|
||||
|
||||
dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n",
|
||||
dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s%s), %d channels\n",
|
||||
dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "",
|
||||
dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "",
|
||||
dma_has_cap(DMA_SG, atdma->dma_common.cap_mask) ? "sg-cpy " : "",
|
||||
plat_dat->nr_channels);
|
||||
|
||||
dma_async_device_register(&atdma->dma_common);
|
||||
|
@ -1154,8 +1154,10 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
|
||||
dev_dbg(chan2dev(chan), "%s\n", __func__);
|
||||
|
||||
spin_lock_bh(&atchan->lock);
|
||||
if (!at_xdmac_chan_is_paused(atchan))
|
||||
if (!at_xdmac_chan_is_paused(atchan)) {
|
||||
spin_unlock_bh(&atchan->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
|
||||
clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
|
||||
|
@ -30,7 +30,7 @@
|
||||
#define DRIVER_NAME "bestcomm-core"
|
||||
|
||||
/* MPC5200 device tree match tables */
|
||||
static struct of_device_id mpc52xx_sram_ids[] = {
|
||||
static const struct of_device_id mpc52xx_sram_ids[] = {
|
||||
{ .compatible = "fsl,mpc5200-sram", },
|
||||
{ .compatible = "mpc5200-sram", },
|
||||
{}
|
||||
@ -481,7 +481,7 @@ static int mpc52xx_bcom_remove(struct platform_device *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mpc52xx_bcom_of_match[] = {
|
||||
static const struct of_device_id mpc52xx_bcom_of_match[] = {
|
||||
{ .compatible = "fsl,mpc5200-bestcomm", },
|
||||
{ .compatible = "mpc5200-bestcomm", },
|
||||
{},
|
||||
|
@ -7,10 +7,6 @@
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
@ -343,7 +339,7 @@ static void jz4740_dma_chan_irq(struct jz4740_dmaengine_chan *chan)
|
||||
{
|
||||
spin_lock(&chan->vchan.lock);
|
||||
if (chan->desc) {
|
||||
if (chan->desc && chan->desc->cyclic) {
|
||||
if (chan->desc->cyclic) {
|
||||
vchan_cyclic_callback(&chan->desc->vdesc);
|
||||
} else {
|
||||
if (chan->next_sg == chan->desc->num_sgs) {
|
||||
@ -496,11 +492,6 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int jz4740_dma_alloc_chan_resources(struct dma_chan *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4740_dma_free_chan_resources(struct dma_chan *c)
|
||||
{
|
||||
vchan_free_chan_resources(to_virt_chan(c));
|
||||
@ -543,7 +534,6 @@ static int jz4740_dma_probe(struct platform_device *pdev)
|
||||
|
||||
dma_cap_set(DMA_SLAVE, dd->cap_mask);
|
||||
dma_cap_set(DMA_CYCLIC, dd->cap_mask);
|
||||
dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources;
|
||||
dd->device_free_chan_resources = jz4740_dma_free_chan_resources;
|
||||
dd->device_tx_status = jz4740_dma_tx_status;
|
||||
dd->device_issue_pending = jz4740_dma_issue_pending;
|
||||
|
877
drivers/dma/dma-jz4780.c
Normal file
877
drivers/dma/dma-jz4780.c
Normal file
@ -0,0 +1,877 @@
|
||||
/*
|
||||
* Ingenic JZ4780 DMA controller
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex@alex-smith.me.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
#include "virt-dma.h"
|
||||
|
||||
#define JZ_DMA_NR_CHANNELS 32
|
||||
|
||||
/* Global registers. */
|
||||
#define JZ_DMA_REG_DMAC 0x1000
|
||||
#define JZ_DMA_REG_DIRQP 0x1004
|
||||
#define JZ_DMA_REG_DDR 0x1008
|
||||
#define JZ_DMA_REG_DDRS 0x100c
|
||||
#define JZ_DMA_REG_DMACP 0x101c
|
||||
#define JZ_DMA_REG_DSIRQP 0x1020
|
||||
#define JZ_DMA_REG_DSIRQM 0x1024
|
||||
#define JZ_DMA_REG_DCIRQP 0x1028
|
||||
#define JZ_DMA_REG_DCIRQM 0x102c
|
||||
|
||||
/* Per-channel registers. */
|
||||
#define JZ_DMA_REG_CHAN(n) (n * 0x20)
|
||||
#define JZ_DMA_REG_DSA(n) (0x00 + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DTA(n) (0x04 + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DTC(n) (0x08 + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DRT(n) (0x0c + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DCS(n) (0x10 + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DCM(n) (0x14 + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DDA(n) (0x18 + JZ_DMA_REG_CHAN(n))
|
||||
#define JZ_DMA_REG_DSD(n) (0x1c + JZ_DMA_REG_CHAN(n))
|
||||
|
||||
#define JZ_DMA_DMAC_DMAE BIT(0)
|
||||
#define JZ_DMA_DMAC_AR BIT(2)
|
||||
#define JZ_DMA_DMAC_HLT BIT(3)
|
||||
#define JZ_DMA_DMAC_FMSC BIT(31)
|
||||
|
||||
#define JZ_DMA_DRT_AUTO 0x8
|
||||
|
||||
#define JZ_DMA_DCS_CTE BIT(0)
|
||||
#define JZ_DMA_DCS_HLT BIT(2)
|
||||
#define JZ_DMA_DCS_TT BIT(3)
|
||||
#define JZ_DMA_DCS_AR BIT(4)
|
||||
#define JZ_DMA_DCS_DES8 BIT(30)
|
||||
|
||||
#define JZ_DMA_DCM_LINK BIT(0)
|
||||
#define JZ_DMA_DCM_TIE BIT(1)
|
||||
#define JZ_DMA_DCM_STDE BIT(2)
|
||||
#define JZ_DMA_DCM_TSZ_SHIFT 8
|
||||
#define JZ_DMA_DCM_TSZ_MASK (0x7 << JZ_DMA_DCM_TSZ_SHIFT)
|
||||
#define JZ_DMA_DCM_DP_SHIFT 12
|
||||
#define JZ_DMA_DCM_SP_SHIFT 14
|
||||
#define JZ_DMA_DCM_DAI BIT(22)
|
||||
#define JZ_DMA_DCM_SAI BIT(23)
|
||||
|
||||
#define JZ_DMA_SIZE_4_BYTE 0x0
|
||||
#define JZ_DMA_SIZE_1_BYTE 0x1
|
||||
#define JZ_DMA_SIZE_2_BYTE 0x2
|
||||
#define JZ_DMA_SIZE_16_BYTE 0x3
|
||||
#define JZ_DMA_SIZE_32_BYTE 0x4
|
||||
#define JZ_DMA_SIZE_64_BYTE 0x5
|
||||
#define JZ_DMA_SIZE_128_BYTE 0x6
|
||||
|
||||
#define JZ_DMA_WIDTH_32_BIT 0x0
|
||||
#define JZ_DMA_WIDTH_8_BIT 0x1
|
||||
#define JZ_DMA_WIDTH_16_BIT 0x2
|
||||
|
||||
#define JZ_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
|
||||
|
||||
/**
|
||||
* struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller.
|
||||
* @dcm: value for the DCM (channel command) register
|
||||
* @dsa: source address
|
||||
* @dta: target address
|
||||
* @dtc: transfer count (number of blocks of the transfer size specified in DCM
|
||||
* to transfer) in the low 24 bits, offset of the next descriptor from the
|
||||
* descriptor base address in the upper 8 bits.
|
||||
* @sd: target/source stride difference (in stride transfer mode).
|
||||
* @drt: request type
|
||||
*/
|
||||
struct jz4780_dma_hwdesc {
|
||||
uint32_t dcm;
|
||||
uint32_t dsa;
|
||||
uint32_t dta;
|
||||
uint32_t dtc;
|
||||
uint32_t sd;
|
||||
uint32_t drt;
|
||||
uint32_t reserved[2];
|
||||
};
|
||||
|
||||
/* Size of allocations for hardware descriptor blocks. */
|
||||
#define JZ_DMA_DESC_BLOCK_SIZE PAGE_SIZE
|
||||
#define JZ_DMA_MAX_DESC \
|
||||
(JZ_DMA_DESC_BLOCK_SIZE / sizeof(struct jz4780_dma_hwdesc))
|
||||
|
||||
struct jz4780_dma_desc {
|
||||
struct virt_dma_desc vdesc;
|
||||
|
||||
struct jz4780_dma_hwdesc *desc;
|
||||
dma_addr_t desc_phys;
|
||||
unsigned int count;
|
||||
enum dma_transaction_type type;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
struct jz4780_dma_chan {
|
||||
struct virt_dma_chan vchan;
|
||||
unsigned int id;
|
||||
struct dma_pool *desc_pool;
|
||||
|
||||
uint32_t transfer_type;
|
||||
uint32_t transfer_shift;
|
||||
struct dma_slave_config config;
|
||||
|
||||
struct jz4780_dma_desc *desc;
|
||||
unsigned int curr_hwdesc;
|
||||
};
|
||||
|
||||
struct jz4780_dma_dev {
|
||||
struct dma_device dma_device;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
unsigned int irq;
|
||||
|
||||
uint32_t chan_reserved;
|
||||
struct jz4780_dma_chan chan[JZ_DMA_NR_CHANNELS];
|
||||
};
|
||||
|
||||
struct jz4780_dma_data {
|
||||
uint32_t transfer_type;
|
||||
int channel;
|
||||
};
|
||||
|
||||
static inline struct jz4780_dma_chan *to_jz4780_dma_chan(struct dma_chan *chan)
|
||||
{
|
||||
return container_of(chan, struct jz4780_dma_chan, vchan.chan);
|
||||
}
|
||||
|
||||
static inline struct jz4780_dma_desc *to_jz4780_dma_desc(
|
||||
struct virt_dma_desc *vdesc)
|
||||
{
|
||||
return container_of(vdesc, struct jz4780_dma_desc, vdesc);
|
||||
}
|
||||
|
||||
static inline struct jz4780_dma_dev *jz4780_dma_chan_parent(
|
||||
struct jz4780_dma_chan *jzchan)
|
||||
{
|
||||
return container_of(jzchan->vchan.chan.device, struct jz4780_dma_dev,
|
||||
dma_device);
|
||||
}
|
||||
|
||||
static inline uint32_t jz4780_dma_readl(struct jz4780_dma_dev *jzdma,
|
||||
unsigned int reg)
|
||||
{
|
||||
return readl(jzdma->base + reg);
|
||||
}
|
||||
|
||||
static inline void jz4780_dma_writel(struct jz4780_dma_dev *jzdma,
|
||||
unsigned int reg, uint32_t val)
|
||||
{
|
||||
writel(val, jzdma->base + reg);
|
||||
}
|
||||
|
||||
static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
|
||||
struct jz4780_dma_chan *jzchan, unsigned int count,
|
||||
enum dma_transaction_type type)
|
||||
{
|
||||
struct jz4780_dma_desc *desc;
|
||||
|
||||
if (count > JZ_DMA_MAX_DESC)
|
||||
return NULL;
|
||||
|
||||
desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
desc->desc = dma_pool_alloc(jzchan->desc_pool, GFP_NOWAIT,
|
||||
&desc->desc_phys);
|
||||
if (!desc->desc) {
|
||||
kfree(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc->count = count;
|
||||
desc->type = type;
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
|
||||
{
|
||||
struct jz4780_dma_desc *desc = to_jz4780_dma_desc(vdesc);
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(vdesc->tx.chan);
|
||||
|
||||
dma_pool_free(jzchan->desc_pool, desc->desc, desc->desc_phys);
|
||||
kfree(desc);
|
||||
}
|
||||
|
||||
static uint32_t jz4780_dma_transfer_size(unsigned long val, int *ord)
|
||||
{
|
||||
*ord = ffs(val) - 1;
|
||||
|
||||
switch (*ord) {
|
||||
case 0:
|
||||
return JZ_DMA_SIZE_1_BYTE;
|
||||
case 1:
|
||||
return JZ_DMA_SIZE_2_BYTE;
|
||||
case 2:
|
||||
return JZ_DMA_SIZE_4_BYTE;
|
||||
case 4:
|
||||
return JZ_DMA_SIZE_16_BYTE;
|
||||
case 5:
|
||||
return JZ_DMA_SIZE_32_BYTE;
|
||||
case 6:
|
||||
return JZ_DMA_SIZE_64_BYTE;
|
||||
case 7:
|
||||
return JZ_DMA_SIZE_128_BYTE;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
|
||||
struct jz4780_dma_hwdesc *desc, dma_addr_t addr, size_t len,
|
||||
enum dma_transfer_direction direction)
|
||||
{
|
||||
struct dma_slave_config *config = &jzchan->config;
|
||||
uint32_t width, maxburst, tsz;
|
||||
int ord;
|
||||
|
||||
if (direction == DMA_MEM_TO_DEV) {
|
||||
desc->dcm = JZ_DMA_DCM_SAI;
|
||||
desc->dsa = addr;
|
||||
desc->dta = config->dst_addr;
|
||||
desc->drt = jzchan->transfer_type;
|
||||
|
||||
width = config->dst_addr_width;
|
||||
maxburst = config->dst_maxburst;
|
||||
} else {
|
||||
desc->dcm = JZ_DMA_DCM_DAI;
|
||||
desc->dsa = config->src_addr;
|
||||
desc->dta = addr;
|
||||
desc->drt = jzchan->transfer_type;
|
||||
|
||||
width = config->src_addr_width;
|
||||
maxburst = config->src_maxburst;
|
||||
}
|
||||
|
||||
/*
|
||||
* This calculates the maximum transfer size that can be used with the
|
||||
* given address, length, width and maximum burst size. The address
|
||||
* must be aligned to the transfer size, the total length must be
|
||||
* divisible by the transfer size, and we must not use more than the
|
||||
* maximum burst specified by the user.
|
||||
*/
|
||||
tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst), &ord);
|
||||
jzchan->transfer_shift = ord;
|
||||
|
||||
switch (width) {
|
||||
case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
||||
case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
||||
width = JZ_DMA_WIDTH_32_BIT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc->dcm |= tsz << JZ_DMA_DCM_TSZ_SHIFT;
|
||||
desc->dcm |= width << JZ_DMA_DCM_SP_SHIFT;
|
||||
desc->dcm |= width << JZ_DMA_DCM_DP_SHIFT;
|
||||
|
||||
desc->dtc = len >> ord;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg(
|
||||
struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
|
||||
enum dma_transfer_direction direction, unsigned long flags)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
struct jz4780_dma_desc *desc;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
desc = jz4780_dma_desc_alloc(jzchan, sg_len, DMA_SLAVE);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < sg_len; i++) {
|
||||
err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i],
|
||||
sg_dma_address(&sgl[i]),
|
||||
sg_dma_len(&sgl[i]),
|
||||
direction);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
|
||||
desc->desc[i].dcm |= JZ_DMA_DCM_TIE;
|
||||
|
||||
if (i != (sg_len - 1)) {
|
||||
/* Automatically proceeed to the next descriptor. */
|
||||
desc->desc[i].dcm |= JZ_DMA_DCM_LINK;
|
||||
|
||||
/*
|
||||
* The upper 8 bits of the DTC field in the descriptor
|
||||
* must be set to (offset from descriptor base of next
|
||||
* descriptor >> 4).
|
||||
*/
|
||||
desc->desc[i].dtc |=
|
||||
(((i + 1) * sizeof(*desc->desc)) >> 4) << 24;
|
||||
}
|
||||
}
|
||||
|
||||
return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *jz4780_dma_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 jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
struct jz4780_dma_desc *desc;
|
||||
unsigned int periods, i;
|
||||
int err;
|
||||
|
||||
if (buf_len % period_len)
|
||||
return NULL;
|
||||
|
||||
periods = buf_len / period_len;
|
||||
|
||||
desc = jz4780_dma_desc_alloc(jzchan, periods, DMA_CYCLIC);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < periods; i++) {
|
||||
err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], buf_addr,
|
||||
period_len, direction);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
buf_addr += period_len;
|
||||
|
||||
/*
|
||||
* Set the link bit to indicate that the controller should
|
||||
* automatically proceed to the next descriptor. In
|
||||
* jz4780_dma_begin(), this will be cleared if we need to issue
|
||||
* an interrupt after each period.
|
||||
*/
|
||||
desc->desc[i].dcm |= JZ_DMA_DCM_TIE | JZ_DMA_DCM_LINK;
|
||||
|
||||
/*
|
||||
* The upper 8 bits of the DTC field in the descriptor must be
|
||||
* set to (offset from descriptor base of next descriptor >> 4).
|
||||
* If this is the last descriptor, link it back to the first,
|
||||
* i.e. leave offset set to 0, otherwise point to the next one.
|
||||
*/
|
||||
if (i != (periods - 1)) {
|
||||
desc->desc[i].dtc |=
|
||||
(((i + 1) * sizeof(*desc->desc)) >> 4) << 24;
|
||||
}
|
||||
}
|
||||
|
||||
return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags);
|
||||
}
|
||||
|
||||
struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy(
|
||||
struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
||||
size_t len, unsigned long flags)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
struct jz4780_dma_desc *desc;
|
||||
uint32_t tsz;
|
||||
int ord;
|
||||
|
||||
desc = jz4780_dma_desc_alloc(jzchan, 1, DMA_MEMCPY);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
tsz = jz4780_dma_transfer_size(dest | src | len, &ord);
|
||||
if (tsz < 0)
|
||||
return ERR_PTR(tsz);
|
||||
|
||||
desc->desc[0].dsa = src;
|
||||
desc->desc[0].dta = dest;
|
||||
desc->desc[0].drt = JZ_DMA_DRT_AUTO;
|
||||
desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI |
|
||||
tsz << JZ_DMA_DCM_TSZ_SHIFT |
|
||||
JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT |
|
||||
JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_DP_SHIFT;
|
||||
desc->desc[0].dtc = len >> ord;
|
||||
|
||||
return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags);
|
||||
}
|
||||
|
||||
static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
|
||||
{
|
||||
struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
|
||||
struct virt_dma_desc *vdesc;
|
||||
unsigned int i;
|
||||
dma_addr_t desc_phys;
|
||||
|
||||
if (!jzchan->desc) {
|
||||
vdesc = vchan_next_desc(&jzchan->vchan);
|
||||
if (!vdesc)
|
||||
return;
|
||||
|
||||
list_del(&vdesc->node);
|
||||
|
||||
jzchan->desc = to_jz4780_dma_desc(vdesc);
|
||||
jzchan->curr_hwdesc = 0;
|
||||
|
||||
if (jzchan->desc->type == DMA_CYCLIC && vdesc->tx.callback) {
|
||||
/*
|
||||
* The DMA controller doesn't support triggering an
|
||||
* interrupt after processing each descriptor, only
|
||||
* after processing an entire terminated list of
|
||||
* descriptors. For a cyclic DMA setup the list of
|
||||
* descriptors is not terminated so we can never get an
|
||||
* interrupt.
|
||||
*
|
||||
* If the user requested a callback for a cyclic DMA
|
||||
* setup then we workaround this hardware limitation
|
||||
* here by degrading to a set of unlinked descriptors
|
||||
* which we will submit in sequence in response to the
|
||||
* completion of processing the previous descriptor.
|
||||
*/
|
||||
for (i = 0; i < jzchan->desc->count; i++)
|
||||
jzchan->desc->desc[i].dcm &= ~JZ_DMA_DCM_LINK;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* There is an existing transfer, therefore this must be one
|
||||
* for which we unlinked the descriptors above. Advance to the
|
||||
* next one in the list.
|
||||
*/
|
||||
jzchan->curr_hwdesc =
|
||||
(jzchan->curr_hwdesc + 1) % jzchan->desc->count;
|
||||
}
|
||||
|
||||
/* Use 8-word descriptors. */
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), JZ_DMA_DCS_DES8);
|
||||
|
||||
/* Write descriptor address and initiate descriptor fetch. */
|
||||
desc_phys = jzchan->desc->desc_phys +
|
||||
(jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc));
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DDA(jzchan->id), desc_phys);
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id));
|
||||
|
||||
/* Enable the channel. */
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id),
|
||||
JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
|
||||
}
|
||||
|
||||
static void jz4780_dma_issue_pending(struct dma_chan *chan)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&jzchan->vchan.lock, flags);
|
||||
|
||||
if (vchan_issue_pending(&jzchan->vchan) && !jzchan->desc)
|
||||
jz4780_dma_begin(jzchan);
|
||||
|
||||
spin_unlock_irqrestore(&jzchan->vchan.lock, flags);
|
||||
}
|
||||
|
||||
static int jz4780_dma_terminate_all(struct jz4780_dma_chan *jzchan)
|
||||
{
|
||||
struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
|
||||
unsigned long flags;
|
||||
LIST_HEAD(head);
|
||||
|
||||
spin_lock_irqsave(&jzchan->vchan.lock, flags);
|
||||
|
||||
/* Clear the DMA status and stop the transfer. */
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0);
|
||||
if (jzchan->desc) {
|
||||
jz4780_dma_desc_free(&jzchan->desc->vdesc);
|
||||
jzchan->desc = NULL;
|
||||
}
|
||||
|
||||
vchan_get_all_descriptors(&jzchan->vchan, &head);
|
||||
|
||||
spin_unlock_irqrestore(&jzchan->vchan.lock, flags);
|
||||
|
||||
vchan_dma_desc_free_list(&jzchan->vchan, &head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4780_dma_slave_config(struct jz4780_dma_chan *jzchan,
|
||||
const struct dma_slave_config *config)
|
||||
{
|
||||
if ((config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
|
||||
|| (config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES))
|
||||
return -EINVAL;
|
||||
|
||||
/* Copy the reset of the slave configuration, it is used later. */
|
||||
memcpy(&jzchan->config, config, sizeof(jzchan->config));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
|
||||
struct jz4780_dma_desc *desc, unsigned int next_sg)
|
||||
{
|
||||
struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
|
||||
unsigned int residue, count;
|
||||
unsigned int i;
|
||||
|
||||
residue = 0;
|
||||
|
||||
for (i = next_sg; i < desc->count; i++)
|
||||
residue += desc->desc[i].dtc << jzchan->transfer_shift;
|
||||
|
||||
if (next_sg != 0) {
|
||||
count = jz4780_dma_readl(jzdma,
|
||||
JZ_DMA_REG_DTC(jzchan->id));
|
||||
residue += count << jzchan->transfer_shift;
|
||||
}
|
||||
|
||||
return residue;
|
||||
}
|
||||
|
||||
static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
|
||||
dma_cookie_t cookie, struct dma_tx_state *txstate)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
struct virt_dma_desc *vdesc;
|
||||
enum dma_status status;
|
||||
unsigned long flags;
|
||||
|
||||
status = dma_cookie_status(chan, cookie, txstate);
|
||||
if ((status == DMA_COMPLETE) || (txstate == NULL))
|
||||
return status;
|
||||
|
||||
spin_lock_irqsave(&jzchan->vchan.lock, flags);
|
||||
|
||||
vdesc = vchan_find_desc(&jzchan->vchan, cookie);
|
||||
if (vdesc) {
|
||||
/* On the issued list, so hasn't been processed yet */
|
||||
txstate->residue = jz4780_dma_desc_residue(jzchan,
|
||||
to_jz4780_dma_desc(vdesc), 0);
|
||||
} else if (cookie == jzchan->desc->vdesc.tx.cookie) {
|
||||
txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
|
||||
(jzchan->curr_hwdesc + 1) % jzchan->desc->count);
|
||||
} else
|
||||
txstate->residue = 0;
|
||||
|
||||
if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc
|
||||
&& jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT))
|
||||
status = DMA_ERROR;
|
||||
|
||||
spin_unlock_irqrestore(&jzchan->vchan.lock, flags);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma,
|
||||
struct jz4780_dma_chan *jzchan)
|
||||
{
|
||||
uint32_t dcs;
|
||||
|
||||
spin_lock(&jzchan->vchan.lock);
|
||||
|
||||
dcs = jz4780_dma_readl(jzdma, JZ_DMA_REG_DCS(jzchan->id));
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0);
|
||||
|
||||
if (dcs & JZ_DMA_DCS_AR) {
|
||||
dev_warn(&jzchan->vchan.chan.dev->device,
|
||||
"address error (DCS=0x%x)\n", dcs);
|
||||
}
|
||||
|
||||
if (dcs & JZ_DMA_DCS_HLT) {
|
||||
dev_warn(&jzchan->vchan.chan.dev->device,
|
||||
"channel halt (DCS=0x%x)\n", dcs);
|
||||
}
|
||||
|
||||
if (jzchan->desc) {
|
||||
jzchan->desc->status = dcs;
|
||||
|
||||
if ((dcs & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) == 0) {
|
||||
if (jzchan->desc->type == DMA_CYCLIC) {
|
||||
vchan_cyclic_callback(&jzchan->desc->vdesc);
|
||||
} else {
|
||||
vchan_cookie_complete(&jzchan->desc->vdesc);
|
||||
jzchan->desc = NULL;
|
||||
}
|
||||
|
||||
jz4780_dma_begin(jzchan);
|
||||
}
|
||||
} else {
|
||||
dev_err(&jzchan->vchan.chan.dev->device,
|
||||
"channel IRQ with no active transfer\n");
|
||||
}
|
||||
|
||||
spin_unlock(&jzchan->vchan.lock);
|
||||
}
|
||||
|
||||
static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct jz4780_dma_dev *jzdma = data;
|
||||
uint32_t pending, dmac;
|
||||
int i;
|
||||
|
||||
pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP);
|
||||
|
||||
for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) {
|
||||
if (!(pending & (1<<i)))
|
||||
continue;
|
||||
|
||||
jz4780_dma_chan_irq(jzdma, &jzdma->chan[i]);
|
||||
}
|
||||
|
||||
/* Clear halt and address error status of all channels. */
|
||||
dmac = jz4780_dma_readl(jzdma, JZ_DMA_REG_DMAC);
|
||||
dmac &= ~(JZ_DMA_DMAC_HLT | JZ_DMA_DMAC_AR);
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, dmac);
|
||||
|
||||
/* Clear interrupt pending status. */
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DIRQP, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int jz4780_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
|
||||
jzchan->desc_pool = dma_pool_create(dev_name(&chan->dev->device),
|
||||
chan->device->dev,
|
||||
JZ_DMA_DESC_BLOCK_SIZE,
|
||||
PAGE_SIZE, 0);
|
||||
if (!jzchan->desc_pool) {
|
||||
dev_err(&chan->dev->device,
|
||||
"failed to allocate descriptor pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4780_dma_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
|
||||
vchan_free_chan_resources(&jzchan->vchan);
|
||||
dma_pool_destroy(jzchan->desc_pool);
|
||||
jzchan->desc_pool = NULL;
|
||||
}
|
||||
|
||||
static bool jz4780_dma_filter_fn(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
|
||||
struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
|
||||
struct jz4780_dma_data *data = param;
|
||||
|
||||
if (data->channel > -1) {
|
||||
if (data->channel != jzchan->id)
|
||||
return false;
|
||||
} else if (jzdma->chan_reserved & BIT(jzchan->id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jzchan->transfer_type = data->transfer_type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
struct jz4780_dma_dev *jzdma = ofdma->of_dma_data;
|
||||
dma_cap_mask_t mask = jzdma->dma_device.cap_mask;
|
||||
struct jz4780_dma_data data;
|
||||
|
||||
if (dma_spec->args_count != 2)
|
||||
return NULL;
|
||||
|
||||
data.transfer_type = dma_spec->args[0];
|
||||
data.channel = dma_spec->args[1];
|
||||
|
||||
if (data.channel > -1) {
|
||||
if (data.channel >= JZ_DMA_NR_CHANNELS) {
|
||||
dev_err(jzdma->dma_device.dev,
|
||||
"device requested non-existent channel %u\n",
|
||||
data.channel);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Can only select a channel marked as reserved. */
|
||||
if (!(jzdma->chan_reserved & BIT(data.channel))) {
|
||||
dev_err(jzdma->dma_device.dev,
|
||||
"device requested unreserved channel %u\n",
|
||||
data.channel);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dma_request_channel(mask, jz4780_dma_filter_fn, &data);
|
||||
}
|
||||
|
||||
static int jz4780_dma_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz4780_dma_dev *jzdma;
|
||||
struct jz4780_dma_chan *jzchan;
|
||||
struct dma_device *dd;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
|
||||
jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL);
|
||||
if (!jzdma)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, jzdma);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "failed to get I/O memory\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
jzdma->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(jzdma->base))
|
||||
return PTR_ERR(jzdma->base);
|
||||
|
||||
jzdma->irq = platform_get_irq(pdev, 0);
|
||||
if (jzdma->irq < 0) {
|
||||
dev_err(dev, "failed to get IRQ: %d\n", ret);
|
||||
return jzdma->irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, jzdma->irq, jz4780_dma_irq_handler, 0,
|
||||
dev_name(dev), jzdma);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ %u!\n", jzdma->irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
jzdma->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(jzdma->clk)) {
|
||||
dev_err(dev, "failed to get clock\n");
|
||||
return PTR_ERR(jzdma->clk);
|
||||
}
|
||||
|
||||
clk_prepare_enable(jzdma->clk);
|
||||
|
||||
/* Property is optional, if it doesn't exist the value will remain 0. */
|
||||
of_property_read_u32_index(dev->of_node, "ingenic,reserved-channels",
|
||||
0, &jzdma->chan_reserved);
|
||||
|
||||
dd = &jzdma->dma_device;
|
||||
|
||||
dma_cap_set(DMA_MEMCPY, dd->cap_mask);
|
||||
dma_cap_set(DMA_SLAVE, dd->cap_mask);
|
||||
dma_cap_set(DMA_CYCLIC, dd->cap_mask);
|
||||
|
||||
dd->dev = dev;
|
||||
dd->copy_align = 2; /* 2^2 = 4 byte alignment */
|
||||
dd->device_alloc_chan_resources = jz4780_dma_alloc_chan_resources;
|
||||
dd->device_free_chan_resources = jz4780_dma_free_chan_resources;
|
||||
dd->device_prep_slave_sg = jz4780_dma_prep_slave_sg;
|
||||
dd->device_prep_dma_cyclic = jz4780_dma_prep_dma_cyclic;
|
||||
dd->device_prep_dma_memcpy = jz4780_dma_prep_dma_memcpy;
|
||||
dd->device_config = jz4780_dma_slave_config;
|
||||
dd->device_terminate_all = jz4780_dma_terminate_all;
|
||||
dd->device_tx_status = jz4780_dma_tx_status;
|
||||
dd->device_issue_pending = jz4780_dma_issue_pending;
|
||||
dd->src_addr_widths = JZ_DMA_BUSWIDTHS;
|
||||
dd->dst_addr_widths = JZ_DMA_BUSWIDTHS;
|
||||
dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
||||
dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
|
||||
|
||||
/*
|
||||
* Enable DMA controller, mark all channels as not programmable.
|
||||
* Also set the FMSC bit - it increases MSC performance, so it makes
|
||||
* little sense not to enable it.
|
||||
*/
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC,
|
||||
JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
|
||||
jz4780_dma_writel(jzdma, JZ_DMA_REG_DMACP, 0);
|
||||
|
||||
INIT_LIST_HEAD(&dd->channels);
|
||||
|
||||
for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) {
|
||||
jzchan = &jzdma->chan[i];
|
||||
jzchan->id = i;
|
||||
|
||||
vchan_init(&jzchan->vchan, dd);
|
||||
jzchan->vchan.desc_free = jz4780_dma_desc_free;
|
||||
}
|
||||
|
||||
ret = dma_async_device_register(dd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register device\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
/* Register with OF DMA helpers. */
|
||||
ret = of_dma_controller_register(dev->of_node, jz4780_of_dma_xlate,
|
||||
jzdma);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register OF DMA controller\n");
|
||||
goto err_unregister_dev;
|
||||
}
|
||||
|
||||
dev_info(dev, "JZ4780 DMA controller initialised\n");
|
||||
return 0;
|
||||
|
||||
err_unregister_dev:
|
||||
dma_async_device_unregister(dd);
|
||||
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(jzdma->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jz4780_dma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct jz4780_dma_dev *jzdma = platform_get_drvdata(pdev);
|
||||
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
devm_free_irq(&pdev->dev, jzdma->irq, jzdma);
|
||||
dma_async_device_unregister(&jzdma->dma_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id jz4780_dma_dt_match[] = {
|
||||
{ .compatible = "ingenic,jz4780-dma", .data = NULL },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match);
|
||||
|
||||
static struct platform_driver jz4780_dma_driver = {
|
||||
.probe = jz4780_dma_probe,
|
||||
.remove = jz4780_dma_remove,
|
||||
.driver = {
|
||||
.name = "jz4780-dma",
|
||||
.of_match_table = of_match_ptr(jz4780_dma_dt_match),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init jz4780_dma_init(void)
|
||||
{
|
||||
return platform_driver_register(&jz4780_dma_driver);
|
||||
}
|
||||
subsys_initcall(jz4780_dma_init);
|
||||
|
||||
static void __exit jz4780_dma_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&jz4780_dma_driver);
|
||||
}
|
||||
module_exit(jz4780_dma_exit);
|
||||
|
||||
MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
|
||||
MODULE_DESCRIPTION("Ingenic JZ4780 DMA controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
@ -355,20 +351,6 @@ struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type)
|
||||
}
|
||||
EXPORT_SYMBOL(dma_find_channel);
|
||||
|
||||
/*
|
||||
* net_dma_find_channel - find a channel for net_dma
|
||||
* net_dma has alignment requirements
|
||||
*/
|
||||
struct dma_chan *net_dma_find_channel(void)
|
||||
{
|
||||
struct dma_chan *chan = dma_find_channel(DMA_MEMCPY);
|
||||
if (chan && !is_dma_copy_aligned(chan->device, 1, 1, 1))
|
||||
return NULL;
|
||||
|
||||
return chan;
|
||||
}
|
||||
EXPORT_SYMBOL(net_dma_find_channel);
|
||||
|
||||
/**
|
||||
* dma_issue_pending_all - flush all pending operations across all channels
|
||||
*/
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
config DW_DMAC_CORE
|
||||
tristate "Synopsys DesignWare AHB DMA support"
|
||||
tristate
|
||||
select DMA_ENGINE
|
||||
|
||||
config DW_DMAC
|
||||
|
@ -230,7 +230,8 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
|
||||
/* ASSERT: channel is idle */
|
||||
if (dma_readl(dw, CH_EN) & dwc->mask) {
|
||||
dev_err(chan2dev(&dwc->chan),
|
||||
"BUG: Attempted to start non-idle channel\n");
|
||||
"%s: BUG: Attempted to start non-idle channel\n",
|
||||
__func__);
|
||||
dwc_dump_chan_regs(dwc);
|
||||
|
||||
/* The tasklet will hopefully advance the queue... */
|
||||
@ -814,11 +815,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
|
||||
slave_sg_todev_fill_desc:
|
||||
desc = dwc_desc_get(dwc);
|
||||
if (!desc) {
|
||||
dev_err(chan2dev(chan),
|
||||
"not enough descriptors available\n");
|
||||
if (!desc)
|
||||
goto err_desc_get;
|
||||
}
|
||||
|
||||
desc->lli.sar = mem;
|
||||
desc->lli.dar = reg;
|
||||
@ -874,11 +872,8 @@ slave_sg_todev_fill_desc:
|
||||
|
||||
slave_sg_fromdev_fill_desc:
|
||||
desc = dwc_desc_get(dwc);
|
||||
if (!desc) {
|
||||
dev_err(chan2dev(chan),
|
||||
"not enough descriptors available\n");
|
||||
if (!desc)
|
||||
goto err_desc_get;
|
||||
}
|
||||
|
||||
desc->lli.sar = reg;
|
||||
desc->lli.dar = mem;
|
||||
@ -922,6 +917,8 @@ slave_sg_fromdev_fill_desc:
|
||||
return &first->txd;
|
||||
|
||||
err_desc_get:
|
||||
dev_err(chan2dev(chan),
|
||||
"not enough descriptors available. Direction %d\n", direction);
|
||||
dwc_desc_put(dwc, first);
|
||||
return NULL;
|
||||
}
|
||||
@ -1261,7 +1258,8 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
|
||||
/* Assert channel is idle */
|
||||
if (dma_readl(dw, CH_EN) & dwc->mask) {
|
||||
dev_err(chan2dev(&dwc->chan),
|
||||
"BUG: Attempted to start non-idle channel\n");
|
||||
"%s: BUG: Attempted to start non-idle channel\n",
|
||||
__func__);
|
||||
dwc_dump_chan_regs(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EBUSY;
|
||||
|
@ -812,7 +812,7 @@ static int edma_alloc_chan_resources(struct dma_chan *chan)
|
||||
LIST_HEAD(descs);
|
||||
|
||||
a_ch_num = edma_alloc_channel(echan->ch_num, edma_callback,
|
||||
chan, EVENTQ_DEFAULT);
|
||||
echan, EVENTQ_DEFAULT);
|
||||
|
||||
if (a_ch_num < 0) {
|
||||
ret = -ENODEV;
|
||||
|
904
drivers/dma/fsl_raid.c
Normal file
904
drivers/dma/fsl_raid.c
Normal file
@ -0,0 +1,904 @@
|
||||
/*
|
||||
* drivers/dma/fsl_raid.c
|
||||
*
|
||||
* Freescale RAID Engine device driver
|
||||
*
|
||||
* Author:
|
||||
* Harninder Rai <harninder.rai@freescale.com>
|
||||
* Naveen Burmi <naveenburmi@freescale.com>
|
||||
*
|
||||
* Rewrite:
|
||||
* Xuelin Shi <xuelin.shi@freescale.com>
|
||||
*
|
||||
* Copyright (c) 2010-2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Freescale Semiconductor nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Theory of operation:
|
||||
*
|
||||
* General capabilities:
|
||||
* RAID Engine (RE) block is capable of offloading XOR, memcpy and P/Q
|
||||
* calculations required in RAID5 and RAID6 operations. RE driver
|
||||
* registers with Linux's ASYNC layer as dma driver. RE hardware
|
||||
* maintains strict ordering of the requests through chained
|
||||
* command queueing.
|
||||
*
|
||||
* Data flow:
|
||||
* Software RAID layer of Linux (MD layer) maintains RAID partitions,
|
||||
* strips, stripes etc. It sends requests to the underlying ASYNC layer
|
||||
* which further passes it to RE driver. ASYNC layer decides which request
|
||||
* goes to which job ring of RE hardware. For every request processed by
|
||||
* RAID Engine, driver gets an interrupt unless coalescing is set. The
|
||||
* per job ring interrupt handler checks the status register for errors,
|
||||
* clears the interrupt and leave the post interrupt processing to the irq
|
||||
* thread.
|
||||
*/
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
#include "fsl_raid.h"
|
||||
|
||||
#define FSL_RE_MAX_XOR_SRCS 16
|
||||
#define FSL_RE_MAX_PQ_SRCS 16
|
||||
#define FSL_RE_MIN_DESCS 256
|
||||
#define FSL_RE_MAX_DESCS (4 * FSL_RE_MIN_DESCS)
|
||||
#define FSL_RE_FRAME_FORMAT 0x1
|
||||
#define FSL_RE_MAX_DATA_LEN (1024*1024)
|
||||
|
||||
#define to_fsl_re_dma_desc(tx) container_of(tx, struct fsl_re_desc, async_tx)
|
||||
|
||||
/* Add descriptors into per chan software queue - submit_q */
|
||||
static dma_cookie_t fsl_re_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
struct fsl_re_desc *desc;
|
||||
struct fsl_re_chan *re_chan;
|
||||
dma_cookie_t cookie;
|
||||
unsigned long flags;
|
||||
|
||||
desc = to_fsl_re_dma_desc(tx);
|
||||
re_chan = container_of(tx->chan, struct fsl_re_chan, chan);
|
||||
|
||||
spin_lock_irqsave(&re_chan->desc_lock, flags);
|
||||
cookie = dma_cookie_assign(tx);
|
||||
list_add_tail(&desc->node, &re_chan->submit_q);
|
||||
spin_unlock_irqrestore(&re_chan->desc_lock, flags);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/* Copy descriptor from per chan software queue into hardware job ring */
|
||||
static void fsl_re_issue_pending(struct dma_chan *chan)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
int avail;
|
||||
struct fsl_re_desc *desc, *_desc;
|
||||
unsigned long flags;
|
||||
|
||||
re_chan = container_of(chan, struct fsl_re_chan, chan);
|
||||
|
||||
spin_lock_irqsave(&re_chan->desc_lock, flags);
|
||||
avail = FSL_RE_SLOT_AVAIL(
|
||||
in_be32(&re_chan->jrregs->inbring_slot_avail));
|
||||
|
||||
list_for_each_entry_safe(desc, _desc, &re_chan->submit_q, node) {
|
||||
if (!avail)
|
||||
break;
|
||||
|
||||
list_move_tail(&desc->node, &re_chan->active_q);
|
||||
|
||||
memcpy(&re_chan->inb_ring_virt_addr[re_chan->inb_count],
|
||||
&desc->hwdesc, sizeof(struct fsl_re_hw_desc));
|
||||
|
||||
re_chan->inb_count = (re_chan->inb_count + 1) &
|
||||
FSL_RE_RING_SIZE_MASK;
|
||||
out_be32(&re_chan->jrregs->inbring_add_job, FSL_RE_ADD_JOB(1));
|
||||
avail--;
|
||||
}
|
||||
spin_unlock_irqrestore(&re_chan->desc_lock, flags);
|
||||
}
|
||||
|
||||
static void fsl_re_desc_done(struct fsl_re_desc *desc)
|
||||
{
|
||||
dma_async_tx_callback callback;
|
||||
void *callback_param;
|
||||
|
||||
dma_cookie_complete(&desc->async_tx);
|
||||
|
||||
callback = desc->async_tx.callback;
|
||||
callback_param = desc->async_tx.callback_param;
|
||||
if (callback)
|
||||
callback(callback_param);
|
||||
|
||||
dma_descriptor_unmap(&desc->async_tx);
|
||||
}
|
||||
|
||||
static void fsl_re_cleanup_descs(struct fsl_re_chan *re_chan)
|
||||
{
|
||||
struct fsl_re_desc *desc, *_desc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&re_chan->desc_lock, flags);
|
||||
list_for_each_entry_safe(desc, _desc, &re_chan->ack_q, node) {
|
||||
if (async_tx_test_ack(&desc->async_tx))
|
||||
list_move_tail(&desc->node, &re_chan->free_q);
|
||||
}
|
||||
spin_unlock_irqrestore(&re_chan->desc_lock, flags);
|
||||
|
||||
fsl_re_issue_pending(&re_chan->chan);
|
||||
}
|
||||
|
||||
static void fsl_re_dequeue(unsigned long data)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
struct fsl_re_desc *desc, *_desc;
|
||||
struct fsl_re_hw_desc *hwdesc;
|
||||
unsigned long flags;
|
||||
unsigned int count, oub_count;
|
||||
int found;
|
||||
|
||||
re_chan = dev_get_drvdata((struct device *)data);
|
||||
|
||||
fsl_re_cleanup_descs(re_chan);
|
||||
|
||||
spin_lock_irqsave(&re_chan->desc_lock, flags);
|
||||
count = FSL_RE_SLOT_FULL(in_be32(&re_chan->jrregs->oubring_slot_full));
|
||||
while (count--) {
|
||||
found = 0;
|
||||
hwdesc = &re_chan->oub_ring_virt_addr[re_chan->oub_count];
|
||||
list_for_each_entry_safe(desc, _desc, &re_chan->active_q,
|
||||
node) {
|
||||
/* compare the hw dma addr to find the completed */
|
||||
if (desc->hwdesc.lbea32 == hwdesc->lbea32 &&
|
||||
desc->hwdesc.addr_low == hwdesc->addr_low) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
fsl_re_desc_done(desc);
|
||||
list_move_tail(&desc->node, &re_chan->ack_q);
|
||||
} else {
|
||||
dev_err(re_chan->dev,
|
||||
"found hwdesc not in sw queue, discard it\n");
|
||||
}
|
||||
|
||||
oub_count = (re_chan->oub_count + 1) & FSL_RE_RING_SIZE_MASK;
|
||||
re_chan->oub_count = oub_count;
|
||||
|
||||
out_be32(&re_chan->jrregs->oubring_job_rmvd,
|
||||
FSL_RE_RMVD_JOB(1));
|
||||
}
|
||||
spin_unlock_irqrestore(&re_chan->desc_lock, flags);
|
||||
}
|
||||
|
||||
/* Per Job Ring interrupt handler */
|
||||
static irqreturn_t fsl_re_isr(int irq, void *data)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
u32 irqstate, status;
|
||||
|
||||
re_chan = dev_get_drvdata((struct device *)data);
|
||||
|
||||
irqstate = in_be32(&re_chan->jrregs->jr_interrupt_status);
|
||||
if (!irqstate)
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* There's no way in upper layer (read MD layer) to recover from
|
||||
* error conditions except restart everything. In long term we
|
||||
* need to do something more than just crashing
|
||||
*/
|
||||
if (irqstate & FSL_RE_ERROR) {
|
||||
status = in_be32(&re_chan->jrregs->jr_status);
|
||||
dev_err(re_chan->dev, "chan error irqstate: %x, status: %x\n",
|
||||
irqstate, status);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
out_be32(&re_chan->jrregs->jr_interrupt_status, FSL_RE_CLR_INTR);
|
||||
|
||||
tasklet_schedule(&re_chan->irqtask);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static enum dma_status fsl_re_tx_status(struct dma_chan *chan,
|
||||
dma_cookie_t cookie,
|
||||
struct dma_tx_state *txstate)
|
||||
{
|
||||
return dma_cookie_status(chan, cookie, txstate);
|
||||
}
|
||||
|
||||
static void fill_cfd_frame(struct fsl_re_cmpnd_frame *cf, u8 index,
|
||||
size_t length, dma_addr_t addr, bool final)
|
||||
{
|
||||
u32 efrl = length & FSL_RE_CF_LENGTH_MASK;
|
||||
|
||||
efrl |= final << FSL_RE_CF_FINAL_SHIFT;
|
||||
cf[index].efrl32 = efrl;
|
||||
cf[index].addr_high = upper_32_bits(addr);
|
||||
cf[index].addr_low = lower_32_bits(addr);
|
||||
}
|
||||
|
||||
static struct fsl_re_desc *fsl_re_init_desc(struct fsl_re_chan *re_chan,
|
||||
struct fsl_re_desc *desc,
|
||||
void *cf, dma_addr_t paddr)
|
||||
{
|
||||
desc->re_chan = re_chan;
|
||||
desc->async_tx.tx_submit = fsl_re_tx_submit;
|
||||
dma_async_tx_descriptor_init(&desc->async_tx, &re_chan->chan);
|
||||
INIT_LIST_HEAD(&desc->node);
|
||||
|
||||
desc->hwdesc.fmt32 = FSL_RE_FRAME_FORMAT << FSL_RE_HWDESC_FMT_SHIFT;
|
||||
desc->hwdesc.lbea32 = upper_32_bits(paddr);
|
||||
desc->hwdesc.addr_low = lower_32_bits(paddr);
|
||||
desc->cf_addr = cf;
|
||||
desc->cf_paddr = paddr;
|
||||
|
||||
desc->cdb_addr = (void *)(cf + FSL_RE_CF_DESC_SIZE);
|
||||
desc->cdb_paddr = paddr + FSL_RE_CF_DESC_SIZE;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static struct fsl_re_desc *fsl_re_chan_alloc_desc(struct fsl_re_chan *re_chan,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct fsl_re_desc *desc = NULL;
|
||||
void *cf;
|
||||
dma_addr_t paddr;
|
||||
unsigned long lock_flag;
|
||||
|
||||
fsl_re_cleanup_descs(re_chan);
|
||||
|
||||
spin_lock_irqsave(&re_chan->desc_lock, lock_flag);
|
||||
if (!list_empty(&re_chan->free_q)) {
|
||||
/* take one desc from free_q */
|
||||
desc = list_first_entry(&re_chan->free_q,
|
||||
struct fsl_re_desc, node);
|
||||
list_del(&desc->node);
|
||||
|
||||
desc->async_tx.flags = flags;
|
||||
}
|
||||
spin_unlock_irqrestore(&re_chan->desc_lock, lock_flag);
|
||||
|
||||
if (!desc) {
|
||||
desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
cf = dma_pool_alloc(re_chan->re_dev->cf_desc_pool, GFP_NOWAIT,
|
||||
&paddr);
|
||||
if (!cf) {
|
||||
kfree(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = fsl_re_init_desc(re_chan, desc, cf, paddr);
|
||||
desc->async_tx.flags = flags;
|
||||
|
||||
spin_lock_irqsave(&re_chan->desc_lock, lock_flag);
|
||||
re_chan->alloc_count++;
|
||||
spin_unlock_irqrestore(&re_chan->desc_lock, lock_flag);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *fsl_re_prep_dma_genq(
|
||||
struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
|
||||
unsigned int src_cnt, const unsigned char *scf, size_t len,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
struct fsl_re_desc *desc;
|
||||
struct fsl_re_xor_cdb *xor;
|
||||
struct fsl_re_cmpnd_frame *cf;
|
||||
u32 cdb;
|
||||
unsigned int i, j;
|
||||
unsigned int save_src_cnt = src_cnt;
|
||||
int cont_q = 0;
|
||||
|
||||
re_chan = container_of(chan, struct fsl_re_chan, chan);
|
||||
if (len > FSL_RE_MAX_DATA_LEN) {
|
||||
dev_err(re_chan->dev, "genq tx length %lu, max length %d\n",
|
||||
len, FSL_RE_MAX_DATA_LEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = fsl_re_chan_alloc_desc(re_chan, flags);
|
||||
if (desc <= 0)
|
||||
return NULL;
|
||||
|
||||
if (scf && (flags & DMA_PREP_CONTINUE)) {
|
||||
cont_q = 1;
|
||||
src_cnt += 1;
|
||||
}
|
||||
|
||||
/* Filling xor CDB */
|
||||
cdb = FSL_RE_XOR_OPCODE << FSL_RE_CDB_OPCODE_SHIFT;
|
||||
cdb |= (src_cnt - 1) << FSL_RE_CDB_NRCS_SHIFT;
|
||||
cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT;
|
||||
cdb |= FSL_RE_INTR_ON_ERROR << FSL_RE_CDB_ERROR_SHIFT;
|
||||
cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT;
|
||||
xor = desc->cdb_addr;
|
||||
xor->cdb32 = cdb;
|
||||
|
||||
if (scf) {
|
||||
/* compute q = src0*coef0^src1*coef1^..., * is GF(8) mult */
|
||||
for (i = 0; i < save_src_cnt; i++)
|
||||
xor->gfm[i] = scf[i];
|
||||
if (cont_q)
|
||||
xor->gfm[i++] = 1;
|
||||
} else {
|
||||
/* compute P, that is XOR all srcs */
|
||||
for (i = 0; i < src_cnt; i++)
|
||||
xor->gfm[i] = 1;
|
||||
}
|
||||
|
||||
/* Filling frame 0 of compound frame descriptor with CDB */
|
||||
cf = desc->cf_addr;
|
||||
fill_cfd_frame(cf, 0, sizeof(*xor), desc->cdb_paddr, 0);
|
||||
|
||||
/* Fill CFD's 1st frame with dest buffer */
|
||||
fill_cfd_frame(cf, 1, len, dest, 0);
|
||||
|
||||
/* Fill CFD's rest of the frames with source buffers */
|
||||
for (i = 2, j = 0; j < save_src_cnt; i++, j++)
|
||||
fill_cfd_frame(cf, i, len, src[j], 0);
|
||||
|
||||
if (cont_q)
|
||||
fill_cfd_frame(cf, i++, len, dest, 0);
|
||||
|
||||
/* Setting the final bit in the last source buffer frame in CFD */
|
||||
cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT;
|
||||
|
||||
return &desc->async_tx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prep function for P parity calculation.In RAID Engine terminology,
|
||||
* XOR calculation is called GenQ calculation done through GenQ command
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *fsl_re_prep_dma_xor(
|
||||
struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
|
||||
unsigned int src_cnt, size_t len, unsigned long flags)
|
||||
{
|
||||
/* NULL let genq take all coef as 1 */
|
||||
return fsl_re_prep_dma_genq(chan, dest, src, src_cnt, NULL, len, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prep function for P/Q parity calculation.In RAID Engine terminology,
|
||||
* P/Q calculation is called GenQQ done through GenQQ command
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *fsl_re_prep_dma_pq(
|
||||
struct dma_chan *chan, dma_addr_t *dest, dma_addr_t *src,
|
||||
unsigned int src_cnt, const unsigned char *scf, size_t len,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
struct fsl_re_desc *desc;
|
||||
struct fsl_re_pq_cdb *pq;
|
||||
struct fsl_re_cmpnd_frame *cf;
|
||||
u32 cdb;
|
||||
u8 *p;
|
||||
int gfmq_len, i, j;
|
||||
unsigned int save_src_cnt = src_cnt;
|
||||
|
||||
re_chan = container_of(chan, struct fsl_re_chan, chan);
|
||||
if (len > FSL_RE_MAX_DATA_LEN) {
|
||||
dev_err(re_chan->dev, "pq tx length is %lu, max length is %d\n",
|
||||
len, FSL_RE_MAX_DATA_LEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* RE requires at least 2 sources, if given only one source, we pass the
|
||||
* second source same as the first one.
|
||||
* With only one source, generating P is meaningless, only generate Q.
|
||||
*/
|
||||
if (src_cnt == 1) {
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
dma_addr_t dma_src[2];
|
||||
unsigned char coef[2];
|
||||
|
||||
dma_src[0] = *src;
|
||||
coef[0] = *scf;
|
||||
dma_src[1] = *src;
|
||||
coef[1] = 0;
|
||||
tx = fsl_re_prep_dma_genq(chan, dest[1], dma_src, 2, coef, len,
|
||||
flags);
|
||||
if (tx)
|
||||
desc = to_fsl_re_dma_desc(tx);
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
/*
|
||||
* During RAID6 array creation, Linux's MD layer gets P and Q
|
||||
* calculated separately in two steps. But our RAID Engine has
|
||||
* the capability to calculate both P and Q with a single command
|
||||
* Hence to merge well with MD layer, we need to provide a hook
|
||||
* here and call re_jq_prep_dma_genq() function
|
||||
*/
|
||||
|
||||
if (flags & DMA_PREP_PQ_DISABLE_P)
|
||||
return fsl_re_prep_dma_genq(chan, dest[1], src, src_cnt,
|
||||
scf, len, flags);
|
||||
|
||||
if (flags & DMA_PREP_CONTINUE)
|
||||
src_cnt += 3;
|
||||
|
||||
desc = fsl_re_chan_alloc_desc(re_chan, flags);
|
||||
if (desc <= 0)
|
||||
return NULL;
|
||||
|
||||
/* Filling GenQQ CDB */
|
||||
cdb = FSL_RE_PQ_OPCODE << FSL_RE_CDB_OPCODE_SHIFT;
|
||||
cdb |= (src_cnt - 1) << FSL_RE_CDB_NRCS_SHIFT;
|
||||
cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT;
|
||||
cdb |= FSL_RE_BUFFER_OUTPUT << FSL_RE_CDB_BUFFER_SHIFT;
|
||||
cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT;
|
||||
|
||||
pq = desc->cdb_addr;
|
||||
pq->cdb32 = cdb;
|
||||
|
||||
p = pq->gfm_q1;
|
||||
/* Init gfm_q1[] */
|
||||
for (i = 0; i < src_cnt; i++)
|
||||
p[i] = 1;
|
||||
|
||||
/* Align gfm[] to 32bit */
|
||||
gfmq_len = ALIGN(src_cnt, 4);
|
||||
|
||||
/* Init gfm_q2[] */
|
||||
p += gfmq_len;
|
||||
for (i = 0; i < src_cnt; i++)
|
||||
p[i] = scf[i];
|
||||
|
||||
/* Filling frame 0 of compound frame descriptor with CDB */
|
||||
cf = desc->cf_addr;
|
||||
fill_cfd_frame(cf, 0, sizeof(struct fsl_re_pq_cdb), desc->cdb_paddr, 0);
|
||||
|
||||
/* Fill CFD's 1st & 2nd frame with dest buffers */
|
||||
for (i = 1, j = 0; i < 3; i++, j++)
|
||||
fill_cfd_frame(cf, i, len, dest[j], 0);
|
||||
|
||||
/* Fill CFD's rest of the frames with source buffers */
|
||||
for (i = 3, j = 0; j < save_src_cnt; i++, j++)
|
||||
fill_cfd_frame(cf, i, len, src[j], 0);
|
||||
|
||||
/* PQ computation continuation */
|
||||
if (flags & DMA_PREP_CONTINUE) {
|
||||
if (src_cnt - save_src_cnt == 3) {
|
||||
p[save_src_cnt] = 0;
|
||||
p[save_src_cnt + 1] = 0;
|
||||
p[save_src_cnt + 2] = 1;
|
||||
fill_cfd_frame(cf, i++, len, dest[0], 0);
|
||||
fill_cfd_frame(cf, i++, len, dest[1], 0);
|
||||
fill_cfd_frame(cf, i++, len, dest[1], 0);
|
||||
} else {
|
||||
dev_err(re_chan->dev, "PQ tx continuation error!\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setting the final bit in the last source buffer frame in CFD */
|
||||
cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT;
|
||||
|
||||
return &desc->async_tx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prep function for memcpy. In RAID Engine, memcpy is done through MOVE
|
||||
* command. Logic of this function will need to be modified once multipage
|
||||
* support is added in Linux's MD/ASYNC Layer
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *fsl_re_prep_dma_memcpy(
|
||||
struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
||||
size_t len, unsigned long flags)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
struct fsl_re_desc *desc;
|
||||
size_t length;
|
||||
struct fsl_re_cmpnd_frame *cf;
|
||||
struct fsl_re_move_cdb *move;
|
||||
u32 cdb;
|
||||
|
||||
re_chan = container_of(chan, struct fsl_re_chan, chan);
|
||||
|
||||
if (len > FSL_RE_MAX_DATA_LEN) {
|
||||
dev_err(re_chan->dev, "cp tx length is %lu, max length is %d\n",
|
||||
len, FSL_RE_MAX_DATA_LEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = fsl_re_chan_alloc_desc(re_chan, flags);
|
||||
if (desc <= 0)
|
||||
return NULL;
|
||||
|
||||
/* Filling move CDB */
|
||||
cdb = FSL_RE_MOVE_OPCODE << FSL_RE_CDB_OPCODE_SHIFT;
|
||||
cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT;
|
||||
cdb |= FSL_RE_INTR_ON_ERROR << FSL_RE_CDB_ERROR_SHIFT;
|
||||
cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT;
|
||||
|
||||
move = desc->cdb_addr;
|
||||
move->cdb32 = cdb;
|
||||
|
||||
/* Filling frame 0 of CFD with move CDB */
|
||||
cf = desc->cf_addr;
|
||||
fill_cfd_frame(cf, 0, sizeof(*move), desc->cdb_paddr, 0);
|
||||
|
||||
length = min_t(size_t, len, FSL_RE_MAX_DATA_LEN);
|
||||
|
||||
/* Fill CFD's 1st frame with dest buffer */
|
||||
fill_cfd_frame(cf, 1, length, dest, 0);
|
||||
|
||||
/* Fill CFD's 2nd frame with src buffer */
|
||||
fill_cfd_frame(cf, 2, length, src, 1);
|
||||
|
||||
return &desc->async_tx;
|
||||
}
|
||||
|
||||
static int fsl_re_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
struct fsl_re_desc *desc;
|
||||
void *cf;
|
||||
dma_addr_t paddr;
|
||||
int i;
|
||||
|
||||
re_chan = container_of(chan, struct fsl_re_chan, chan);
|
||||
for (i = 0; i < FSL_RE_MIN_DESCS; i++) {
|
||||
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
break;
|
||||
|
||||
cf = dma_pool_alloc(re_chan->re_dev->cf_desc_pool, GFP_KERNEL,
|
||||
&paddr);
|
||||
if (!cf) {
|
||||
kfree(desc);
|
||||
break;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&desc->node);
|
||||
fsl_re_init_desc(re_chan, desc, cf, paddr);
|
||||
|
||||
list_add_tail(&desc->node, &re_chan->free_q);
|
||||
re_chan->alloc_count++;
|
||||
}
|
||||
return re_chan->alloc_count;
|
||||
}
|
||||
|
||||
static void fsl_re_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct fsl_re_chan *re_chan;
|
||||
struct fsl_re_desc *desc;
|
||||
|
||||
re_chan = container_of(chan, struct fsl_re_chan, chan);
|
||||
while (re_chan->alloc_count--) {
|
||||
desc = list_first_entry(&re_chan->free_q,
|
||||
struct fsl_re_desc,
|
||||
node);
|
||||
|
||||
list_del(&desc->node);
|
||||
dma_pool_free(re_chan->re_dev->cf_desc_pool, desc->cf_addr,
|
||||
desc->cf_paddr);
|
||||
kfree(desc);
|
||||
}
|
||||
|
||||
if (!list_empty(&re_chan->free_q))
|
||||
dev_err(re_chan->dev, "chan resource cannot be cleaned!\n");
|
||||
}
|
||||
|
||||
static int fsl_re_chan_probe(struct platform_device *ofdev,
|
||||
struct device_node *np, u8 q, u32 off)
|
||||
{
|
||||
struct device *dev, *chandev;
|
||||
struct fsl_re_drv_private *re_priv;
|
||||
struct fsl_re_chan *chan;
|
||||
struct dma_device *dma_dev;
|
||||
u32 ptr;
|
||||
u32 status;
|
||||
int ret = 0, rc;
|
||||
struct platform_device *chan_ofdev;
|
||||
|
||||
dev = &ofdev->dev;
|
||||
re_priv = dev_get_drvdata(dev);
|
||||
dma_dev = &re_priv->dma_dev;
|
||||
|
||||
chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL);
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
/* create platform device for chan node */
|
||||
chan_ofdev = of_platform_device_create(np, NULL, dev);
|
||||
if (!chan_ofdev) {
|
||||
dev_err(dev, "Not able to create ofdev for jr %d\n", q);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* read reg property from dts */
|
||||
rc = of_property_read_u32(np, "reg", &ptr);
|
||||
if (rc) {
|
||||
dev_err(dev, "Reg property not found in jr %d\n", q);
|
||||
ret = -ENODEV;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
chan->jrregs = (struct fsl_re_chan_cfg *)((u8 *)re_priv->re_regs +
|
||||
off + ptr);
|
||||
|
||||
/* read irq property from dts */
|
||||
chan->irq = irq_of_parse_and_map(np, 0);
|
||||
if (chan->irq == NO_IRQ) {
|
||||
dev_err(dev, "No IRQ defined for JR %d\n", q);
|
||||
ret = -ENODEV;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
snprintf(chan->name, sizeof(chan->name), "re_jr%02d", q);
|
||||
|
||||
chandev = &chan_ofdev->dev;
|
||||
tasklet_init(&chan->irqtask, fsl_re_dequeue, (unsigned long)chandev);
|
||||
|
||||
ret = request_irq(chan->irq, fsl_re_isr, 0, chan->name, chandev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to register interrupt for JR %d\n", q);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
re_priv->re_jrs[q] = chan;
|
||||
chan->chan.device = dma_dev;
|
||||
chan->chan.private = chan;
|
||||
chan->dev = chandev;
|
||||
chan->re_dev = re_priv;
|
||||
|
||||
spin_lock_init(&chan->desc_lock);
|
||||
INIT_LIST_HEAD(&chan->ack_q);
|
||||
INIT_LIST_HEAD(&chan->active_q);
|
||||
INIT_LIST_HEAD(&chan->submit_q);
|
||||
INIT_LIST_HEAD(&chan->free_q);
|
||||
|
||||
chan->inb_ring_virt_addr = dma_pool_alloc(chan->re_dev->hw_desc_pool,
|
||||
GFP_KERNEL, &chan->inb_phys_addr);
|
||||
if (!chan->inb_ring_virt_addr) {
|
||||
dev_err(dev, "No dma memory for inb_ring_virt_addr\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
chan->oub_ring_virt_addr = dma_pool_alloc(chan->re_dev->hw_desc_pool,
|
||||
GFP_KERNEL, &chan->oub_phys_addr);
|
||||
if (!chan->oub_ring_virt_addr) {
|
||||
dev_err(dev, "No dma memory for oub_ring_virt_addr\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_free_1;
|
||||
}
|
||||
|
||||
/* Program the Inbound/Outbound ring base addresses and size */
|
||||
out_be32(&chan->jrregs->inbring_base_h,
|
||||
chan->inb_phys_addr & FSL_RE_ADDR_BIT_MASK);
|
||||
out_be32(&chan->jrregs->oubring_base_h,
|
||||
chan->oub_phys_addr & FSL_RE_ADDR_BIT_MASK);
|
||||
out_be32(&chan->jrregs->inbring_base_l,
|
||||
chan->inb_phys_addr >> FSL_RE_ADDR_BIT_SHIFT);
|
||||
out_be32(&chan->jrregs->oubring_base_l,
|
||||
chan->oub_phys_addr >> FSL_RE_ADDR_BIT_SHIFT);
|
||||
out_be32(&chan->jrregs->inbring_size,
|
||||
FSL_RE_RING_SIZE << FSL_RE_RING_SIZE_SHIFT);
|
||||
out_be32(&chan->jrregs->oubring_size,
|
||||
FSL_RE_RING_SIZE << FSL_RE_RING_SIZE_SHIFT);
|
||||
|
||||
/* Read LIODN value from u-boot */
|
||||
status = in_be32(&chan->jrregs->jr_config_1) & FSL_RE_REG_LIODN_MASK;
|
||||
|
||||
/* Program the CFG reg */
|
||||
out_be32(&chan->jrregs->jr_config_1,
|
||||
FSL_RE_CFG1_CBSI | FSL_RE_CFG1_CBS0 | status);
|
||||
|
||||
dev_set_drvdata(chandev, chan);
|
||||
|
||||
/* Enable RE/CHAN */
|
||||
out_be32(&chan->jrregs->jr_command, FSL_RE_ENABLE);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_1:
|
||||
dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr,
|
||||
chan->inb_phys_addr);
|
||||
err_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Probe function for RAID Engine */
|
||||
static int fsl_re_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct fsl_re_drv_private *re_priv;
|
||||
struct device_node *np;
|
||||
struct device_node *child;
|
||||
u32 off;
|
||||
u8 ridx = 0;
|
||||
struct dma_device *dma_dev;
|
||||
struct resource *res;
|
||||
int rc;
|
||||
struct device *dev = &ofdev->dev;
|
||||
|
||||
re_priv = devm_kzalloc(dev, sizeof(*re_priv), GFP_KERNEL);
|
||||
if (!re_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
/* IOMAP the entire RAID Engine region */
|
||||
re_priv->re_regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!re_priv->re_regs)
|
||||
return -EBUSY;
|
||||
|
||||
/* Program the RE mode */
|
||||
out_be32(&re_priv->re_regs->global_config, FSL_RE_NON_DPAA_MODE);
|
||||
|
||||
/* Program Galois Field polynomial */
|
||||
out_be32(&re_priv->re_regs->galois_field_config, FSL_RE_GFM_POLY);
|
||||
|
||||
dev_info(dev, "version %x, mode %x, gfp %x\n",
|
||||
in_be32(&re_priv->re_regs->re_version_id),
|
||||
in_be32(&re_priv->re_regs->global_config),
|
||||
in_be32(&re_priv->re_regs->galois_field_config));
|
||||
|
||||
dma_dev = &re_priv->dma_dev;
|
||||
dma_dev->dev = dev;
|
||||
INIT_LIST_HEAD(&dma_dev->channels);
|
||||
dma_set_mask(dev, DMA_BIT_MASK(40));
|
||||
|
||||
dma_dev->device_alloc_chan_resources = fsl_re_alloc_chan_resources;
|
||||
dma_dev->device_tx_status = fsl_re_tx_status;
|
||||
dma_dev->device_issue_pending = fsl_re_issue_pending;
|
||||
|
||||
dma_dev->max_xor = FSL_RE_MAX_XOR_SRCS;
|
||||
dma_dev->device_prep_dma_xor = fsl_re_prep_dma_xor;
|
||||
dma_cap_set(DMA_XOR, dma_dev->cap_mask);
|
||||
|
||||
dma_dev->max_pq = FSL_RE_MAX_PQ_SRCS;
|
||||
dma_dev->device_prep_dma_pq = fsl_re_prep_dma_pq;
|
||||
dma_cap_set(DMA_PQ, dma_dev->cap_mask);
|
||||
|
||||
dma_dev->device_prep_dma_memcpy = fsl_re_prep_dma_memcpy;
|
||||
dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
|
||||
|
||||
dma_dev->device_free_chan_resources = fsl_re_free_chan_resources;
|
||||
|
||||
re_priv->total_chans = 0;
|
||||
|
||||
re_priv->cf_desc_pool = dmam_pool_create("fsl_re_cf_desc_pool", dev,
|
||||
FSL_RE_CF_CDB_SIZE,
|
||||
FSL_RE_CF_CDB_ALIGN, 0);
|
||||
|
||||
if (!re_priv->cf_desc_pool) {
|
||||
dev_err(dev, "No memory for fsl re_cf desc pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
re_priv->hw_desc_pool = dmam_pool_create("fsl_re_hw_desc_pool", dev,
|
||||
sizeof(struct fsl_re_hw_desc) * FSL_RE_RING_SIZE,
|
||||
FSL_RE_FRAME_ALIGN, 0);
|
||||
if (!re_priv->hw_desc_pool) {
|
||||
dev_err(dev, "No memory for fsl re_hw desc pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, re_priv);
|
||||
|
||||
/* Parse Device tree to find out the total number of JQs present */
|
||||
for_each_compatible_node(np, NULL, "fsl,raideng-v1.0-job-queue") {
|
||||
rc = of_property_read_u32(np, "reg", &off);
|
||||
if (rc) {
|
||||
dev_err(dev, "Reg property not found in JQ node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Find out the Job Rings present under each JQ */
|
||||
for_each_child_of_node(np, child) {
|
||||
rc = of_device_is_compatible(child,
|
||||
"fsl,raideng-v1.0-job-ring");
|
||||
if (rc) {
|
||||
fsl_re_chan_probe(ofdev, child, ridx++, off);
|
||||
re_priv->total_chans++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dma_async_device_register(dma_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_re_remove_chan(struct fsl_re_chan *chan)
|
||||
{
|
||||
dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr,
|
||||
chan->inb_phys_addr);
|
||||
|
||||
dma_pool_free(chan->re_dev->hw_desc_pool, chan->oub_ring_virt_addr,
|
||||
chan->oub_phys_addr);
|
||||
}
|
||||
|
||||
static int fsl_re_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct fsl_re_drv_private *re_priv;
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
dev = &ofdev->dev;
|
||||
re_priv = dev_get_drvdata(dev);
|
||||
|
||||
/* Cleanup chan related memory areas */
|
||||
for (i = 0; i < re_priv->total_chans; i++)
|
||||
fsl_re_remove_chan(re_priv->re_jrs[i]);
|
||||
|
||||
/* Unregister the driver */
|
||||
dma_async_device_unregister(&re_priv->dma_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id fsl_re_ids[] = {
|
||||
{ .compatible = "fsl,raideng-v1.0", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_re_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-raideng",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fsl_re_ids,
|
||||
},
|
||||
.probe = fsl_re_probe,
|
||||
.remove = fsl_re_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(fsl_re_driver);
|
||||
|
||||
MODULE_AUTHOR("Harninder Rai <harninder.rai@freescale.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Freescale RAID Engine Device Driver");
|
306
drivers/dma/fsl_raid.h
Normal file
306
drivers/dma/fsl_raid.h
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* drivers/dma/fsl_raid.h
|
||||
*
|
||||
* Freescale RAID Engine device driver
|
||||
*
|
||||
* Author:
|
||||
* Harninder Rai <harninder.rai@freescale.com>
|
||||
* Naveen Burmi <naveenburmi@freescale.com>
|
||||
*
|
||||
* Rewrite:
|
||||
* Xuelin Shi <xuelin.shi@freescale.com>
|
||||
|
||||
* Copyright (c) 2010-2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Freescale Semiconductor nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FSL_RE_MAX_CHANS 4
|
||||
#define FSL_RE_DPAA_MODE BIT(30)
|
||||
#define FSL_RE_NON_DPAA_MODE BIT(31)
|
||||
#define FSL_RE_GFM_POLY 0x1d000000
|
||||
#define FSL_RE_ADD_JOB(x) ((x) << 16)
|
||||
#define FSL_RE_RMVD_JOB(x) ((x) << 16)
|
||||
#define FSL_RE_CFG1_CBSI 0x08000000
|
||||
#define FSL_RE_CFG1_CBS0 0x00080000
|
||||
#define FSL_RE_SLOT_FULL_SHIFT 8
|
||||
#define FSL_RE_SLOT_FULL(x) ((x) >> FSL_RE_SLOT_FULL_SHIFT)
|
||||
#define FSL_RE_SLOT_AVAIL_SHIFT 8
|
||||
#define FSL_RE_SLOT_AVAIL(x) ((x) >> FSL_RE_SLOT_AVAIL_SHIFT)
|
||||
#define FSL_RE_PQ_OPCODE 0x1B
|
||||
#define FSL_RE_XOR_OPCODE 0x1A
|
||||
#define FSL_RE_MOVE_OPCODE 0x8
|
||||
#define FSL_RE_FRAME_ALIGN 16
|
||||
#define FSL_RE_BLOCK_SIZE 0x3 /* 4096 bytes */
|
||||
#define FSL_RE_CACHEABLE_IO 0x0
|
||||
#define FSL_RE_BUFFER_OUTPUT 0x0
|
||||
#define FSL_RE_INTR_ON_ERROR 0x1
|
||||
#define FSL_RE_DATA_DEP 0x1
|
||||
#define FSL_RE_ENABLE_DPI 0x0
|
||||
#define FSL_RE_RING_SIZE 0x400
|
||||
#define FSL_RE_RING_SIZE_MASK (FSL_RE_RING_SIZE - 1)
|
||||
#define FSL_RE_RING_SIZE_SHIFT 8
|
||||
#define FSL_RE_ADDR_BIT_SHIFT 4
|
||||
#define FSL_RE_ADDR_BIT_MASK (BIT(FSL_RE_ADDR_BIT_SHIFT) - 1)
|
||||
#define FSL_RE_ERROR 0x40000000
|
||||
#define FSL_RE_INTR 0x80000000
|
||||
#define FSL_RE_CLR_INTR 0x80000000
|
||||
#define FSL_RE_PAUSE 0x80000000
|
||||
#define FSL_RE_ENABLE 0x80000000
|
||||
#define FSL_RE_REG_LIODN_MASK 0x00000FFF
|
||||
|
||||
#define FSL_RE_CDB_OPCODE_MASK 0xF8000000
|
||||
#define FSL_RE_CDB_OPCODE_SHIFT 27
|
||||
#define FSL_RE_CDB_EXCLEN_MASK 0x03000000
|
||||
#define FSL_RE_CDB_EXCLEN_SHIFT 24
|
||||
#define FSL_RE_CDB_EXCLQ1_MASK 0x00F00000
|
||||
#define FSL_RE_CDB_EXCLQ1_SHIFT 20
|
||||
#define FSL_RE_CDB_EXCLQ2_MASK 0x000F0000
|
||||
#define FSL_RE_CDB_EXCLQ2_SHIFT 16
|
||||
#define FSL_RE_CDB_BLKSIZE_MASK 0x0000C000
|
||||
#define FSL_RE_CDB_BLKSIZE_SHIFT 14
|
||||
#define FSL_RE_CDB_CACHE_MASK 0x00003000
|
||||
#define FSL_RE_CDB_CACHE_SHIFT 12
|
||||
#define FSL_RE_CDB_BUFFER_MASK 0x00000800
|
||||
#define FSL_RE_CDB_BUFFER_SHIFT 11
|
||||
#define FSL_RE_CDB_ERROR_MASK 0x00000400
|
||||
#define FSL_RE_CDB_ERROR_SHIFT 10
|
||||
#define FSL_RE_CDB_NRCS_MASK 0x0000003C
|
||||
#define FSL_RE_CDB_NRCS_SHIFT 6
|
||||
#define FSL_RE_CDB_DEPEND_MASK 0x00000008
|
||||
#define FSL_RE_CDB_DEPEND_SHIFT 3
|
||||
#define FSL_RE_CDB_DPI_MASK 0x00000004
|
||||
#define FSL_RE_CDB_DPI_SHIFT 2
|
||||
|
||||
/*
|
||||
* the largest cf block is 19*sizeof(struct cmpnd_frame), which is 304 bytes.
|
||||
* here 19 = 1(cdb)+2(dest)+16(src), align to 64bytes, that is 320 bytes.
|
||||
* the largest cdb block: struct pq_cdb which is 180 bytes, adding to cf block
|
||||
* 320+180=500, align to 64bytes, that is 512 bytes.
|
||||
*/
|
||||
#define FSL_RE_CF_DESC_SIZE 320
|
||||
#define FSL_RE_CF_CDB_SIZE 512
|
||||
#define FSL_RE_CF_CDB_ALIGN 64
|
||||
|
||||
struct fsl_re_ctrl {
|
||||
/* General Configuration Registers */
|
||||
__be32 global_config; /* Global Configuration Register */
|
||||
u8 rsvd1[4];
|
||||
__be32 galois_field_config; /* Galois Field Configuration Register */
|
||||
u8 rsvd2[4];
|
||||
__be32 jq_wrr_config; /* WRR Configuration register */
|
||||
u8 rsvd3[4];
|
||||
__be32 crc_config; /* CRC Configuration register */
|
||||
u8 rsvd4[228];
|
||||
__be32 system_reset; /* System Reset Register */
|
||||
u8 rsvd5[252];
|
||||
__be32 global_status; /* Global Status Register */
|
||||
u8 rsvd6[832];
|
||||
__be32 re_liodn_base; /* LIODN Base Register */
|
||||
u8 rsvd7[1712];
|
||||
__be32 re_version_id; /* Version ID register of RE */
|
||||
__be32 re_version_id_2; /* Version ID 2 register of RE */
|
||||
u8 rsvd8[512];
|
||||
__be32 host_config; /* Host I/F Configuration Register */
|
||||
};
|
||||
|
||||
struct fsl_re_chan_cfg {
|
||||
/* Registers for JR interface */
|
||||
__be32 jr_config_0; /* Job Queue Configuration 0 Register */
|
||||
__be32 jr_config_1; /* Job Queue Configuration 1 Register */
|
||||
__be32 jr_interrupt_status; /* Job Queue Interrupt Status Register */
|
||||
u8 rsvd1[4];
|
||||
__be32 jr_command; /* Job Queue Command Register */
|
||||
u8 rsvd2[4];
|
||||
__be32 jr_status; /* Job Queue Status Register */
|
||||
u8 rsvd3[228];
|
||||
|
||||
/* Input Ring */
|
||||
__be32 inbring_base_h; /* Inbound Ring Base Address Register - High */
|
||||
__be32 inbring_base_l; /* Inbound Ring Base Address Register - Low */
|
||||
__be32 inbring_size; /* Inbound Ring Size Register */
|
||||
u8 rsvd4[4];
|
||||
__be32 inbring_slot_avail; /* Inbound Ring Slot Available Register */
|
||||
u8 rsvd5[4];
|
||||
__be32 inbring_add_job; /* Inbound Ring Add Job Register */
|
||||
u8 rsvd6[4];
|
||||
__be32 inbring_cnsmr_indx; /* Inbound Ring Consumer Index Register */
|
||||
u8 rsvd7[220];
|
||||
|
||||
/* Output Ring */
|
||||
__be32 oubring_base_h; /* Outbound Ring Base Address Register - High */
|
||||
__be32 oubring_base_l; /* Outbound Ring Base Address Register - Low */
|
||||
__be32 oubring_size; /* Outbound Ring Size Register */
|
||||
u8 rsvd8[4];
|
||||
__be32 oubring_job_rmvd; /* Outbound Ring Job Removed Register */
|
||||
u8 rsvd9[4];
|
||||
__be32 oubring_slot_full; /* Outbound Ring Slot Full Register */
|
||||
u8 rsvd10[4];
|
||||
__be32 oubring_prdcr_indx; /* Outbound Ring Producer Index */
|
||||
};
|
||||
|
||||
/*
|
||||
* Command Descriptor Block (CDB) for unicast move command.
|
||||
* In RAID Engine terms, memcpy is done through move command
|
||||
*/
|
||||
struct fsl_re_move_cdb {
|
||||
__be32 cdb32;
|
||||
};
|
||||
|
||||
/* Data protection/integrity related fields */
|
||||
#define FSL_RE_DPI_APPS_MASK 0xC0000000
|
||||
#define FSL_RE_DPI_APPS_SHIFT 30
|
||||
#define FSL_RE_DPI_REF_MASK 0x30000000
|
||||
#define FSL_RE_DPI_REF_SHIFT 28
|
||||
#define FSL_RE_DPI_GUARD_MASK 0x0C000000
|
||||
#define FSL_RE_DPI_GUARD_SHIFT 26
|
||||
#define FSL_RE_DPI_ATTR_MASK 0x03000000
|
||||
#define FSL_RE_DPI_ATTR_SHIFT 24
|
||||
#define FSL_RE_DPI_META_MASK 0x0000FFFF
|
||||
|
||||
struct fsl_re_dpi {
|
||||
__be32 dpi32;
|
||||
__be32 ref;
|
||||
};
|
||||
|
||||
/*
|
||||
* CDB for GenQ command. In RAID Engine terminology, XOR is
|
||||
* done through this command
|
||||
*/
|
||||
struct fsl_re_xor_cdb {
|
||||
__be32 cdb32;
|
||||
u8 gfm[16];
|
||||
struct fsl_re_dpi dpi_dest_spec;
|
||||
struct fsl_re_dpi dpi_src_spec[16];
|
||||
};
|
||||
|
||||
/* CDB for no-op command */
|
||||
struct fsl_re_noop_cdb {
|
||||
__be32 cdb32;
|
||||
};
|
||||
|
||||
/*
|
||||
* CDB for GenQQ command. In RAID Engine terminology, P/Q is
|
||||
* done through this command
|
||||
*/
|
||||
struct fsl_re_pq_cdb {
|
||||
__be32 cdb32;
|
||||
u8 gfm_q1[16];
|
||||
u8 gfm_q2[16];
|
||||
struct fsl_re_dpi dpi_dest_spec[2];
|
||||
struct fsl_re_dpi dpi_src_spec[16];
|
||||
};
|
||||
|
||||
/* Compound frame */
|
||||
#define FSL_RE_CF_ADDR_HIGH_MASK 0x000000FF
|
||||
#define FSL_RE_CF_EXT_MASK 0x80000000
|
||||
#define FSL_RE_CF_EXT_SHIFT 31
|
||||
#define FSL_RE_CF_FINAL_MASK 0x40000000
|
||||
#define FSL_RE_CF_FINAL_SHIFT 30
|
||||
#define FSL_RE_CF_LENGTH_MASK 0x000FFFFF
|
||||
#define FSL_RE_CF_BPID_MASK 0x00FF0000
|
||||
#define FSL_RE_CF_BPID_SHIFT 16
|
||||
#define FSL_RE_CF_OFFSET_MASK 0x00001FFF
|
||||
|
||||
struct fsl_re_cmpnd_frame {
|
||||
__be32 addr_high;
|
||||
__be32 addr_low;
|
||||
__be32 efrl32;
|
||||
__be32 rbro32;
|
||||
};
|
||||
|
||||
/* Frame descriptor */
|
||||
#define FSL_RE_HWDESC_LIODN_MASK 0x3F000000
|
||||
#define FSL_RE_HWDESC_LIODN_SHIFT 24
|
||||
#define FSL_RE_HWDESC_BPID_MASK 0x00FF0000
|
||||
#define FSL_RE_HWDESC_BPID_SHIFT 16
|
||||
#define FSL_RE_HWDESC_ELIODN_MASK 0x0000F000
|
||||
#define FSL_RE_HWDESC_ELIODN_SHIFT 12
|
||||
#define FSL_RE_HWDESC_FMT_SHIFT 29
|
||||
#define FSL_RE_HWDESC_FMT_MASK (0x3 << FSL_RE_HWDESC_FMT_SHIFT)
|
||||
|
||||
struct fsl_re_hw_desc {
|
||||
__be32 lbea32;
|
||||
__be32 addr_low;
|
||||
__be32 fmt32;
|
||||
__be32 status;
|
||||
};
|
||||
|
||||
/* Raid Engine device private data */
|
||||
struct fsl_re_drv_private {
|
||||
u8 total_chans;
|
||||
struct dma_device dma_dev;
|
||||
struct fsl_re_ctrl *re_regs;
|
||||
struct fsl_re_chan *re_jrs[FSL_RE_MAX_CHANS];
|
||||
struct dma_pool *cf_desc_pool;
|
||||
struct dma_pool *hw_desc_pool;
|
||||
};
|
||||
|
||||
/* Per job ring data structure */
|
||||
struct fsl_re_chan {
|
||||
char name[16];
|
||||
spinlock_t desc_lock; /* queue lock */
|
||||
struct list_head ack_q; /* wait to acked queue */
|
||||
struct list_head active_q; /* already issued on hw, not completed */
|
||||
struct list_head submit_q;
|
||||
struct list_head free_q; /* alloc available queue */
|
||||
struct device *dev;
|
||||
struct fsl_re_drv_private *re_dev;
|
||||
struct dma_chan chan;
|
||||
struct fsl_re_chan_cfg *jrregs;
|
||||
int irq;
|
||||
struct tasklet_struct irqtask;
|
||||
u32 alloc_count;
|
||||
|
||||
/* hw descriptor ring for inbound queue*/
|
||||
dma_addr_t inb_phys_addr;
|
||||
struct fsl_re_hw_desc *inb_ring_virt_addr;
|
||||
u32 inb_count;
|
||||
|
||||
/* hw descriptor ring for outbound queue */
|
||||
dma_addr_t oub_phys_addr;
|
||||
struct fsl_re_hw_desc *oub_ring_virt_addr;
|
||||
u32 oub_count;
|
||||
};
|
||||
|
||||
/* Async transaction descriptor */
|
||||
struct fsl_re_desc {
|
||||
struct dma_async_tx_descriptor async_tx;
|
||||
struct list_head node;
|
||||
struct fsl_re_hw_desc hwdesc;
|
||||
struct fsl_re_chan *re_chan;
|
||||
|
||||
/* hwdesc will point to cf_addr */
|
||||
void *cf_addr;
|
||||
dma_addr_t cf_paddr;
|
||||
|
||||
void *cdb_addr;
|
||||
dma_addr_t cdb_paddr;
|
||||
int status;
|
||||
};
|
@ -689,11 +689,6 @@ static int mdc_slave_config(struct dma_chan *chan,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdc_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mdc_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct mdc_chan *mchan = to_mdc_chan(chan);
|
||||
@ -910,7 +905,6 @@ static int mdc_dma_probe(struct platform_device *pdev)
|
||||
mdma->dma_dev.device_prep_slave_sg = mdc_prep_slave_sg;
|
||||
mdma->dma_dev.device_prep_dma_cyclic = mdc_prep_dma_cyclic;
|
||||
mdma->dma_dev.device_prep_dma_memcpy = mdc_prep_dma_memcpy;
|
||||
mdma->dma_dev.device_alloc_chan_resources = mdc_alloc_chan_resources;
|
||||
mdma->dma_dev.device_free_chan_resources = mdc_free_chan_resources;
|
||||
mdma->dma_dev.device_tx_status = mdc_tx_status;
|
||||
mdma->dma_dev.device_issue_pending = mdc_issue_pending;
|
||||
|
@ -1260,6 +1260,7 @@ static void sdma_issue_pending(struct dma_chan *chan)
|
||||
|
||||
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34
|
||||
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 38
|
||||
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3 41
|
||||
|
||||
static void sdma_add_scripts(struct sdma_engine *sdma,
|
||||
const struct sdma_script_start_addrs *addr)
|
||||
@ -1306,6 +1307,9 @@ static void sdma_load_firmware(const struct firmware *fw, void *context)
|
||||
case 2:
|
||||
sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2;
|
||||
break;
|
||||
case 3:
|
||||
sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3;
|
||||
break;
|
||||
default:
|
||||
dev_err(sdma->dev, "unknown firmware version\n");
|
||||
goto err_firmware;
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in
|
||||
* the file called "COPYING".
|
||||
*
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in
|
||||
* the file called "COPYING".
|
||||
*
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in
|
||||
* the file called "COPYING".
|
||||
*
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
|
@ -15,10 +15,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in
|
||||
* the file called "COPYING".
|
||||
*
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in
|
||||
* the file called "COPYING".
|
||||
*
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -313,11 +313,6 @@ static void k3_dma_tasklet(unsigned long arg)
|
||||
}
|
||||
}
|
||||
|
||||
static int k3_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void k3_dma_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct k3_dma_chan *c = to_k3_chan(chan);
|
||||
@ -654,7 +649,7 @@ static void k3_dma_free_desc(struct virt_dma_desc *vd)
|
||||
kfree(ds);
|
||||
}
|
||||
|
||||
static struct of_device_id k3_pdma_dt_ids[] = {
|
||||
static const struct of_device_id k3_pdma_dt_ids[] = {
|
||||
{ .compatible = "hisilicon,k3-dma-1.0", },
|
||||
{}
|
||||
};
|
||||
@ -728,7 +723,6 @@ static int k3_dma_probe(struct platform_device *op)
|
||||
dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
|
||||
dma_cap_set(DMA_MEMCPY, d->slave.cap_mask);
|
||||
d->slave.dev = &op->dev;
|
||||
d->slave.device_alloc_chan_resources = k3_dma_alloc_chan_resources;
|
||||
d->slave.device_free_chan_resources = k3_dma_free_chan_resources;
|
||||
d->slave.device_tx_status = k3_dma_tx_status;
|
||||
d->slave.device_prep_dma_memcpy = k3_dma_prep_memcpy;
|
||||
|
@ -973,7 +973,7 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mmp_pdma_dt_ids[] = {
|
||||
static const struct of_device_id mmp_pdma_dt_ids[] = {
|
||||
{ .compatible = "marvell,pdma-1.0", },
|
||||
{}
|
||||
};
|
||||
|
@ -613,7 +613,7 @@ struct dma_chan *mmp_tdma_xlate(struct of_phandle_args *dma_spec,
|
||||
return dma_request_channel(mask, mmp_tdma_filter_fn, ¶m);
|
||||
}
|
||||
|
||||
static struct of_device_id mmp_tdma_dt_ids[] = {
|
||||
static const struct of_device_id mmp_tdma_dt_ids[] = {
|
||||
{ .compatible = "marvell,adma-1.0", .data = (void *)MMP_AUD_TDMA},
|
||||
{ .compatible = "marvell,pxa910-squ", .data = (void *)PXA910_SQU},
|
||||
{}
|
||||
|
@ -21,10 +21,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
@ -1072,7 +1068,7 @@ static int mpc_dma_remove(struct platform_device *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mpc_dma_match[] = {
|
||||
static const struct of_device_id mpc_dma_match[] = {
|
||||
{ .compatible = "fsl,mpc5121-dma", },
|
||||
{ .compatible = "fsl,mpc8308-dma", },
|
||||
{},
|
||||
|
@ -10,10 +10,6 @@
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
@ -1249,7 +1245,7 @@ static int mv_xor_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id mv_xor_dt_ids[] = {
|
||||
static const struct of_device_id mv_xor_dt_ids[] = {
|
||||
{ .compatible = "marvell,orion-xor", },
|
||||
{},
|
||||
};
|
||||
|
@ -9,10 +9,6 @@
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MV_XOR_H
|
||||
|
@ -949,6 +949,7 @@ err_free_res:
|
||||
err_disable_pdev:
|
||||
pci_disable_device(pdev);
|
||||
err_free_mem:
|
||||
kfree(pd);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -556,7 +556,7 @@ static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[],
|
||||
|
||||
buf[0] = CMD_DMAADDH;
|
||||
buf[0] |= (da << 1);
|
||||
*((u16 *)&buf[1]) = val;
|
||||
*((__le16 *)&buf[1]) = cpu_to_le16(val);
|
||||
|
||||
PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n",
|
||||
da == 1 ? "DA" : "SA", val);
|
||||
@ -710,7 +710,7 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[],
|
||||
|
||||
buf[0] = CMD_DMAMOV;
|
||||
buf[1] = dst;
|
||||
*((u32 *)&buf[2]) = val;
|
||||
*((__le32 *)&buf[2]) = cpu_to_le32(val);
|
||||
|
||||
PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n",
|
||||
dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val);
|
||||
@ -888,7 +888,7 @@ static inline u32 _emit_GO(unsigned dry_run, u8 buf[],
|
||||
|
||||
buf[1] = chan & 0x7;
|
||||
|
||||
*((u32 *)&buf[2]) = addr;
|
||||
*((__le32 *)&buf[2]) = cpu_to_le32(addr);
|
||||
|
||||
return SZ_DMAGO;
|
||||
}
|
||||
@ -928,7 +928,7 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd,
|
||||
}
|
||||
writel(val, regs + DBGINST0);
|
||||
|
||||
val = *((u32 *)&insn[2]);
|
||||
val = le32_to_cpu(*((__le32 *)&insn[2]));
|
||||
writel(val, regs + DBGINST1);
|
||||
|
||||
/* If timed out due to halted state-machine */
|
||||
@ -2162,7 +2162,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
|
||||
* DMA transfer again. This pause feature was implemented to
|
||||
* allow safely read residue before channel termination.
|
||||
*/
|
||||
int pl330_pause(struct dma_chan *chan)
|
||||
static int pl330_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct dma_pl330_chan *pch = to_pchan(chan);
|
||||
struct pl330_dmac *pl330 = pch->dmac;
|
||||
@ -2203,8 +2203,8 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
|
||||
pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
|
||||
}
|
||||
|
||||
int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
|
||||
struct dma_pl330_desc *desc)
|
||||
static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
|
||||
struct dma_pl330_desc *desc)
|
||||
{
|
||||
struct pl330_thread *thrd = pch->thread;
|
||||
struct pl330_dmac *pl330 = pch->dmac;
|
||||
@ -2259,7 +2259,17 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
||||
transferred = 0;
|
||||
residual += desc->bytes_requested - transferred;
|
||||
if (desc->txd.cookie == cookie) {
|
||||
ret = desc->status;
|
||||
switch (desc->status) {
|
||||
case DONE:
|
||||
ret = DMA_COMPLETE;
|
||||
break;
|
||||
case PREP:
|
||||
case BUSY:
|
||||
ret = DMA_IN_PROGRESS;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (desc->last)
|
||||
|
@ -16,10 +16,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
|
@ -171,6 +171,35 @@ static const struct reg_offset_data bam_v1_4_reg_info[] = {
|
||||
[BAM_P_FIFO_SIZES] = { 0x1820, 0x00, 0x1000, 0x00 },
|
||||
};
|
||||
|
||||
static const struct reg_offset_data bam_v1_7_reg_info[] = {
|
||||
[BAM_CTRL] = { 0x00000, 0x00, 0x00, 0x00 },
|
||||
[BAM_REVISION] = { 0x01000, 0x00, 0x00, 0x00 },
|
||||
[BAM_NUM_PIPES] = { 0x01008, 0x00, 0x00, 0x00 },
|
||||
[BAM_DESC_CNT_TRSHLD] = { 0x00008, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_SRCS] = { 0x03010, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_SRCS_MSK] = { 0x03014, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_SRCS_UNMASKED] = { 0x03018, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_STTS] = { 0x00014, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_CLR] = { 0x00018, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_EN] = { 0x0001C, 0x00, 0x00, 0x00 },
|
||||
[BAM_CNFG_BITS] = { 0x0007C, 0x00, 0x00, 0x00 },
|
||||
[BAM_IRQ_SRCS_EE] = { 0x03000, 0x00, 0x00, 0x1000 },
|
||||
[BAM_IRQ_SRCS_MSK_EE] = { 0x03004, 0x00, 0x00, 0x1000 },
|
||||
[BAM_P_CTRL] = { 0x13000, 0x1000, 0x00, 0x00 },
|
||||
[BAM_P_RST] = { 0x13004, 0x1000, 0x00, 0x00 },
|
||||
[BAM_P_HALT] = { 0x13008, 0x1000, 0x00, 0x00 },
|
||||
[BAM_P_IRQ_STTS] = { 0x13010, 0x1000, 0x00, 0x00 },
|
||||
[BAM_P_IRQ_CLR] = { 0x13014, 0x1000, 0x00, 0x00 },
|
||||
[BAM_P_IRQ_EN] = { 0x13018, 0x1000, 0x00, 0x00 },
|
||||
[BAM_P_EVNT_DEST_ADDR] = { 0x1382C, 0x00, 0x1000, 0x00 },
|
||||
[BAM_P_EVNT_REG] = { 0x13818, 0x00, 0x1000, 0x00 },
|
||||
[BAM_P_SW_OFSTS] = { 0x13800, 0x00, 0x1000, 0x00 },
|
||||
[BAM_P_DATA_FIFO_ADDR] = { 0x13824, 0x00, 0x1000, 0x00 },
|
||||
[BAM_P_DESC_FIFO_ADDR] = { 0x1381C, 0x00, 0x1000, 0x00 },
|
||||
[BAM_P_EVNT_GEN_TRSHLD] = { 0x13828, 0x00, 0x1000, 0x00 },
|
||||
[BAM_P_FIFO_SIZES] = { 0x13820, 0x00, 0x1000, 0x00 },
|
||||
};
|
||||
|
||||
/* BAM CTRL */
|
||||
#define BAM_SW_RST BIT(0)
|
||||
#define BAM_EN BIT(1)
|
||||
@ -1051,6 +1080,7 @@ static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan,
|
||||
static const struct of_device_id bam_of_match[] = {
|
||||
{ .compatible = "qcom,bam-v1.3.0", .data = &bam_v1_3_reg_info },
|
||||
{ .compatible = "qcom,bam-v1.4.0", .data = &bam_v1_4_reg_info },
|
||||
{ .compatible = "qcom,bam-v1.7.0", .data = &bam_v1_7_reg_info },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1113,7 +1143,7 @@ static int bam_dma_probe(struct platform_device *pdev)
|
||||
|
||||
if (!bdev->channels) {
|
||||
ret = -ENOMEM;
|
||||
goto err_disable_clk;
|
||||
goto err_tasklet_kill;
|
||||
}
|
||||
|
||||
/* allocate and initialize channels */
|
||||
@ -1125,7 +1155,7 @@ static int bam_dma_probe(struct platform_device *pdev)
|
||||
ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq,
|
||||
IRQF_TRIGGER_HIGH, "bam_dma", bdev);
|
||||
if (ret)
|
||||
goto err_disable_clk;
|
||||
goto err_bam_channel_exit;
|
||||
|
||||
/* set max dma segment size */
|
||||
bdev->common.dev = bdev->dev;
|
||||
@ -1133,7 +1163,7 @@ static int bam_dma_probe(struct platform_device *pdev)
|
||||
ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE);
|
||||
if (ret) {
|
||||
dev_err(bdev->dev, "cannot set maximum segment size\n");
|
||||
goto err_disable_clk;
|
||||
goto err_bam_channel_exit;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, bdev);
|
||||
@ -1161,7 +1191,7 @@ static int bam_dma_probe(struct platform_device *pdev)
|
||||
ret = dma_async_device_register(&bdev->common);
|
||||
if (ret) {
|
||||
dev_err(bdev->dev, "failed to register dma async device\n");
|
||||
goto err_disable_clk;
|
||||
goto err_bam_channel_exit;
|
||||
}
|
||||
|
||||
ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate,
|
||||
@ -1173,8 +1203,14 @@ static int bam_dma_probe(struct platform_device *pdev)
|
||||
|
||||
err_unregister_dma:
|
||||
dma_async_device_unregister(&bdev->common);
|
||||
err_bam_channel_exit:
|
||||
for (i = 0; i < bdev->num_channels; i++)
|
||||
tasklet_kill(&bdev->channels[i].vc.task);
|
||||
err_tasklet_kill:
|
||||
tasklet_kill(&bdev->task);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(bdev->bamclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -749,11 +749,6 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c24xx_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s3c24xx_dma_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
/* Ensure all queued descriptors are freed */
|
||||
@ -1238,7 +1233,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev)
|
||||
if (!s3cdma->phy_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
/* aquire irqs and clocks for all physical channels */
|
||||
/* acquire irqs and clocks for all physical channels */
|
||||
for (i = 0; i < pdata->num_phy_channels; i++) {
|
||||
struct s3c24xx_dma_phy *phy = &s3cdma->phy_chans[i];
|
||||
char clk_name[6];
|
||||
@ -1266,7 +1261,7 @@ static int s3c24xx_dma_probe(struct platform_device *pdev)
|
||||
sprintf(clk_name, "dma.%d", i);
|
||||
phy->clk = devm_clk_get(&pdev->dev, clk_name);
|
||||
if (IS_ERR(phy->clk) && sdata->has_clocks) {
|
||||
dev_err(&pdev->dev, "unable to aquire clock for channel %d, error %lu",
|
||||
dev_err(&pdev->dev, "unable to acquire clock for channel %d, error %lu\n",
|
||||
i, PTR_ERR(phy->clk));
|
||||
continue;
|
||||
}
|
||||
@ -1290,8 +1285,6 @@ static int s3c24xx_dma_probe(struct platform_device *pdev)
|
||||
dma_cap_set(DMA_MEMCPY, s3cdma->memcpy.cap_mask);
|
||||
dma_cap_set(DMA_PRIVATE, s3cdma->memcpy.cap_mask);
|
||||
s3cdma->memcpy.dev = &pdev->dev;
|
||||
s3cdma->memcpy.device_alloc_chan_resources =
|
||||
s3c24xx_dma_alloc_chan_resources;
|
||||
s3cdma->memcpy.device_free_chan_resources =
|
||||
s3c24xx_dma_free_chan_resources;
|
||||
s3cdma->memcpy.device_prep_dma_memcpy = s3c24xx_dma_prep_memcpy;
|
||||
@ -1305,8 +1298,6 @@ static int s3c24xx_dma_probe(struct platform_device *pdev)
|
||||
dma_cap_set(DMA_CYCLIC, s3cdma->slave.cap_mask);
|
||||
dma_cap_set(DMA_PRIVATE, s3cdma->slave.cap_mask);
|
||||
s3cdma->slave.dev = &pdev->dev;
|
||||
s3cdma->slave.device_alloc_chan_resources =
|
||||
s3c24xx_dma_alloc_chan_resources;
|
||||
s3cdma->slave.device_free_chan_resources =
|
||||
s3c24xx_dma_free_chan_resources;
|
||||
s3cdma->slave.device_tx_status = s3c24xx_dma_tx_status;
|
||||
|
@ -389,11 +389,6 @@ static void sa11x0_dma_tasklet(unsigned long arg)
|
||||
}
|
||||
|
||||
|
||||
static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
|
||||
@ -835,7 +830,6 @@ static int sa11x0_dma_init_dmadev(struct dma_device *dmadev,
|
||||
|
||||
INIT_LIST_HEAD(&dmadev->channels);
|
||||
dmadev->dev = dev;
|
||||
dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources;
|
||||
dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources;
|
||||
dmadev->device_config = sa11x0_dma_device_config;
|
||||
dmadev->device_pause = sa11x0_dma_device_pause;
|
||||
@ -948,6 +942,12 @@ static int sa11x0_dma_probe(struct platform_device *pdev)
|
||||
dma_cap_set(DMA_CYCLIC, d->slave.cap_mask);
|
||||
d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg;
|
||||
d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic;
|
||||
d->slave.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
||||
d->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
d->slave.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES);
|
||||
d->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES);
|
||||
ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev);
|
||||
if (ret) {
|
||||
dev_warn(d->slave.dev, "failed to register slave async device: %d\n",
|
||||
|
@ -51,12 +51,6 @@ config RCAR_HPB_DMAE
|
||||
help
|
||||
Enable support for the Renesas R-Car series DMA controllers.
|
||||
|
||||
config RCAR_AUDMAC_PP
|
||||
tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support"
|
||||
depends on SH_DMAE_BASE
|
||||
help
|
||||
Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers.
|
||||
|
||||
config RCAR_DMAC
|
||||
tristate "Renesas R-Car Gen2 DMA Controller"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
@ -64,3 +58,12 @@ config RCAR_DMAC
|
||||
help
|
||||
This driver supports the general purpose DMA controller found in the
|
||||
Renesas R-Car second generation SoCs.
|
||||
|
||||
config RENESAS_USB_DMAC
|
||||
tristate "Renesas USB-DMA Controller"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
select RENESAS_DMA
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
help
|
||||
This driver supports the USB-DMA controller found in the Renesas
|
||||
SoCs.
|
||||
|
@ -15,5 +15,5 @@ obj-$(CONFIG_SH_DMAE) += shdma.o
|
||||
|
||||
obj-$(CONFIG_SUDMAC) += sudmac.o
|
||||
obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o
|
||||
obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o
|
||||
obj-$(CONFIG_RCAR_DMAC) += rcar-dmac.o
|
||||
obj-$(CONFIG_RENESAS_USB_DMAC) += usb-dmac.o
|
||||
|
@ -1,376 +0,0 @@
|
||||
/*
|
||||
* This is for Renesas R-Car Audio-DMAC-peri-peri.
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2014 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* based on the drivers/dma/sh/shdma.c
|
||||
*
|
||||
* Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
* Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
|
||||
* Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/platform_data/dma-rcar-audmapp.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/shdma-base.h>
|
||||
|
||||
/*
|
||||
* DMA register
|
||||
*/
|
||||
#define PDMASAR 0x00
|
||||
#define PDMADAR 0x04
|
||||
#define PDMACHCR 0x0c
|
||||
|
||||
/* PDMACHCR */
|
||||
#define PDMACHCR_DE (1 << 0)
|
||||
|
||||
#define AUDMAPP_MAX_CHANNELS 29
|
||||
|
||||
/* Default MEMCPY transfer size = 2^2 = 4 bytes */
|
||||
#define LOG2_DEFAULT_XFER_SIZE 2
|
||||
#define AUDMAPP_SLAVE_NUMBER 256
|
||||
#define AUDMAPP_LEN_MAX (16 * 1024 * 1024)
|
||||
|
||||
struct audmapp_chan {
|
||||
struct shdma_chan shdma_chan;
|
||||
void __iomem *base;
|
||||
dma_addr_t slave_addr;
|
||||
u32 chcr;
|
||||
};
|
||||
|
||||
struct audmapp_device {
|
||||
struct shdma_dev shdma_dev;
|
||||
struct audmapp_pdata *pdata;
|
||||
struct device *dev;
|
||||
void __iomem *chan_reg;
|
||||
};
|
||||
|
||||
struct audmapp_desc {
|
||||
struct shdma_desc shdma_desc;
|
||||
dma_addr_t src;
|
||||
dma_addr_t dst;
|
||||
};
|
||||
|
||||
#define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan)
|
||||
|
||||
#define to_chan(chan) container_of(chan, struct audmapp_chan, shdma_chan)
|
||||
#define to_desc(sdesc) container_of(sdesc, struct audmapp_desc, shdma_desc)
|
||||
#define to_dev(chan) container_of(chan->shdma_chan.dma_chan.device, \
|
||||
struct audmapp_device, shdma_dev.dma_dev)
|
||||
|
||||
static void audmapp_write(struct audmapp_chan *auchan, u32 data, u32 reg)
|
||||
{
|
||||
struct audmapp_device *audev = to_dev(auchan);
|
||||
struct device *dev = audev->dev;
|
||||
|
||||
dev_dbg(dev, "w %p : %08x\n", auchan->base + reg, data);
|
||||
|
||||
iowrite32(data, auchan->base + reg);
|
||||
}
|
||||
|
||||
static u32 audmapp_read(struct audmapp_chan *auchan, u32 reg)
|
||||
{
|
||||
return ioread32(auchan->base + reg);
|
||||
}
|
||||
|
||||
static void audmapp_halt(struct shdma_chan *schan)
|
||||
{
|
||||
struct audmapp_chan *auchan = to_chan(schan);
|
||||
int i;
|
||||
|
||||
audmapp_write(auchan, 0, PDMACHCR);
|
||||
|
||||
for (i = 0; i < 1024; i++) {
|
||||
if (0 == audmapp_read(auchan, PDMACHCR))
|
||||
return;
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void audmapp_start_xfer(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc)
|
||||
{
|
||||
struct audmapp_chan *auchan = to_chan(schan);
|
||||
struct audmapp_device *audev = to_dev(auchan);
|
||||
struct audmapp_desc *desc = to_desc(sdesc);
|
||||
struct device *dev = audev->dev;
|
||||
u32 chcr = auchan->chcr | PDMACHCR_DE;
|
||||
|
||||
dev_dbg(dev, "src/dst/chcr = %pad/%pad/%08x\n",
|
||||
&desc->src, &desc->dst, chcr);
|
||||
|
||||
audmapp_write(auchan, desc->src, PDMASAR);
|
||||
audmapp_write(auchan, desc->dst, PDMADAR);
|
||||
audmapp_write(auchan, chcr, PDMACHCR);
|
||||
}
|
||||
|
||||
static int audmapp_get_config(struct audmapp_chan *auchan, int slave_id,
|
||||
u32 *chcr, dma_addr_t *dst)
|
||||
{
|
||||
struct audmapp_device *audev = to_dev(auchan);
|
||||
struct audmapp_pdata *pdata = audev->pdata;
|
||||
struct audmapp_slave_config *cfg;
|
||||
int i;
|
||||
|
||||
*chcr = 0;
|
||||
*dst = 0;
|
||||
|
||||
if (!pdata) { /* DT */
|
||||
*chcr = ((u32)slave_id) << 16;
|
||||
auchan->shdma_chan.slave_id = (slave_id) >> 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* non-DT */
|
||||
|
||||
if (slave_id >= AUDMAPP_SLAVE_NUMBER)
|
||||
return -ENXIO;
|
||||
|
||||
for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
|
||||
if (cfg->slave_id == slave_id) {
|
||||
*chcr = cfg->chcr;
|
||||
*dst = cfg->dst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int audmapp_set_slave(struct shdma_chan *schan, int slave_id,
|
||||
dma_addr_t slave_addr, bool try)
|
||||
{
|
||||
struct audmapp_chan *auchan = to_chan(schan);
|
||||
u32 chcr;
|
||||
dma_addr_t dst;
|
||||
int ret;
|
||||
|
||||
ret = audmapp_get_config(auchan, slave_id, &chcr, &dst);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (try)
|
||||
return 0;
|
||||
|
||||
auchan->chcr = chcr;
|
||||
auchan->slave_addr = slave_addr ? : dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audmapp_desc_setup(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc,
|
||||
dma_addr_t src, dma_addr_t dst, size_t *len)
|
||||
{
|
||||
struct audmapp_desc *desc = to_desc(sdesc);
|
||||
|
||||
if (*len > (size_t)AUDMAPP_LEN_MAX)
|
||||
*len = (size_t)AUDMAPP_LEN_MAX;
|
||||
|
||||
desc->src = src;
|
||||
desc->dst = dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audmapp_setup_xfer(struct shdma_chan *schan,
|
||||
int slave_id)
|
||||
{
|
||||
}
|
||||
|
||||
static dma_addr_t audmapp_slave_addr(struct shdma_chan *schan)
|
||||
{
|
||||
struct audmapp_chan *auchan = to_chan(schan);
|
||||
|
||||
return auchan->slave_addr;
|
||||
}
|
||||
|
||||
static bool audmapp_channel_busy(struct shdma_chan *schan)
|
||||
{
|
||||
struct audmapp_chan *auchan = to_chan(schan);
|
||||
u32 chcr = audmapp_read(auchan, PDMACHCR);
|
||||
|
||||
return chcr & ~PDMACHCR_DE;
|
||||
}
|
||||
|
||||
static bool audmapp_desc_completed(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct shdma_desc *audmapp_embedded_desc(void *buf, int i)
|
||||
{
|
||||
return &((struct audmapp_desc *)buf)[i].shdma_desc;
|
||||
}
|
||||
|
||||
static const struct shdma_ops audmapp_shdma_ops = {
|
||||
.halt_channel = audmapp_halt,
|
||||
.desc_setup = audmapp_desc_setup,
|
||||
.set_slave = audmapp_set_slave,
|
||||
.start_xfer = audmapp_start_xfer,
|
||||
.embedded_desc = audmapp_embedded_desc,
|
||||
.setup_xfer = audmapp_setup_xfer,
|
||||
.slave_addr = audmapp_slave_addr,
|
||||
.channel_busy = audmapp_channel_busy,
|
||||
.desc_completed = audmapp_desc_completed,
|
||||
};
|
||||
|
||||
static int audmapp_chan_probe(struct platform_device *pdev,
|
||||
struct audmapp_device *audev, int id)
|
||||
{
|
||||
struct shdma_dev *sdev = &audev->shdma_dev;
|
||||
struct audmapp_chan *auchan;
|
||||
struct shdma_chan *schan;
|
||||
struct device *dev = audev->dev;
|
||||
|
||||
auchan = devm_kzalloc(dev, sizeof(*auchan), GFP_KERNEL);
|
||||
if (!auchan)
|
||||
return -ENOMEM;
|
||||
|
||||
schan = &auchan->shdma_chan;
|
||||
schan->max_xfer_len = AUDMAPP_LEN_MAX;
|
||||
|
||||
shdma_chan_probe(sdev, schan, id);
|
||||
|
||||
auchan->base = audev->chan_reg + 0x20 + (0x10 * id);
|
||||
dev_dbg(dev, "%02d : %p / %p", id, auchan->base, audev->chan_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audmapp_chan_remove(struct audmapp_device *audev)
|
||||
{
|
||||
struct shdma_chan *schan;
|
||||
int i;
|
||||
|
||||
shdma_for_each_chan(schan, &audev->shdma_dev, i) {
|
||||
BUG_ON(!schan);
|
||||
shdma_chan_remove(schan);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dma_chan *audmapp_of_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
dma_cap_mask_t mask;
|
||||
struct dma_chan *chan;
|
||||
u32 chcr = dma_spec->args[0];
|
||||
|
||||
if (dma_spec->args_count != 1)
|
||||
return NULL;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
chan = dma_request_channel(mask, shdma_chan_filter, NULL);
|
||||
if (chan)
|
||||
to_shdma_chan(chan)->hw_req = chcr;
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static int audmapp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct audmapp_pdata *pdata = pdev->dev.platform_data;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct audmapp_device *audev;
|
||||
struct shdma_dev *sdev;
|
||||
struct dma_device *dma_dev;
|
||||
struct resource *res;
|
||||
int err, i;
|
||||
|
||||
if (np)
|
||||
of_dma_controller_register(np, audmapp_of_xlate, pdev);
|
||||
else if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
audev = devm_kzalloc(&pdev->dev, sizeof(*audev), GFP_KERNEL);
|
||||
if (!audev)
|
||||
return -ENOMEM;
|
||||
|
||||
audev->dev = &pdev->dev;
|
||||
audev->pdata = pdata;
|
||||
audev->chan_reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(audev->chan_reg))
|
||||
return PTR_ERR(audev->chan_reg);
|
||||
|
||||
sdev = &audev->shdma_dev;
|
||||
sdev->ops = &audmapp_shdma_ops;
|
||||
sdev->desc_size = sizeof(struct audmapp_desc);
|
||||
|
||||
dma_dev = &sdev->dma_dev;
|
||||
dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE;
|
||||
dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
|
||||
|
||||
err = shdma_init(&pdev->dev, sdev, AUDMAPP_MAX_CHANNELS);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
platform_set_drvdata(pdev, audev);
|
||||
|
||||
/* Create DMA Channel */
|
||||
for (i = 0; i < AUDMAPP_MAX_CHANNELS; i++) {
|
||||
err = audmapp_chan_probe(pdev, audev, i);
|
||||
if (err)
|
||||
goto chan_probe_err;
|
||||
}
|
||||
|
||||
err = dma_async_device_register(dma_dev);
|
||||
if (err < 0)
|
||||
goto chan_probe_err;
|
||||
|
||||
return err;
|
||||
|
||||
chan_probe_err:
|
||||
audmapp_chan_remove(audev);
|
||||
shdma_cleanup(sdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int audmapp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct audmapp_device *audev = platform_get_drvdata(pdev);
|
||||
struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;
|
||||
|
||||
dma_async_device_unregister(dma_dev);
|
||||
|
||||
audmapp_chan_remove(audev);
|
||||
shdma_cleanup(&audev->shdma_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id audmapp_of_match[] = {
|
||||
{ .compatible = "renesas,rcar-audmapp", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver audmapp_driver = {
|
||||
.probe = audmapp_probe,
|
||||
.remove = audmapp_remove,
|
||||
.driver = {
|
||||
.name = "rcar-audmapp-engine",
|
||||
.of_match_table = audmapp_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(audmapp_driver);
|
||||
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
||||
MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -171,8 +171,7 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int shdma_setup_slave(struct shdma_chan *schan, int slave_id,
|
||||
dma_addr_t slave_addr)
|
||||
static int shdma_setup_slave(struct shdma_chan *schan, dma_addr_t slave_addr)
|
||||
{
|
||||
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
|
||||
const struct shdma_ops *ops = sdev->ops;
|
||||
@ -183,25 +182,23 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id,
|
||||
ret = ops->set_slave(schan, match, slave_addr, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
slave_id = schan->slave_id;
|
||||
} else {
|
||||
match = slave_id;
|
||||
match = schan->real_slave_id;
|
||||
}
|
||||
|
||||
if (slave_id < 0 || slave_id >= slave_num)
|
||||
if (schan->real_slave_id < 0 || schan->real_slave_id >= slave_num)
|
||||
return -EINVAL;
|
||||
|
||||
if (test_and_set_bit(slave_id, shdma_slave_used))
|
||||
if (test_and_set_bit(schan->real_slave_id, shdma_slave_used))
|
||||
return -EBUSY;
|
||||
|
||||
ret = ops->set_slave(schan, match, slave_addr, false);
|
||||
if (ret < 0) {
|
||||
clear_bit(slave_id, shdma_slave_used);
|
||||
clear_bit(schan->real_slave_id, shdma_slave_used);
|
||||
return ret;
|
||||
}
|
||||
|
||||
schan->slave_id = slave_id;
|
||||
schan->slave_id = schan->real_slave_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -221,10 +218,12 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan)
|
||||
*/
|
||||
if (slave) {
|
||||
/* Legacy mode: .private is set in filter */
|
||||
ret = shdma_setup_slave(schan, slave->slave_id, 0);
|
||||
schan->real_slave_id = slave->slave_id;
|
||||
ret = shdma_setup_slave(schan, 0);
|
||||
if (ret < 0)
|
||||
goto esetslave;
|
||||
} else {
|
||||
/* Normal mode: real_slave_id was set by filter */
|
||||
schan->slave_id = -EINVAL;
|
||||
}
|
||||
|
||||
@ -258,11 +257,14 @@ esetslave:
|
||||
|
||||
/*
|
||||
* This is the standard shdma filter function to be used as a replacement to the
|
||||
* "old" method, using the .private pointer. If for some reason you allocate a
|
||||
* channel without slave data, use something like ERR_PTR(-EINVAL) as a filter
|
||||
* "old" method, using the .private pointer.
|
||||
* You always have to pass a valid slave id as the argument, old drivers that
|
||||
* pass ERR_PTR(-EINVAL) as a filter parameter and set it up in dma_slave_config
|
||||
* need to be updated so we can remove the slave_id field from dma_slave_config.
|
||||
* parameter. If this filter is used, the slave driver, after calling
|
||||
* dma_request_channel(), will also have to call dmaengine_slave_config() with
|
||||
* .slave_id, .direction, and either .src_addr or .dst_addr set.
|
||||
* .direction, and either .src_addr or .dst_addr set.
|
||||
*
|
||||
* NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE
|
||||
* capability! If this becomes a requirement, hardware glue drivers, using this
|
||||
* services would have to provide their own filters, which first would check
|
||||
@ -276,7 +278,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg)
|
||||
{
|
||||
struct shdma_chan *schan;
|
||||
struct shdma_dev *sdev;
|
||||
int match = (long)arg;
|
||||
int slave_id = (long)arg;
|
||||
int ret;
|
||||
|
||||
/* Only support channels handled by this driver. */
|
||||
@ -284,19 +286,39 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg)
|
||||
shdma_alloc_chan_resources)
|
||||
return false;
|
||||
|
||||
if (match < 0)
|
||||
/* No slave requested - arbitrary channel */
|
||||
return true;
|
||||
|
||||
schan = to_shdma_chan(chan);
|
||||
if (!schan->dev->of_node && match >= slave_num)
|
||||
sdev = to_shdma_dev(chan->device);
|
||||
|
||||
/*
|
||||
* For DT, the schan->slave_id field is generated by the
|
||||
* set_slave function from the slave ID that is passed in
|
||||
* from xlate. For the non-DT case, the slave ID is
|
||||
* directly passed into the filter function by the driver
|
||||
*/
|
||||
if (schan->dev->of_node) {
|
||||
ret = sdev->ops->set_slave(schan, slave_id, 0, true);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
schan->real_slave_id = schan->slave_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (slave_id < 0) {
|
||||
/* No slave requested - arbitrary channel */
|
||||
dev_warn(sdev->dma_dev.dev, "invalid slave ID passed to dma_request_slave\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (slave_id >= slave_num)
|
||||
return false;
|
||||
|
||||
sdev = to_shdma_dev(schan->dma_chan.device);
|
||||
ret = sdev->ops->set_slave(schan, match, 0, true);
|
||||
ret = sdev->ops->set_slave(schan, slave_id, 0, true);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
schan->real_slave_id = slave_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(shdma_chan_filter);
|
||||
@ -452,6 +474,8 @@ static void shdma_free_chan_resources(struct dma_chan *chan)
|
||||
chan->private = NULL;
|
||||
}
|
||||
|
||||
schan->real_slave_id = 0;
|
||||
|
||||
spin_lock_irq(&schan->chan_lock);
|
||||
|
||||
list_splice_init(&schan->ld_free, &list);
|
||||
@ -764,11 +788,20 @@ static int shdma_config(struct dma_chan *chan,
|
||||
*/
|
||||
if (!config)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* overriding the slave_id through dma_slave_config is deprecated,
|
||||
* but possibly some out-of-tree drivers still do it.
|
||||
*/
|
||||
if (WARN_ON_ONCE(config->slave_id &&
|
||||
config->slave_id != schan->real_slave_id))
|
||||
schan->real_slave_id = config->slave_id;
|
||||
|
||||
/*
|
||||
* We could lock this, but you shouldn't be configuring the
|
||||
* channel, while using it...
|
||||
*/
|
||||
return shdma_setup_slave(schan, config->slave_id,
|
||||
return shdma_setup_slave(schan,
|
||||
config->direction == DMA_DEV_TO_MEM ?
|
||||
config->src_addr : config->dst_addr);
|
||||
}
|
||||
|
@ -443,7 +443,7 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM)
|
||||
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
|
||||
static irqreturn_t sh_dmae_err(int irq, void *data)
|
||||
{
|
||||
struct sh_dmae_device *shdev = data;
|
||||
@ -689,7 +689,7 @@ static int sh_dmae_probe(struct platform_device *pdev)
|
||||
const struct sh_dmae_pdata *pdata;
|
||||
unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {};
|
||||
int chan_irq[SH_DMAE_MAX_CHANNELS];
|
||||
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM)
|
||||
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
|
||||
unsigned long irqflags = 0;
|
||||
int errirq;
|
||||
#endif
|
||||
|
910
drivers/dma/sh/usb-dmac.c
Normal file
910
drivers/dma/sh/usb-dmac.c
Normal file
@ -0,0 +1,910 @@
|
||||
/*
|
||||
* Renesas USB DMA Controller Driver
|
||||
*
|
||||
* Copyright (C) 2015 Renesas Electronics Corporation
|
||||
*
|
||||
* based on rcar-dmac.c
|
||||
* Copyright (C) 2014 Renesas Electronics Inc.
|
||||
* Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "../dmaengine.h"
|
||||
#include "../virt-dma.h"
|
||||
|
||||
/*
|
||||
* struct usb_dmac_sg - Descriptor for a hardware transfer
|
||||
* @mem_addr: memory address
|
||||
* @size: transfer size in bytes
|
||||
*/
|
||||
struct usb_dmac_sg {
|
||||
dma_addr_t mem_addr;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct usb_dmac_desc - USB DMA Transfer Descriptor
|
||||
* @vd: base virtual channel DMA transaction descriptor
|
||||
* @direction: direction of the DMA transfer
|
||||
* @sg_allocated_len: length of allocated sg
|
||||
* @sg_len: length of sg
|
||||
* @sg_index: index of sg
|
||||
* @residue: residue after the DMAC completed a transfer
|
||||
* @node: node for desc_got and desc_freed
|
||||
* @done_cookie: cookie after the DMAC completed a transfer
|
||||
* @sg: information for the transfer
|
||||
*/
|
||||
struct usb_dmac_desc {
|
||||
struct virt_dma_desc vd;
|
||||
enum dma_transfer_direction direction;
|
||||
unsigned int sg_allocated_len;
|
||||
unsigned int sg_len;
|
||||
unsigned int sg_index;
|
||||
u32 residue;
|
||||
struct list_head node;
|
||||
dma_cookie_t done_cookie;
|
||||
struct usb_dmac_sg sg[0];
|
||||
};
|
||||
|
||||
#define to_usb_dmac_desc(vd) container_of(vd, struct usb_dmac_desc, vd)
|
||||
|
||||
/*
|
||||
* struct usb_dmac_chan - USB DMA Controller Channel
|
||||
* @vc: base virtual DMA channel object
|
||||
* @iomem: channel I/O memory base
|
||||
* @index: index of this channel in the controller
|
||||
* @irq: irq number of this channel
|
||||
* @desc: the current descriptor
|
||||
* @descs_allocated: number of descriptors allocated
|
||||
* @desc_got: got descriptors
|
||||
* @desc_freed: freed descriptors after the DMAC completed a transfer
|
||||
*/
|
||||
struct usb_dmac_chan {
|
||||
struct virt_dma_chan vc;
|
||||
void __iomem *iomem;
|
||||
unsigned int index;
|
||||
int irq;
|
||||
struct usb_dmac_desc *desc;
|
||||
int descs_allocated;
|
||||
struct list_head desc_got;
|
||||
struct list_head desc_freed;
|
||||
};
|
||||
|
||||
#define to_usb_dmac_chan(c) container_of(c, struct usb_dmac_chan, vc.chan)
|
||||
|
||||
/*
|
||||
* struct usb_dmac - USB DMA Controller
|
||||
* @engine: base DMA engine object
|
||||
* @dev: the hardware device
|
||||
* @iomem: remapped I/O memory base
|
||||
* @n_channels: number of available channels
|
||||
* @channels: array of DMAC channels
|
||||
*/
|
||||
struct usb_dmac {
|
||||
struct dma_device engine;
|
||||
struct device *dev;
|
||||
void __iomem *iomem;
|
||||
|
||||
unsigned int n_channels;
|
||||
struct usb_dmac_chan *channels;
|
||||
};
|
||||
|
||||
#define to_usb_dmac(d) container_of(d, struct usb_dmac, engine)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Registers
|
||||
*/
|
||||
|
||||
#define USB_DMAC_CHAN_OFFSET(i) (0x20 + 0x20 * (i))
|
||||
|
||||
#define USB_DMASWR 0x0008
|
||||
#define USB_DMASWR_SWR (1 << 0)
|
||||
#define USB_DMAOR 0x0060
|
||||
#define USB_DMAOR_AE (1 << 2)
|
||||
#define USB_DMAOR_DME (1 << 0)
|
||||
|
||||
#define USB_DMASAR 0x0000
|
||||
#define USB_DMADAR 0x0004
|
||||
#define USB_DMATCR 0x0008
|
||||
#define USB_DMATCR_MASK 0x00ffffff
|
||||
#define USB_DMACHCR 0x0014
|
||||
#define USB_DMACHCR_FTE (1 << 24)
|
||||
#define USB_DMACHCR_NULLE (1 << 16)
|
||||
#define USB_DMACHCR_NULL (1 << 12)
|
||||
#define USB_DMACHCR_TS_8B ((0 << 7) | (0 << 6))
|
||||
#define USB_DMACHCR_TS_16B ((0 << 7) | (1 << 6))
|
||||
#define USB_DMACHCR_TS_32B ((1 << 7) | (0 << 6))
|
||||
#define USB_DMACHCR_IE (1 << 5)
|
||||
#define USB_DMACHCR_SP (1 << 2)
|
||||
#define USB_DMACHCR_TE (1 << 1)
|
||||
#define USB_DMACHCR_DE (1 << 0)
|
||||
#define USB_DMATEND 0x0018
|
||||
|
||||
/* Hardcode the xfer_shift to 5 (32bytes) */
|
||||
#define USB_DMAC_XFER_SHIFT 5
|
||||
#define USB_DMAC_XFER_SIZE (1 << USB_DMAC_XFER_SHIFT)
|
||||
#define USB_DMAC_CHCR_TS USB_DMACHCR_TS_32B
|
||||
#define USB_DMAC_SLAVE_BUSWIDTH DMA_SLAVE_BUSWIDTH_32_BYTES
|
||||
|
||||
/* for descriptors */
|
||||
#define USB_DMAC_INITIAL_NR_DESC 16
|
||||
#define USB_DMAC_INITIAL_NR_SG 8
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Device access
|
||||
*/
|
||||
|
||||
static void usb_dmac_write(struct usb_dmac *dmac, u32 reg, u32 data)
|
||||
{
|
||||
writel(data, dmac->iomem + reg);
|
||||
}
|
||||
|
||||
static u32 usb_dmac_read(struct usb_dmac *dmac, u32 reg)
|
||||
{
|
||||
return readl(dmac->iomem + reg);
|
||||
}
|
||||
|
||||
static u32 usb_dmac_chan_read(struct usb_dmac_chan *chan, u32 reg)
|
||||
{
|
||||
return readl(chan->iomem + reg);
|
||||
}
|
||||
|
||||
static void usb_dmac_chan_write(struct usb_dmac_chan *chan, u32 reg, u32 data)
|
||||
{
|
||||
writel(data, chan->iomem + reg);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization and configuration
|
||||
*/
|
||||
|
||||
static bool usb_dmac_chan_is_busy(struct usb_dmac_chan *chan)
|
||||
{
|
||||
u32 chcr = usb_dmac_chan_read(chan, USB_DMACHCR);
|
||||
|
||||
return (chcr & (USB_DMACHCR_DE | USB_DMACHCR_TE)) == USB_DMACHCR_DE;
|
||||
}
|
||||
|
||||
static u32 usb_dmac_calc_tend(u32 size)
|
||||
{
|
||||
/*
|
||||
* Please refer to the Figure "Example of Final Transaction Valid
|
||||
* Data Transfer Enable (EDTEN) Setting" in the data sheet.
|
||||
*/
|
||||
return 0xffffffff << (32 - (size % USB_DMAC_XFER_SIZE ? :
|
||||
USB_DMAC_XFER_SIZE));
|
||||
}
|
||||
|
||||
/* This function is already held by vc.lock */
|
||||
static void usb_dmac_chan_start_sg(struct usb_dmac_chan *chan,
|
||||
unsigned int index)
|
||||
{
|
||||
struct usb_dmac_desc *desc = chan->desc;
|
||||
struct usb_dmac_sg *sg = desc->sg + index;
|
||||
dma_addr_t src_addr = 0, dst_addr = 0;
|
||||
|
||||
WARN_ON_ONCE(usb_dmac_chan_is_busy(chan));
|
||||
|
||||
if (desc->direction == DMA_DEV_TO_MEM)
|
||||
dst_addr = sg->mem_addr;
|
||||
else
|
||||
src_addr = sg->mem_addr;
|
||||
|
||||
dev_dbg(chan->vc.chan.device->dev,
|
||||
"chan%u: queue sg %p: %u@%pad -> %pad\n",
|
||||
chan->index, sg, sg->size, &src_addr, &dst_addr);
|
||||
|
||||
usb_dmac_chan_write(chan, USB_DMASAR, src_addr & 0xffffffff);
|
||||
usb_dmac_chan_write(chan, USB_DMADAR, dst_addr & 0xffffffff);
|
||||
usb_dmac_chan_write(chan, USB_DMATCR,
|
||||
DIV_ROUND_UP(sg->size, USB_DMAC_XFER_SIZE));
|
||||
usb_dmac_chan_write(chan, USB_DMATEND, usb_dmac_calc_tend(sg->size));
|
||||
|
||||
usb_dmac_chan_write(chan, USB_DMACHCR, USB_DMAC_CHCR_TS |
|
||||
USB_DMACHCR_NULLE | USB_DMACHCR_IE | USB_DMACHCR_DE);
|
||||
}
|
||||
|
||||
/* This function is already held by vc.lock */
|
||||
static void usb_dmac_chan_start_desc(struct usb_dmac_chan *chan)
|
||||
{
|
||||
struct virt_dma_desc *vd;
|
||||
|
||||
vd = vchan_next_desc(&chan->vc);
|
||||
if (!vd) {
|
||||
chan->desc = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove this request from vc->desc_issued. Otherwise, this driver
|
||||
* will get the previous value from vchan_next_desc() after a transfer
|
||||
* was completed.
|
||||
*/
|
||||
list_del(&vd->node);
|
||||
|
||||
chan->desc = to_usb_dmac_desc(vd);
|
||||
chan->desc->sg_index = 0;
|
||||
usb_dmac_chan_start_sg(chan, 0);
|
||||
}
|
||||
|
||||
static int usb_dmac_init(struct usb_dmac *dmac)
|
||||
{
|
||||
u16 dmaor;
|
||||
|
||||
/* Clear all channels and enable the DMAC globally. */
|
||||
usb_dmac_write(dmac, USB_DMAOR, USB_DMAOR_DME);
|
||||
|
||||
dmaor = usb_dmac_read(dmac, USB_DMAOR);
|
||||
if ((dmaor & (USB_DMAOR_AE | USB_DMAOR_DME)) != USB_DMAOR_DME) {
|
||||
dev_warn(dmac->dev, "DMAOR initialization failed.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Descriptors allocation and free
|
||||
*/
|
||||
static int usb_dmac_desc_alloc(struct usb_dmac_chan *chan, unsigned int sg_len,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct usb_dmac_desc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
desc = kzalloc(sizeof(*desc) + sg_len * sizeof(desc->sg[0]), gfp);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->sg_allocated_len = sg_len;
|
||||
INIT_LIST_HEAD(&desc->node);
|
||||
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
list_add_tail(&desc->node, &chan->desc_freed);
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_dmac_desc_free(struct usb_dmac_chan *chan)
|
||||
{
|
||||
struct usb_dmac_desc *desc, *_desc;
|
||||
LIST_HEAD(list);
|
||||
|
||||
list_splice_init(&chan->desc_freed, &list);
|
||||
list_splice_init(&chan->desc_got, &list);
|
||||
|
||||
list_for_each_entry_safe(desc, _desc, &list, node) {
|
||||
list_del(&desc->node);
|
||||
kfree(desc);
|
||||
}
|
||||
chan->descs_allocated = 0;
|
||||
}
|
||||
|
||||
static struct usb_dmac_desc *usb_dmac_desc_get(struct usb_dmac_chan *chan,
|
||||
unsigned int sg_len, gfp_t gfp)
|
||||
{
|
||||
struct usb_dmac_desc *desc = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
/* Get a freed descritpor */
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
list_for_each_entry(desc, &chan->desc_freed, node) {
|
||||
if (sg_len <= desc->sg_allocated_len) {
|
||||
list_move_tail(&desc->node, &chan->desc_got);
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
|
||||
/* Allocate a new descriptor */
|
||||
if (!usb_dmac_desc_alloc(chan, sg_len, gfp)) {
|
||||
/* If allocated the desc, it was added to tail of the list */
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
desc = list_last_entry(&chan->desc_freed, struct usb_dmac_desc,
|
||||
node);
|
||||
list_move_tail(&desc->node, &chan->desc_got);
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
return desc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void usb_dmac_desc_put(struct usb_dmac_chan *chan,
|
||||
struct usb_dmac_desc *desc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chan->vc.lock, flags);
|
||||
list_move_tail(&desc->node, &chan->desc_freed);
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Stop and reset
|
||||
*/
|
||||
|
||||
static void usb_dmac_soft_reset(struct usb_dmac_chan *uchan)
|
||||
{
|
||||
struct dma_chan *chan = &uchan->vc.chan;
|
||||
struct usb_dmac *dmac = to_usb_dmac(chan->device);
|
||||
int i;
|
||||
|
||||
/* Don't issue soft reset if any one of channels is busy */
|
||||
for (i = 0; i < dmac->n_channels; ++i) {
|
||||
if (usb_dmac_chan_is_busy(uchan))
|
||||
return;
|
||||
}
|
||||
|
||||
usb_dmac_write(dmac, USB_DMAOR, 0);
|
||||
usb_dmac_write(dmac, USB_DMASWR, USB_DMASWR_SWR);
|
||||
udelay(100);
|
||||
usb_dmac_write(dmac, USB_DMASWR, 0);
|
||||
usb_dmac_write(dmac, USB_DMAOR, 1);
|
||||
}
|
||||
|
||||
static void usb_dmac_chan_halt(struct usb_dmac_chan *chan)
|
||||
{
|
||||
u32 chcr = usb_dmac_chan_read(chan, USB_DMACHCR);
|
||||
|
||||
chcr &= ~(USB_DMACHCR_IE | USB_DMACHCR_TE | USB_DMACHCR_DE);
|
||||
usb_dmac_chan_write(chan, USB_DMACHCR, chcr);
|
||||
|
||||
usb_dmac_soft_reset(chan);
|
||||
}
|
||||
|
||||
static void usb_dmac_stop(struct usb_dmac *dmac)
|
||||
{
|
||||
usb_dmac_write(dmac, USB_DMAOR, 0);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* DMA engine operations
|
||||
*/
|
||||
|
||||
static int usb_dmac_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
int ret;
|
||||
|
||||
while (uchan->descs_allocated < USB_DMAC_INITIAL_NR_DESC) {
|
||||
ret = usb_dmac_desc_alloc(uchan, USB_DMAC_INITIAL_NR_SG,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
usb_dmac_desc_free(uchan);
|
||||
return ret;
|
||||
}
|
||||
uchan->descs_allocated++;
|
||||
}
|
||||
|
||||
return pm_runtime_get_sync(chan->device->dev);
|
||||
}
|
||||
|
||||
static void usb_dmac_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
/* Protect against ISR */
|
||||
spin_lock_irqsave(&uchan->vc.lock, flags);
|
||||
usb_dmac_chan_halt(uchan);
|
||||
spin_unlock_irqrestore(&uchan->vc.lock, flags);
|
||||
|
||||
usb_dmac_desc_free(uchan);
|
||||
vchan_free_chan_resources(&uchan->vc);
|
||||
|
||||
pm_runtime_put(chan->device->dev);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *
|
||||
usb_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned int sg_len, enum dma_transfer_direction dir,
|
||||
unsigned long dma_flags, void *context)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
struct usb_dmac_desc *desc;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (!sg_len) {
|
||||
dev_warn(chan->device->dev,
|
||||
"%s: bad parameter: len=%d\n", __func__, sg_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc = usb_dmac_desc_get(uchan, sg_len, GFP_NOWAIT);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
desc->direction = dir;
|
||||
desc->sg_len = sg_len;
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
desc->sg[i].mem_addr = sg_dma_address(sg);
|
||||
desc->sg[i].size = sg_dma_len(sg);
|
||||
}
|
||||
|
||||
return vchan_tx_prep(&uchan->vc, &desc->vd, dma_flags);
|
||||
}
|
||||
|
||||
static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
struct usb_dmac_desc *desc;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(head);
|
||||
LIST_HEAD(list);
|
||||
|
||||
spin_lock_irqsave(&uchan->vc.lock, flags);
|
||||
usb_dmac_chan_halt(uchan);
|
||||
vchan_get_all_descriptors(&uchan->vc, &head);
|
||||
if (uchan->desc)
|
||||
uchan->desc = NULL;
|
||||
list_splice_init(&uchan->desc_got, &list);
|
||||
list_for_each_entry(desc, &list, node)
|
||||
list_move_tail(&desc->node, &uchan->desc_freed);
|
||||
spin_unlock_irqrestore(&uchan->vc.lock, flags);
|
||||
vchan_dma_desc_free_list(&uchan->vc, &head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int usb_dmac_get_current_residue(struct usb_dmac_chan *chan,
|
||||
struct usb_dmac_desc *desc,
|
||||
int sg_index)
|
||||
{
|
||||
struct usb_dmac_sg *sg = desc->sg + sg_index;
|
||||
u32 mem_addr = sg->mem_addr & 0xffffffff;
|
||||
unsigned int residue = sg->size;
|
||||
|
||||
/*
|
||||
* We cannot use USB_DMATCR to calculate residue because USB_DMATCR
|
||||
* has unsuited value to calculate.
|
||||
*/
|
||||
if (desc->direction == DMA_DEV_TO_MEM)
|
||||
residue -= usb_dmac_chan_read(chan, USB_DMADAR) - mem_addr;
|
||||
else
|
||||
residue -= usb_dmac_chan_read(chan, USB_DMASAR) - mem_addr;
|
||||
|
||||
return residue;
|
||||
}
|
||||
|
||||
static u32 usb_dmac_chan_get_residue_if_complete(struct usb_dmac_chan *chan,
|
||||
dma_cookie_t cookie)
|
||||
{
|
||||
struct usb_dmac_desc *desc;
|
||||
u32 residue = 0;
|
||||
|
||||
list_for_each_entry_reverse(desc, &chan->desc_freed, node) {
|
||||
if (desc->done_cookie == cookie) {
|
||||
residue = desc->residue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return residue;
|
||||
}
|
||||
|
||||
static u32 usb_dmac_chan_get_residue(struct usb_dmac_chan *chan,
|
||||
dma_cookie_t cookie)
|
||||
{
|
||||
u32 residue = 0;
|
||||
struct virt_dma_desc *vd;
|
||||
struct usb_dmac_desc *desc = chan->desc;
|
||||
int i;
|
||||
|
||||
if (!desc) {
|
||||
vd = vchan_find_desc(&chan->vc, cookie);
|
||||
if (!vd)
|
||||
return 0;
|
||||
desc = to_usb_dmac_desc(vd);
|
||||
}
|
||||
|
||||
/* Compute the size of all usb_dmac_sg still to be transferred */
|
||||
for (i = desc->sg_index + 1; i < desc->sg_len; i++)
|
||||
residue += desc->sg[i].size;
|
||||
|
||||
/* Add the residue for the current sg */
|
||||
residue += usb_dmac_get_current_residue(chan, desc, desc->sg_index);
|
||||
|
||||
return residue;
|
||||
}
|
||||
|
||||
static enum dma_status usb_dmac_tx_status(struct dma_chan *chan,
|
||||
dma_cookie_t cookie,
|
||||
struct dma_tx_state *txstate)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
enum dma_status status;
|
||||
unsigned int residue = 0;
|
||||
unsigned long flags;
|
||||
|
||||
status = dma_cookie_status(chan, cookie, txstate);
|
||||
/* a client driver will get residue after DMA_COMPLETE */
|
||||
if (!txstate)
|
||||
return status;
|
||||
|
||||
spin_lock_irqsave(&uchan->vc.lock, flags);
|
||||
if (status == DMA_COMPLETE)
|
||||
residue = usb_dmac_chan_get_residue_if_complete(uchan, cookie);
|
||||
else
|
||||
residue = usb_dmac_chan_get_residue(uchan, cookie);
|
||||
spin_unlock_irqrestore(&uchan->vc.lock, flags);
|
||||
|
||||
dma_set_residue(txstate, residue);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void usb_dmac_issue_pending(struct dma_chan *chan)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&uchan->vc.lock, flags);
|
||||
if (vchan_issue_pending(&uchan->vc) && !uchan->desc)
|
||||
usb_dmac_chan_start_desc(uchan);
|
||||
spin_unlock_irqrestore(&uchan->vc.lock, flags);
|
||||
}
|
||||
|
||||
static void usb_dmac_virt_desc_free(struct virt_dma_desc *vd)
|
||||
{
|
||||
struct usb_dmac_desc *desc = to_usb_dmac_desc(vd);
|
||||
struct usb_dmac_chan *chan = to_usb_dmac_chan(vd->tx.chan);
|
||||
|
||||
usb_dmac_desc_put(chan, desc);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* IRQ handling
|
||||
*/
|
||||
|
||||
static void usb_dmac_isr_transfer_end(struct usb_dmac_chan *chan)
|
||||
{
|
||||
struct usb_dmac_desc *desc = chan->desc;
|
||||
|
||||
BUG_ON(!desc);
|
||||
|
||||
if (++desc->sg_index < desc->sg_len) {
|
||||
usb_dmac_chan_start_sg(chan, desc->sg_index);
|
||||
} else {
|
||||
desc->residue = usb_dmac_get_current_residue(chan, desc,
|
||||
desc->sg_index - 1);
|
||||
desc->done_cookie = desc->vd.tx.cookie;
|
||||
vchan_cookie_complete(&desc->vd);
|
||||
|
||||
/* Restart the next transfer if this driver has a next desc */
|
||||
usb_dmac_chan_start_desc(chan);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t usb_dmac_isr_channel(int irq, void *dev)
|
||||
{
|
||||
struct usb_dmac_chan *chan = dev;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 mask = USB_DMACHCR_TE;
|
||||
u32 check_bits = USB_DMACHCR_TE | USB_DMACHCR_SP;
|
||||
u32 chcr;
|
||||
|
||||
spin_lock(&chan->vc.lock);
|
||||
|
||||
chcr = usb_dmac_chan_read(chan, USB_DMACHCR);
|
||||
if (chcr & check_bits)
|
||||
mask |= USB_DMACHCR_DE | check_bits;
|
||||
if (chcr & USB_DMACHCR_NULL) {
|
||||
/* An interruption of TE will happen after we set FTE */
|
||||
mask |= USB_DMACHCR_NULL;
|
||||
chcr |= USB_DMACHCR_FTE;
|
||||
ret |= IRQ_HANDLED;
|
||||
}
|
||||
usb_dmac_chan_write(chan, USB_DMACHCR, chcr & ~mask);
|
||||
|
||||
if (chcr & check_bits) {
|
||||
usb_dmac_isr_transfer_end(chan);
|
||||
ret |= IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_unlock(&chan->vc.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* OF xlate and channel filter
|
||||
*/
|
||||
|
||||
static bool usb_dmac_chan_filter(struct dma_chan *chan, void *arg)
|
||||
{
|
||||
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
|
||||
struct of_phandle_args *dma_spec = arg;
|
||||
|
||||
if (dma_spec->np != chan->device->dev->of_node)
|
||||
return false;
|
||||
|
||||
/* USB-DMAC should be used with fixed usb controller's FIFO */
|
||||
if (uchan->index != dma_spec->args[0])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct dma_chan *usb_dmac_of_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
struct usb_dmac_chan *uchan;
|
||||
struct dma_chan *chan;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
if (dma_spec->args_count != 1)
|
||||
return NULL;
|
||||
|
||||
/* Only slave DMA channels can be allocated via DT */
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
chan = dma_request_channel(mask, usb_dmac_chan_filter, dma_spec);
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
uchan = to_usb_dmac_chan(chan);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Power management
|
||||
*/
|
||||
|
||||
static int usb_dmac_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_dmac *dmac = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dmac->n_channels; ++i)
|
||||
usb_dmac_chan_halt(&dmac->channels[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_dmac_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct usb_dmac *dmac = dev_get_drvdata(dev);
|
||||
|
||||
return usb_dmac_init(dmac);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops usb_dmac_pm = {
|
||||
SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Probe and remove
|
||||
*/
|
||||
|
||||
static int usb_dmac_chan_probe(struct usb_dmac *dmac,
|
||||
struct usb_dmac_chan *uchan,
|
||||
unsigned int index)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dmac->dev);
|
||||
char pdev_irqname[5];
|
||||
char *irqname;
|
||||
int ret;
|
||||
|
||||
uchan->index = index;
|
||||
uchan->iomem = dmac->iomem + USB_DMAC_CHAN_OFFSET(index);
|
||||
|
||||
/* Request the channel interrupt. */
|
||||
sprintf(pdev_irqname, "ch%u", index);
|
||||
uchan->irq = platform_get_irq_byname(pdev, pdev_irqname);
|
||||
if (uchan->irq < 0) {
|
||||
dev_err(dmac->dev, "no IRQ specified for channel %u\n", index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
|
||||
dev_name(dmac->dev), index);
|
||||
if (!irqname)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_request_irq(dmac->dev, uchan->irq, usb_dmac_isr_channel,
|
||||
IRQF_SHARED, irqname, uchan);
|
||||
if (ret) {
|
||||
dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
|
||||
uchan->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uchan->vc.desc_free = usb_dmac_virt_desc_free;
|
||||
vchan_init(&uchan->vc, &dmac->engine);
|
||||
INIT_LIST_HEAD(&uchan->desc_freed);
|
||||
INIT_LIST_HEAD(&uchan->desc_got);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_dmac_parse_of(struct device *dev, struct usb_dmac *dmac)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "dma-channels", &dmac->n_channels);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "unable to read dma-channels property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dmac->n_channels <= 0 || dmac->n_channels >= 100) {
|
||||
dev_err(dev, "invalid number of channels %u\n",
|
||||
dmac->n_channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_dmac_probe(struct platform_device *pdev)
|
||||
{
|
||||
const enum dma_slave_buswidth widths = USB_DMAC_SLAVE_BUSWIDTH;
|
||||
struct dma_device *engine;
|
||||
struct usb_dmac *dmac;
|
||||
struct resource *mem;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
|
||||
if (!dmac)
|
||||
return -ENOMEM;
|
||||
|
||||
dmac->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, dmac);
|
||||
|
||||
ret = usb_dmac_parse_of(&pdev->dev, dmac);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels,
|
||||
sizeof(*dmac->channels), GFP_KERNEL);
|
||||
if (!dmac->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Request resources. */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dmac->iomem = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(dmac->iomem))
|
||||
return PTR_ERR(dmac->iomem);
|
||||
|
||||
/* Enable runtime PM and initialize the device. */
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "runtime PM get sync failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_dmac_init(dmac);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to reset device\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Initialize the channels. */
|
||||
INIT_LIST_HEAD(&dmac->engine.channels);
|
||||
|
||||
for (i = 0; i < dmac->n_channels; ++i) {
|
||||
ret = usb_dmac_chan_probe(dmac, &dmac->channels[i], i);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Register the DMAC as a DMA provider for DT. */
|
||||
ret = of_dma_controller_register(pdev->dev.of_node, usb_dmac_of_xlate,
|
||||
NULL);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Register the DMA engine device.
|
||||
*
|
||||
* Default transfer size of 32 bytes requires 32-byte alignment.
|
||||
*/
|
||||
engine = &dmac->engine;
|
||||
dma_cap_set(DMA_SLAVE, engine->cap_mask);
|
||||
|
||||
engine->dev = &pdev->dev;
|
||||
|
||||
engine->src_addr_widths = widths;
|
||||
engine->dst_addr_widths = widths;
|
||||
engine->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
|
||||
engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
|
||||
|
||||
engine->device_alloc_chan_resources = usb_dmac_alloc_chan_resources;
|
||||
engine->device_free_chan_resources = usb_dmac_free_chan_resources;
|
||||
engine->device_prep_slave_sg = usb_dmac_prep_slave_sg;
|
||||
engine->device_terminate_all = usb_dmac_chan_terminate_all;
|
||||
engine->device_tx_status = usb_dmac_tx_status;
|
||||
engine->device_issue_pending = usb_dmac_issue_pending;
|
||||
|
||||
ret = dma_async_device_register(engine);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_dmac_chan_remove(struct usb_dmac *dmac,
|
||||
struct usb_dmac_chan *uchan)
|
||||
{
|
||||
usb_dmac_chan_halt(uchan);
|
||||
devm_free_irq(dmac->dev, uchan->irq, uchan);
|
||||
}
|
||||
|
||||
static int usb_dmac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_dmac *dmac = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dmac->n_channels; ++i)
|
||||
usb_dmac_chan_remove(dmac, &dmac->channels[i]);
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
dma_async_device_unregister(&dmac->engine);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_dmac_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_dmac *dmac = platform_get_drvdata(pdev);
|
||||
|
||||
usb_dmac_stop(dmac);
|
||||
}
|
||||
|
||||
static const struct of_device_id usb_dmac_of_ids[] = {
|
||||
{ .compatible = "renesas,usb-dmac", },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usb_dmac_of_ids);
|
||||
|
||||
static struct platform_driver usb_dmac_driver = {
|
||||
.driver = {
|
||||
.pm = &usb_dmac_pm,
|
||||
.name = "usb-dmac",
|
||||
.of_match_table = usb_dmac_of_ids,
|
||||
},
|
||||
.probe = usb_dmac_probe,
|
||||
.remove = usb_dmac_remove,
|
||||
.shutdown = usb_dmac_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver(usb_dmac_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas USB DMA Controller Driver");
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -896,7 +896,7 @@ static const struct dev_pm_ops sirfsoc_dma_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_dma_pm_suspend, sirfsoc_dma_pm_resume)
|
||||
};
|
||||
|
||||
static struct of_device_id sirfsoc_dma_match[] = {
|
||||
static const struct of_device_id sirfsoc_dma_match[] = {
|
||||
{ .compatible = "sirf,prima2-dmac", },
|
||||
{ .compatible = "sirf,marco-dmac", },
|
||||
{},
|
||||
|
@ -2514,7 +2514,8 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
|
||||
sg_dma_len(&dst_sg) = size;
|
||||
sg_dma_len(&src_sg) = size;
|
||||
|
||||
return d40_prep_sg(chan, &src_sg, &dst_sg, 1, DMA_NONE, dma_flags);
|
||||
return d40_prep_sg(chan, &src_sg, &dst_sg, 1,
|
||||
DMA_MEM_TO_MEM, dma_flags);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *
|
||||
@ -2526,7 +2527,8 @@ d40_prep_memcpy_sg(struct dma_chan *chan,
|
||||
if (dst_nents != src_nents)
|
||||
return NULL;
|
||||
|
||||
return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags);
|
||||
return d40_prep_sg(chan, src_sg, dst_sg, src_nents,
|
||||
DMA_MEM_TO_MEM, dma_flags);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *
|
||||
|
@ -796,11 +796,6 @@ static void sun6i_dma_issue_pending(struct dma_chan *chan)
|
||||
spin_unlock_irqrestore(&vchan->vc.lock, flags);
|
||||
}
|
||||
|
||||
static int sun6i_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sun6i_dma_free_chan_resources(struct dma_chan *chan)
|
||||
{
|
||||
struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
|
||||
@ -896,7 +891,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
|
||||
.nr_max_vchans = 37,
|
||||
};
|
||||
|
||||
static struct of_device_id sun6i_dma_match[] = {
|
||||
static const struct of_device_id sun6i_dma_match[] = {
|
||||
{ .compatible = "allwinner,sun6i-a31-dma", .data = &sun6i_a31_dma_cfg },
|
||||
{ .compatible = "allwinner,sun8i-a23-dma", .data = &sun8i_a23_dma_cfg },
|
||||
{ /* sentinel */ }
|
||||
@ -957,7 +952,6 @@ static int sun6i_dma_probe(struct platform_device *pdev)
|
||||
dma_cap_set(DMA_SLAVE, sdc->slave.cap_mask);
|
||||
|
||||
INIT_LIST_HEAD(&sdc->slave.channels);
|
||||
sdc->slave.device_alloc_chan_resources = sun6i_dma_alloc_chan_resources;
|
||||
sdc->slave.device_free_chan_resources = sun6i_dma_free_chan_resources;
|
||||
sdc->slave.device_tx_status = sun6i_dma_tx_status;
|
||||
sdc->slave.device_issue_pending = sun6i_dma_issue_pending;
|
||||
|
2089
drivers/dma/xgene-dma.c
Executable file
2089
drivers/dma/xgene-dma.c
Executable file
File diff suppressed because it is too large
Load Diff
@ -22,9 +22,9 @@
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/amba/xilinx_dma.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma/xilinx_dma.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -388,7 +388,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||
{
|
||||
struct dma_slave_config cfg = { 0, };
|
||||
struct dma_chan *chan;
|
||||
unsigned int slave_id;
|
||||
void *slave_data = NULL;
|
||||
struct resource *res;
|
||||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
@ -397,13 +397,12 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
if (pdata)
|
||||
slave_id = direction == DMA_MEM_TO_DEV
|
||||
? pdata->slave_id_tx : pdata->slave_id_rx;
|
||||
else
|
||||
slave_id = 0;
|
||||
slave_data = direction == DMA_MEM_TO_DEV ?
|
||||
(void *)pdata->slave_id_tx :
|
||||
(void *)pdata->slave_id_rx;
|
||||
|
||||
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
(void *)(unsigned long)slave_id, &host->pd->dev,
|
||||
slave_data, &host->pd->dev,
|
||||
direction == DMA_MEM_TO_DEV ? "tx" : "rx");
|
||||
|
||||
dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
|
||||
@ -414,8 +413,6 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||
|
||||
res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
|
||||
|
||||
/* In the OF case the driver will get the slave ID from the DT */
|
||||
cfg.slave_id = slave_id;
|
||||
cfg.direction = direction;
|
||||
|
||||
if (direction == DMA_DEV_TO_MEM) {
|
||||
|
@ -201,7 +201,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
|
||||
struct sh_mobile_sdhi *priv;
|
||||
struct tmio_mmc_data *mmc_data;
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
|
||||
struct tmio_mmc_host *host;
|
||||
struct resource *res;
|
||||
int irq, ret, i = 0;
|
||||
@ -245,30 +245,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
else
|
||||
host->bus_shift = 0;
|
||||
|
||||
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
|
||||
if (p) {
|
||||
mmc_data->flags = p->tmio_flags;
|
||||
mmc_data->ocr_mask = p->tmio_ocr_mask;
|
||||
mmc_data->capabilities |= p->tmio_caps;
|
||||
mmc_data->capabilities2 |= p->tmio_caps2;
|
||||
mmc_data->cd_gpio = p->cd_gpio;
|
||||
if (mmd)
|
||||
*mmc_data = *mmd;
|
||||
|
||||
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
|
||||
/*
|
||||
* Yes, we have to provide slave IDs twice to TMIO:
|
||||
* once as a filter parameter and once for channel
|
||||
* configuration as an explicit slave ID
|
||||
*/
|
||||
dma_priv->chan_priv_tx = (void *)p->dma_slave_tx;
|
||||
dma_priv->chan_priv_rx = (void *)p->dma_slave_rx;
|
||||
dma_priv->slave_id_tx = p->dma_slave_tx;
|
||||
dma_priv->slave_id_rx = p->dma_slave_rx;
|
||||
}
|
||||
}
|
||||
dma_priv->filter = shdma_chan_filter;
|
||||
dma_priv->enable = sh_mobile_sdhi_enable_dma;
|
||||
|
||||
mmc_data->alignment_shift = 1; /* 2-byte alignment */
|
||||
mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED;
|
||||
|
||||
/*
|
||||
* All SDHI blocks support 2-byte and larger block sizes in 4-bit
|
||||
|
@ -43,10 +43,6 @@ struct tmio_mmc_data;
|
||||
struct tmio_mmc_host;
|
||||
|
||||
struct tmio_mmc_dma {
|
||||
void *chan_priv_tx;
|
||||
void *chan_priv_rx;
|
||||
int slave_id_tx;
|
||||
int slave_id_rx;
|
||||
enum dma_slave_buswidth dma_buswidth;
|
||||
bool (*filter)(struct dma_chan *chan, void *arg);
|
||||
void (*enable)(struct tmio_mmc_host *host, bool enable);
|
||||
|
@ -261,7 +261,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
{
|
||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||
if (!host->dma || (!host->pdev->dev.of_node &&
|
||||
(!host->dma->chan_priv_tx || !host->dma->chan_priv_rx)))
|
||||
(!pdata->chan_priv_tx || !pdata->chan_priv_rx)))
|
||||
return;
|
||||
|
||||
if (!host->chan_tx && !host->chan_rx) {
|
||||
@ -278,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host->chan_tx = dma_request_slave_channel_compat(mask,
|
||||
host->dma->filter, host->dma->chan_priv_tx,
|
||||
host->dma->filter, pdata->chan_priv_tx,
|
||||
&host->pdev->dev, "tx");
|
||||
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
|
||||
host->chan_tx);
|
||||
@ -286,8 +286,6 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
if (!host->chan_tx)
|
||||
return;
|
||||
|
||||
if (host->dma->chan_priv_tx)
|
||||
cfg.slave_id = host->dma->slave_id_tx;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
|
||||
cfg.dst_addr_width = host->dma->dma_buswidth;
|
||||
@ -299,7 +297,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
goto ecfgtx;
|
||||
|
||||
host->chan_rx = dma_request_slave_channel_compat(mask,
|
||||
host->dma->filter, host->dma->chan_priv_rx,
|
||||
host->dma->filter, pdata->chan_priv_rx,
|
||||
&host->pdev->dev, "rx");
|
||||
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
|
||||
host->chan_rx);
|
||||
@ -307,8 +305,6 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
if (!host->chan_rx)
|
||||
goto ereqrx;
|
||||
|
||||
if (host->dma->chan_priv_rx)
|
||||
cfg.slave_id = host->dma->slave_id_rx;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset;
|
||||
cfg.src_addr_width = host->dma->dma_buswidth;
|
||||
|
@ -159,7 +159,6 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
|
||||
return;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.slave_id = pdata->slave_id_fifo0_tx;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl);
|
||||
cfg.src_addr = 0;
|
||||
@ -175,7 +174,6 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
|
||||
if (!flctl->chan_fifo0_rx)
|
||||
goto err;
|
||||
|
||||
cfg.slave_id = pdata->slave_id_fifo0_rx;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.dst_addr = 0;
|
||||
cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl);
|
||||
|
@ -1023,7 +1023,6 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev,
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.slave_id = id;
|
||||
cfg.direction = dir;
|
||||
if (dir == DMA_MEM_TO_DEV) {
|
||||
cfg.dst_addr = port_addr;
|
||||
|
@ -1030,7 +1030,6 @@ static struct dma_chan *sh_msiof_request_dma_chan(struct device *dev,
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.slave_id = id;
|
||||
cfg.direction = dir;
|
||||
if (dir == DMA_MEM_TO_DEV) {
|
||||
cfg.dst_addr = port_addr;
|
||||
|
49
include/dt-bindings/dma/jz4780-dma.h
Normal file
49
include/dt-bindings/dma/jz4780-dma.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef __DT_BINDINGS_DMA_JZ4780_DMA_H__
|
||||
#define __DT_BINDINGS_DMA_JZ4780_DMA_H__
|
||||
|
||||
/*
|
||||
* Request type numbers for the JZ4780 DMA controller (written to the DRTn
|
||||
* register for the channel).
|
||||
*/
|
||||
#define JZ4780_DMA_I2S1_TX 0x4
|
||||
#define JZ4780_DMA_I2S1_RX 0x5
|
||||
#define JZ4780_DMA_I2S0_TX 0x6
|
||||
#define JZ4780_DMA_I2S0_RX 0x7
|
||||
#define JZ4780_DMA_AUTO 0x8
|
||||
#define JZ4780_DMA_SADC_RX 0x9
|
||||
#define JZ4780_DMA_UART4_TX 0xc
|
||||
#define JZ4780_DMA_UART4_RX 0xd
|
||||
#define JZ4780_DMA_UART3_TX 0xe
|
||||
#define JZ4780_DMA_UART3_RX 0xf
|
||||
#define JZ4780_DMA_UART2_TX 0x10
|
||||
#define JZ4780_DMA_UART2_RX 0x11
|
||||
#define JZ4780_DMA_UART1_TX 0x12
|
||||
#define JZ4780_DMA_UART1_RX 0x13
|
||||
#define JZ4780_DMA_UART0_TX 0x14
|
||||
#define JZ4780_DMA_UART0_RX 0x15
|
||||
#define JZ4780_DMA_SSI0_TX 0x16
|
||||
#define JZ4780_DMA_SSI0_RX 0x17
|
||||
#define JZ4780_DMA_SSI1_TX 0x18
|
||||
#define JZ4780_DMA_SSI1_RX 0x19
|
||||
#define JZ4780_DMA_MSC0_TX 0x1a
|
||||
#define JZ4780_DMA_MSC0_RX 0x1b
|
||||
#define JZ4780_DMA_MSC1_TX 0x1c
|
||||
#define JZ4780_DMA_MSC1_RX 0x1d
|
||||
#define JZ4780_DMA_MSC2_TX 0x1e
|
||||
#define JZ4780_DMA_MSC2_RX 0x1f
|
||||
#define JZ4780_DMA_PCM0_TX 0x20
|
||||
#define JZ4780_DMA_PCM0_RX 0x21
|
||||
#define JZ4780_DMA_SMB0_TX 0x24
|
||||
#define JZ4780_DMA_SMB0_RX 0x25
|
||||
#define JZ4780_DMA_SMB1_TX 0x26
|
||||
#define JZ4780_DMA_SMB1_RX 0x27
|
||||
#define JZ4780_DMA_SMB2_TX 0x28
|
||||
#define JZ4780_DMA_SMB2_RX 0x29
|
||||
#define JZ4780_DMA_SMB3_TX 0x2a
|
||||
#define JZ4780_DMA_SMB3_RX 0x2b
|
||||
#define JZ4780_DMA_SMB4_TX 0x2c
|
||||
#define JZ4780_DMA_SMB4_RX 0x2d
|
||||
#define JZ4780_DMA_DES_TX 0x2e
|
||||
#define JZ4780_DMA_DES_RX 0x2f
|
||||
|
||||
#endif /* __DT_BINDINGS_DMA_JZ4780_DMA_H__ */
|
@ -11,10 +11,6 @@
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
@ -574,7 +570,6 @@ struct dma_tx_state {
|
||||
* @copy_align: alignment shift for memcpy operations
|
||||
* @xor_align: alignment shift for xor operations
|
||||
* @pq_align: alignment shift for pq operations
|
||||
* @fill_align: alignment shift for memset operations
|
||||
* @dev_id: unique device ID
|
||||
* @dev: struct device reference for dma mapping api
|
||||
* @src_addr_widths: bit mask of src addr widths the device supports
|
||||
@ -625,7 +620,6 @@ struct dma_device {
|
||||
u8 copy_align;
|
||||
u8 xor_align;
|
||||
u8 pq_align;
|
||||
u8 fill_align;
|
||||
#define DMA_HAS_PQ_CONTINUE (1 << 15)
|
||||
|
||||
int dev_id;
|
||||
@ -826,12 +820,6 @@ static inline bool is_dma_pq_aligned(struct dma_device *dev, size_t off1,
|
||||
return dmaengine_check_align(dev->pq_align, off1, off2, len);
|
||||
}
|
||||
|
||||
static inline bool is_dma_fill_aligned(struct dma_device *dev, size_t off1,
|
||||
size_t off2, size_t len)
|
||||
{
|
||||
return dmaengine_check_align(dev->fill_align, off1, off2, len);
|
||||
}
|
||||
|
||||
static inline void
|
||||
dma_set_maxpq(struct dma_device *dma, int maxpq, int has_pq_continue)
|
||||
{
|
||||
@ -1098,7 +1086,6 @@ void dma_async_device_unregister(struct dma_device *device);
|
||||
void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
|
||||
struct dma_chan *dma_get_slave_channel(struct dma_chan *chan);
|
||||
struct dma_chan *dma_get_any_slave_channel(struct dma_device *device);
|
||||
struct dma_chan *net_dma_find_channel(void);
|
||||
#define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
|
||||
#define dma_request_slave_channel_compat(mask, x, y, dev, name) \
|
||||
__dma_request_slave_channel_compat(&(mask), x, y, dev, name)
|
||||
@ -1116,27 +1103,4 @@ static inline struct dma_chan
|
||||
|
||||
return __dma_request_channel(mask, fn, fn_param);
|
||||
}
|
||||
|
||||
/* --- Helper iov-locking functions --- */
|
||||
|
||||
struct dma_page_list {
|
||||
char __user *base_address;
|
||||
int nr_pages;
|
||||
struct page **pages;
|
||||
};
|
||||
|
||||
struct dma_pinned_list {
|
||||
int nr_iovecs;
|
||||
struct dma_page_list page_list[0];
|
||||
};
|
||||
|
||||
struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len);
|
||||
void dma_unpin_iovec_pages(struct dma_pinned_list* pinned_list);
|
||||
|
||||
dma_cookie_t dma_memcpy_to_iovec(struct dma_chan *chan, struct iovec *iov,
|
||||
struct dma_pinned_list *pinned_list, unsigned char *kdata, size_t len);
|
||||
dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov,
|
||||
struct dma_pinned_list *pinned_list, struct page *page,
|
||||
unsigned int offset, size_t len);
|
||||
|
||||
#endif /* DMAENGINE_H */
|
||||
|
@ -111,6 +111,8 @@ struct dma_chan;
|
||||
* data for the MMC controller
|
||||
*/
|
||||
struct tmio_mmc_data {
|
||||
void *chan_priv_tx;
|
||||
void *chan_priv_rx;
|
||||
unsigned int hclk;
|
||||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
|
@ -7,14 +7,4 @@
|
||||
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
|
||||
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
|
||||
|
||||
struct sh_mobile_sdhi_info {
|
||||
int dma_slave_tx;
|
||||
int dma_slave_rx;
|
||||
unsigned long tmio_flags;
|
||||
unsigned long tmio_caps;
|
||||
unsigned long tmio_caps2;
|
||||
u32 tmio_ocr_mask; /* available MMC voltages */
|
||||
unsigned int cd_gpio;
|
||||
};
|
||||
|
||||
#endif /* LINUX_MMC_SH_MOBILE_SDHI_H */
|
||||
|
@ -48,6 +48,9 @@ struct sdma_script_start_addrs {
|
||||
s32 ssish_2_mcu_addr;
|
||||
s32 hdmi_dma_addr;
|
||||
/* End of v2 array */
|
||||
s32 zcanfd_2_mcu_addr;
|
||||
s32 zqspi_2_mcu_addr;
|
||||
/* End of v3 array */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -69,6 +69,7 @@ struct shdma_chan {
|
||||
int id; /* Raw id of this channel */
|
||||
int irq; /* Channel IRQ */
|
||||
int slave_id; /* Client ID for slave DMA */
|
||||
int real_slave_id; /* argument passed to filter function */
|
||||
int hw_req; /* DMA request line for slave DMA - same
|
||||
* as MID/RID, used with DT */
|
||||
enum shdma_pm_state pm_state;
|
||||
|
@ -250,6 +250,7 @@ struct fsi_clk {
|
||||
|
||||
struct fsi_priv {
|
||||
void __iomem *base;
|
||||
phys_addr_t phys;
|
||||
struct fsi_master *master;
|
||||
|
||||
struct fsi_stream playback;
|
||||
@ -1371,13 +1372,18 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev
|
||||
shdma_chan_filter, (void *)io->dma_id,
|
||||
dev, is_play ? "tx" : "rx");
|
||||
if (io->chan) {
|
||||
struct dma_slave_config cfg;
|
||||
struct dma_slave_config cfg = {};
|
||||
int ret;
|
||||
|
||||
cfg.slave_id = io->dma_id;
|
||||
cfg.dst_addr = 0; /* use default addr */
|
||||
cfg.src_addr = 0; /* use default addr */
|
||||
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
if (is_play) {
|
||||
cfg.dst_addr = fsi->phys + REG_DODT;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
} else {
|
||||
cfg.src_addr = fsi->phys + REG_DIDT;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(io->chan, &cfg);
|
||||
if (ret < 0) {
|
||||
@ -1974,6 +1980,7 @@ static int fsi_probe(struct platform_device *pdev)
|
||||
/* FSI A setting */
|
||||
fsi = &master->fsia;
|
||||
fsi->base = master->base;
|
||||
fsi->phys = res->start;
|
||||
fsi->master = master;
|
||||
fsi_port_info_init(fsi, &info.port_a);
|
||||
fsi_handler_init(fsi, &info.port_a);
|
||||
@ -1986,6 +1993,7 @@ static int fsi_probe(struct platform_device *pdev)
|
||||
/* FSI B setting */
|
||||
fsi = &master->fsib;
|
||||
fsi->base = master->base + 0x40;
|
||||
fsi->phys = res->start + 0x40;
|
||||
fsi->master = master;
|
||||
fsi_port_info_init(fsi, &info.port_b);
|
||||
fsi_handler_init(fsi, &info.port_b);
|
||||
|
Loading…
Reference in New Issue
Block a user