1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2015-04-24 09:49:37 -07:00
commit d6a4c0e5d3
81 changed files with 5771 additions and 776 deletions

View 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>;
};

View 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";
...
};

View File

@ -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

View File

@ -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";

View 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>;
};

View File

@ -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>

View File

@ -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,
};

View File

@ -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 */

View File

@ -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,
};

View File

@ -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 = {

View File

@ -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>;
};
};
};

View File

@ -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[] = {

View File

@ -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 = {

View File

@ -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,
};

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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", },
{},

View File

@ -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;

View 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");

View File

@ -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
*/

View File

@ -3,7 +3,7 @@
#
config DW_DMAC_CORE
tristate "Synopsys DesignWare AHB DMA support"
tristate
select DMA_ENGINE
config DW_DMAC

View File

@ -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;

View File

@ -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;

View 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");

View 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;
};

View File

@ -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;

View File

@ -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;

View File

@ -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".
*

View File

@ -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".
*

View File

@ -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.
*/

View File

@ -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".
*

View File

@ -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.
*/

View File

@ -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".
*

View File

@ -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.
*/

View File

@ -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".
*

View File

@ -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.
*/

View File

@ -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.
*
*/
/*

View File

@ -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;

View File

@ -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", },
{}
};

View File

@ -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, &param);
}
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},
{}

View File

@ -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", },
{},

View File

@ -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", },
{},
};

View File

@ -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

View File

@ -949,6 +949,7 @@ err_free_res:
err_disable_pdev:
pci_disable_device(pdev);
err_free_mem:
kfree(pd);
return err;
}

View File

@ -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)

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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;

View File

@ -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",

View File

@ -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.

View File

@ -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

View File

@ -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");

View File

@ -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);
}

View File

@ -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

View 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");

View File

@ -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", },
{},

View File

@ -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 *

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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>

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View 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__ */

View File

@ -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 */

View File

@ -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;

View File

@ -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 */

View File

@ -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 */
};
/**

View File

@ -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;

View File

@ -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);