Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma
Pull slave-dmaengine updates from Vinod Koul: "Once you have some time from extended weekend celebrations please consider pulling the following to get: - Various fixes and PCI driver for dw_dmac by Andy - DT binding for imx-dma by Markus & imx-sdma by Shawn - DT fixes for dmaengine by Lars - jz4740 dmac driver by Lars - and various fixes across the drivers" What "extended weekend celebrations"? I'm in the merge window, who has time for extended celebrations.. * 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma: (40 commits) DMA: shdma: add DT support DMA: shdma: shdma_chan_filter() has to be in shdma-base.h DMA: shdma: (cosmetic) don't re-calculate a pointer dmaengine: at_hdmac: prepare clk before calling enable dmaengine/trivial: at_hdmac: add curly brackets to if/else expressions dmaengine: at_hdmac: remove unsuded atc_cleanup_descriptors() dmaengine: at_hdmac: add FIFO configuration parameter to DMA DT binding ARM: at91: dt: add header to define at_hdmac configuration MIPS: jz4740: Correct clock gate bit for DMA controller MIPS: jz4740: Remove custom DMA API MIPS: jz4740: Register jz4740 DMA device dma: Add a jz4740 dmaengine driver MIPS: jz4740: Acquire and enable DMA controller clock dma: mmp_tdma: disable irq when disabling dma channel dmaengine: PL08x: Avoid collisions with get_signal() macro dmaengine: dw: select DW_DMAC_BIG_ENDIAN_IO automagically dma: dw: add PCI part of the driver dma: dw: split driver to library part and platform code dma: move dw_dmac driver to an own directory dw_dmac: don't check resource with devm_ioremap_resource ...
This commit is contained in:
commit
d2b4a64671
@ -24,8 +24,11 @@ The three cells in order are:
|
|||||||
1. A phandle pointing to the DMA controller.
|
1. A phandle pointing to the DMA controller.
|
||||||
2. The memory interface (16 most significant bits), the peripheral interface
|
2. The memory interface (16 most significant bits), the peripheral interface
|
||||||
(16 less significant bits).
|
(16 less significant bits).
|
||||||
3. The peripheral identifier for the hardware handshaking interface. The
|
3. Parameters for the at91 DMA configuration register which are device
|
||||||
identifier can be different for tx and rx.
|
dependant:
|
||||||
|
- bit 7-0: peripheral identifier for the hardware handshaking interface. The
|
||||||
|
identifier can be different for tx and rx.
|
||||||
|
- bit 11-8: FIFO configuration. 0 for half FIFO, 1 for ALAP, 1 for ASAP.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
48
Documentation/devicetree/bindings/dma/fsl-imx-dma.txt
Normal file
48
Documentation/devicetree/bindings/dma/fsl-imx-dma.txt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
* Freescale Direct Memory Access (DMA) Controller for i.MX
|
||||||
|
|
||||||
|
This document will only describe differences to the generic DMA Controller and
|
||||||
|
DMA request bindings as described in dma/dma.txt .
|
||||||
|
|
||||||
|
* DMA controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be "fsl,<chip>-dma". chip can be imx1, imx21 or imx27
|
||||||
|
- reg : Should contain DMA registers location and length
|
||||||
|
- interrupts : First item should be DMA interrupt, second one is optional and
|
||||||
|
should contain DMA Error interrupt
|
||||||
|
- #dma-cells : Has to be 1. imx-dma does not support anything else.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- #dma-channels : Number of DMA channels supported. Should be 16.
|
||||||
|
- #dma-requests : Number of DMA requests supported.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
dma: dma@10001000 {
|
||||||
|
compatible = "fsl,imx27-dma";
|
||||||
|
reg = <0x10001000 0x1000>;
|
||||||
|
interrupts = <32 33>;
|
||||||
|
#dma-cells = <1>;
|
||||||
|
#dma-channels = <16>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
* DMA client
|
||||||
|
|
||||||
|
Clients have to specify the DMA requests with phandles in a list.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- dmas: List of one or more DMA request specifiers. One DMA request specifier
|
||||||
|
consists of a phandle to the DMA controller followed by the integer
|
||||||
|
specifiying the request line.
|
||||||
|
- dma-names: List of string identifiers for the DMA requests. For the correct
|
||||||
|
names, have a look at the specific client driver.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
sdhci1: sdhci@10013000 {
|
||||||
|
...
|
||||||
|
dmas = <&dma 7>;
|
||||||
|
dma-names = "rx-tx";
|
||||||
|
...
|
||||||
|
};
|
@ -4,14 +4,70 @@ Required properties:
|
|||||||
- compatible : Should be "fsl,<chip>-sdma"
|
- compatible : Should be "fsl,<chip>-sdma"
|
||||||
- reg : Should contain SDMA registers location and length
|
- reg : Should contain SDMA registers location and length
|
||||||
- interrupts : Should contain SDMA interrupt
|
- interrupts : Should contain SDMA interrupt
|
||||||
|
- #dma-cells : Must be <3>.
|
||||||
|
The first cell specifies the DMA request/event ID. See details below
|
||||||
|
about the second and third cell.
|
||||||
- fsl,sdma-ram-script-name : Should contain the full path of SDMA RAM
|
- fsl,sdma-ram-script-name : Should contain the full path of SDMA RAM
|
||||||
scripts firmware
|
scripts firmware
|
||||||
|
|
||||||
|
The second cell of dma phandle specifies the peripheral type of DMA transfer.
|
||||||
|
The full ID of peripheral types can be found below.
|
||||||
|
|
||||||
|
ID transfer type
|
||||||
|
---------------------
|
||||||
|
0 MCU domain SSI
|
||||||
|
1 Shared SSI
|
||||||
|
2 MMC
|
||||||
|
3 SDHC
|
||||||
|
4 MCU domain UART
|
||||||
|
5 Shared UART
|
||||||
|
6 FIRI
|
||||||
|
7 MCU domain CSPI
|
||||||
|
8 Shared CSPI
|
||||||
|
9 SIM
|
||||||
|
10 ATA
|
||||||
|
11 CCM
|
||||||
|
12 External peripheral
|
||||||
|
13 Memory Stick Host Controller
|
||||||
|
14 Shared Memory Stick Host Controller
|
||||||
|
15 DSP
|
||||||
|
16 Memory
|
||||||
|
17 FIFO type Memory
|
||||||
|
18 SPDIF
|
||||||
|
19 IPU Memory
|
||||||
|
20 ASRC
|
||||||
|
21 ESAI
|
||||||
|
|
||||||
|
The third cell specifies the transfer priority as below.
|
||||||
|
|
||||||
|
ID transfer priority
|
||||||
|
-------------------------
|
||||||
|
0 High
|
||||||
|
1 Medium
|
||||||
|
2 Low
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
sdma@83fb0000 {
|
sdma@83fb0000 {
|
||||||
compatible = "fsl,imx51-sdma", "fsl,imx35-sdma";
|
compatible = "fsl,imx51-sdma", "fsl,imx35-sdma";
|
||||||
reg = <0x83fb0000 0x4000>;
|
reg = <0x83fb0000 0x4000>;
|
||||||
interrupts = <6>;
|
interrupts = <6>;
|
||||||
|
#dma-cells = <3>;
|
||||||
fsl,sdma-ram-script-name = "sdma-imx51.bin";
|
fsl,sdma-ram-script-name = "sdma-imx51.bin";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DMA clients connected to the i.MX SDMA controller must use the format
|
||||||
|
described in the dma.txt file.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
ssi2: ssi@70014000 {
|
||||||
|
compatible = "fsl,imx51-ssi", "fsl,imx21-ssi";
|
||||||
|
reg = <0x70014000 0x4000>;
|
||||||
|
interrupts = <30>;
|
||||||
|
clocks = <&clks 49>;
|
||||||
|
dmas = <&sdma 24 1 0>,
|
||||||
|
<&sdma 25 1 0>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
fsl,fifo-depth = <15>;
|
||||||
|
};
|
||||||
|
75
Documentation/devicetree/bindings/dma/shdma.txt
Normal file
75
Documentation/devicetree/bindings/dma/shdma.txt
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
* SHDMA Device Tree bindings
|
||||||
|
|
||||||
|
Sh-/r-mobile and r-car systems often have multiple identical DMA controller
|
||||||
|
instances, capable of serving any of a common set of DMA slave devices, using
|
||||||
|
the same configuration. To describe this topology we require all compatible
|
||||||
|
SHDMA DT nodes to be placed under a DMA multiplexer node. All such compatible
|
||||||
|
DMAC instances have the same number of channels and use the same DMA
|
||||||
|
descriptors. Therefore respective DMA DT bindings can also all be placed in the
|
||||||
|
multiplexer node. Even if there is only one such DMAC instance on a system, it
|
||||||
|
still has to be placed under such a multiplexer node.
|
||||||
|
|
||||||
|
* DMA multiplexer
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "renesas,shdma-mux"
|
||||||
|
- #dma-cells: should be <1>, see "dmas" property below
|
||||||
|
|
||||||
|
Optional properties (currently unused):
|
||||||
|
- dma-channels: number of DMA channels
|
||||||
|
- dma-requests: number of DMA request signals
|
||||||
|
|
||||||
|
* DMA controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "renesas,shdma"
|
||||||
|
|
||||||
|
Example:
|
||||||
|
dmac: dma-mux0 {
|
||||||
|
compatible = "renesas,shdma-mux";
|
||||||
|
#dma-cells = <1>;
|
||||||
|
dma-channels = <6>;
|
||||||
|
dma-requests = <256>;
|
||||||
|
reg = <0 0>; /* Needed for AUXDATA */
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
ranges;
|
||||||
|
|
||||||
|
dma0: shdma@fe008020 {
|
||||||
|
compatible = "renesas,shdma";
|
||||||
|
reg = <0xfe008020 0x270>,
|
||||||
|
<0xfe009000 0xc>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
interrupts = <0 34 4
|
||||||
|
0 28 4
|
||||||
|
0 29 4
|
||||||
|
0 30 4
|
||||||
|
0 31 4
|
||||||
|
0 32 4
|
||||||
|
0 33 4>;
|
||||||
|
interrupt-names = "error",
|
||||||
|
"ch0", "ch1", "ch2", "ch3",
|
||||||
|
"ch4", "ch5";
|
||||||
|
};
|
||||||
|
|
||||||
|
dma1: shdma@fe018020 {
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
dma2: shdma@fe028020 {
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
* DMA client
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- dmas: a list of <[DMA multiplexer phandle] [MID/RID value]> pairs,
|
||||||
|
where MID/RID values are fixed handles, specified in the SoC
|
||||||
|
manual
|
||||||
|
- dma-names: a list of DMA channel names, one per "dmas" entry
|
||||||
|
|
||||||
|
Example:
|
||||||
|
dmas = <&dmac 0xd1
|
||||||
|
&dmac 0xd2>;
|
||||||
|
dma-names = "tx", "rx";
|
@ -7057,8 +7057,7 @@ SYNOPSYS DESIGNWARE DMAC DRIVER
|
|||||||
M: Viresh Kumar <viresh.linux@gmail.com>
|
M: Viresh Kumar <viresh.linux@gmail.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: include/linux/dw_dmac.h
|
F: include/linux/dw_dmac.h
|
||||||
F: drivers/dma/dw_dmac_regs.h
|
F: drivers/dma/dw/
|
||||||
F: drivers/dma/dw_dmac.c
|
|
||||||
|
|
||||||
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
|
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
|
||||||
M: Seungwon Jeon <tgih.jun@samsung.com>
|
M: Seungwon Jeon <tgih.jun@samsung.com>
|
||||||
|
@ -182,8 +182,8 @@ static void pl08x_put_signal(const struct pl08x_channel_data *cd, int ch)
|
|||||||
static struct pl08x_platform_data pl08x_pd = {
|
static struct pl08x_platform_data pl08x_pd = {
|
||||||
.slave_channels = &pl08x_slave_channels[0],
|
.slave_channels = &pl08x_slave_channels[0],
|
||||||
.num_slave_channels = ARRAY_SIZE(pl08x_slave_channels),
|
.num_slave_channels = ARRAY_SIZE(pl08x_slave_channels),
|
||||||
.get_signal = pl08x_get_signal,
|
.get_xfer_signal = pl08x_get_signal,
|
||||||
.put_signal = pl08x_put_signal,
|
.put_xfer_signal = pl08x_put_signal,
|
||||||
.lli_buses = PL08X_AHB1,
|
.lli_buses = PL08X_AHB1,
|
||||||
.mem_buses = PL08X_AHB1,
|
.mem_buses = PL08X_AHB1,
|
||||||
};
|
};
|
||||||
|
@ -56,8 +56,8 @@ struct pl08x_platform_data pl080_plat_data = {
|
|||||||
},
|
},
|
||||||
.lli_buses = PL08X_AHB1,
|
.lli_buses = PL08X_AHB1,
|
||||||
.mem_buses = PL08X_AHB1,
|
.mem_buses = PL08X_AHB1,
|
||||||
.get_signal = pl080_get_signal,
|
.get_xfer_signal = pl080_get_signal,
|
||||||
.put_signal = pl080_put_signal,
|
.put_xfer_signal = pl080_put_signal,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -334,8 +334,8 @@ static struct pl08x_platform_data spear6xx_pl080_plat_data = {
|
|||||||
},
|
},
|
||||||
.lli_buses = PL08X_AHB1,
|
.lli_buses = PL08X_AHB1,
|
||||||
.mem_buses = PL08X_AHB1,
|
.mem_buses = PL08X_AHB1,
|
||||||
.get_signal = pl080_get_signal,
|
.get_xfer_signal = pl080_get_signal,
|
||||||
.put_signal = pl080_put_signal,
|
.put_xfer_signal = pl080_put_signal,
|
||||||
.slave_channels = spear600_dma_info,
|
.slave_channels = spear600_dma_info,
|
||||||
.num_slave_channels = ARRAY_SIZE(spear600_dma_info),
|
.num_slave_channels = ARRAY_SIZE(spear600_dma_info),
|
||||||
};
|
};
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
#ifndef __ASM_MACH_JZ4740_DMA_H__
|
#ifndef __ASM_MACH_JZ4740_DMA_H__
|
||||||
#define __ASM_MACH_JZ4740_DMA_H__
|
#define __ASM_MACH_JZ4740_DMA_H__
|
||||||
|
|
||||||
struct jz4740_dma_chan;
|
|
||||||
|
|
||||||
enum jz4740_dma_request_type {
|
enum jz4740_dma_request_type {
|
||||||
JZ4740_DMA_TYPE_AUTO_REQUEST = 8,
|
JZ4740_DMA_TYPE_AUTO_REQUEST = 8,
|
||||||
JZ4740_DMA_TYPE_UART_TRANSMIT = 20,
|
JZ4740_DMA_TYPE_UART_TRANSMIT = 20,
|
||||||
@ -33,58 +31,4 @@ enum jz4740_dma_request_type {
|
|||||||
JZ4740_DMA_TYPE_SLCD = 30,
|
JZ4740_DMA_TYPE_SLCD = 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum jz4740_dma_width {
|
|
||||||
JZ4740_DMA_WIDTH_32BIT = 0,
|
|
||||||
JZ4740_DMA_WIDTH_8BIT = 1,
|
|
||||||
JZ4740_DMA_WIDTH_16BIT = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum jz4740_dma_transfer_size {
|
|
||||||
JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0,
|
|
||||||
JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1,
|
|
||||||
JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2,
|
|
||||||
JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3,
|
|
||||||
JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum jz4740_dma_flags {
|
|
||||||
JZ4740_DMA_SRC_AUTOINC = 0x2,
|
|
||||||
JZ4740_DMA_DST_AUTOINC = 0x1,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum jz4740_dma_mode {
|
|
||||||
JZ4740_DMA_MODE_SINGLE = 0,
|
|
||||||
JZ4740_DMA_MODE_BLOCK = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jz4740_dma_config {
|
|
||||||
enum jz4740_dma_width src_width;
|
|
||||||
enum jz4740_dma_width dst_width;
|
|
||||||
enum jz4740_dma_transfer_size transfer_size;
|
|
||||||
enum jz4740_dma_request_type request_type;
|
|
||||||
enum jz4740_dma_flags flags;
|
|
||||||
enum jz4740_dma_mode mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int, void *);
|
|
||||||
|
|
||||||
struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name);
|
|
||||||
void jz4740_dma_free(struct jz4740_dma_chan *dma);
|
|
||||||
|
|
||||||
void jz4740_dma_configure(struct jz4740_dma_chan *dma,
|
|
||||||
const struct jz4740_dma_config *config);
|
|
||||||
|
|
||||||
|
|
||||||
void jz4740_dma_enable(struct jz4740_dma_chan *dma);
|
|
||||||
void jz4740_dma_disable(struct jz4740_dma_chan *dma);
|
|
||||||
|
|
||||||
void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src);
|
|
||||||
void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst);
|
|
||||||
void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count);
|
|
||||||
|
|
||||||
uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma);
|
|
||||||
|
|
||||||
void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
|
|
||||||
jz4740_dma_complete_callback_t cb);
|
|
||||||
|
|
||||||
#endif /* __ASM_JZ4740_DMA_H__ */
|
#endif /* __ASM_JZ4740_DMA_H__ */
|
||||||
|
@ -32,6 +32,7 @@ extern struct platform_device jz4740_codec_device;
|
|||||||
extern struct platform_device jz4740_adc_device;
|
extern struct platform_device jz4740_adc_device;
|
||||||
extern struct platform_device jz4740_wdt_device;
|
extern struct platform_device jz4740_wdt_device;
|
||||||
extern struct platform_device jz4740_pwm_device;
|
extern struct platform_device jz4740_pwm_device;
|
||||||
|
extern struct platform_device jz4740_dma_device;
|
||||||
|
|
||||||
void jz4740_serial_device_register(void);
|
void jz4740_serial_device_register(void);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# Object file lists.
|
# Object file lists.
|
||||||
|
|
||||||
obj-y += prom.o irq.o time.o reset.o setup.o dma.o \
|
obj-y += prom.o irq.o time.o reset.o setup.o \
|
||||||
gpio.o clock.o platform.o timer.o serial.o
|
gpio.o clock.o platform.o timer.o serial.o
|
||||||
|
|
||||||
obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
|
obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
|
||||||
|
@ -438,6 +438,7 @@ static struct platform_device *jz_platform_devices[] __initdata = {
|
|||||||
&jz4740_rtc_device,
|
&jz4740_rtc_device,
|
||||||
&jz4740_adc_device,
|
&jz4740_adc_device,
|
||||||
&jz4740_pwm_device,
|
&jz4740_pwm_device,
|
||||||
|
&jz4740_dma_device,
|
||||||
&qi_lb60_gpio_keys,
|
&qi_lb60_gpio_keys,
|
||||||
&qi_lb60_pwm_beeper,
|
&qi_lb60_pwm_beeper,
|
||||||
&qi_lb60_charger_device,
|
&qi_lb60_charger_device,
|
||||||
|
@ -687,7 +687,7 @@ static struct clk jz4740_clock_simple_clks[] = {
|
|||||||
[3] = {
|
[3] = {
|
||||||
.name = "dma",
|
.name = "dma",
|
||||||
.parent = &jz_clk_high_speed_peripheral.clk,
|
.parent = &jz_clk_high_speed_peripheral.clk,
|
||||||
.gate_bit = JZ_CLOCK_GATE_UART0,
|
.gate_bit = JZ_CLOCK_GATE_DMAC,
|
||||||
.ops = &jz_clk_simple_ops,
|
.ops = &jz_clk_simple_ops,
|
||||||
},
|
},
|
||||||
[4] = {
|
[4] = {
|
||||||
|
@ -1,287 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
|
|
||||||
* JZ4740 SoC DMA support
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
|
|
||||||
#include <linux/dma-mapping.h>
|
|
||||||
#include <asm/mach-jz4740/dma.h>
|
|
||||||
#include <asm/mach-jz4740/base.h>
|
|
||||||
|
|
||||||
#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20)
|
|
||||||
#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20)
|
|
||||||
#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20)
|
|
||||||
#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20)
|
|
||||||
#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20)
|
|
||||||
#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20)
|
|
||||||
#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20)
|
|
||||||
|
|
||||||
#define JZ_REG_DMA_CTRL 0x300
|
|
||||||
#define JZ_REG_DMA_IRQ 0x304
|
|
||||||
#define JZ_REG_DMA_DOORBELL 0x308
|
|
||||||
#define JZ_REG_DMA_DOORBELL_SET 0x30C
|
|
||||||
|
|
||||||
#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31)
|
|
||||||
#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6)
|
|
||||||
#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4)
|
|
||||||
#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3)
|
|
||||||
#define JZ_DMA_STATUS_CTRL_HALT BIT(2)
|
|
||||||
#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1)
|
|
||||||
#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0)
|
|
||||||
|
|
||||||
#define JZ_DMA_CMD_SRC_INC BIT(23)
|
|
||||||
#define JZ_DMA_CMD_DST_INC BIT(22)
|
|
||||||
#define JZ_DMA_CMD_RDIL_MASK (0xf << 16)
|
|
||||||
#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14)
|
|
||||||
#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12)
|
|
||||||
#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8)
|
|
||||||
#define JZ_DMA_CMD_BLOCK_MODE BIT(7)
|
|
||||||
#define JZ_DMA_CMD_DESC_VALID BIT(4)
|
|
||||||
#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3)
|
|
||||||
#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2)
|
|
||||||
#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1)
|
|
||||||
#define JZ_DMA_CMD_LINK_ENABLE BIT(0)
|
|
||||||
|
|
||||||
#define JZ_DMA_CMD_FLAGS_OFFSET 22
|
|
||||||
#define JZ_DMA_CMD_RDIL_OFFSET 16
|
|
||||||
#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14
|
|
||||||
#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12
|
|
||||||
#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8
|
|
||||||
#define JZ_DMA_CMD_MODE_OFFSET 7
|
|
||||||
|
|
||||||
#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8)
|
|
||||||
#define JZ_DMA_CTRL_HALT BIT(3)
|
|
||||||
#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2)
|
|
||||||
#define JZ_DMA_CTRL_ENABLE BIT(0)
|
|
||||||
|
|
||||||
|
|
||||||
static void __iomem *jz4740_dma_base;
|
|
||||||
static spinlock_t jz4740_dma_lock;
|
|
||||||
|
|
||||||
static inline uint32_t jz4740_dma_read(size_t reg)
|
|
||||||
{
|
|
||||||
return readl(jz4740_dma_base + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void jz4740_dma_write(size_t reg, uint32_t val)
|
|
||||||
{
|
|
||||||
writel(val, jz4740_dma_base + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask)
|
|
||||||
{
|
|
||||||
uint32_t val2;
|
|
||||||
val2 = jz4740_dma_read(reg);
|
|
||||||
val2 &= ~mask;
|
|
||||||
val2 |= val;
|
|
||||||
jz4740_dma_write(reg, val2);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct jz4740_dma_chan {
|
|
||||||
unsigned int id;
|
|
||||||
void *dev;
|
|
||||||
const char *name;
|
|
||||||
|
|
||||||
enum jz4740_dma_flags flags;
|
|
||||||
uint32_t transfer_shift;
|
|
||||||
|
|
||||||
jz4740_dma_complete_callback_t complete_cb;
|
|
||||||
|
|
||||||
unsigned used:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define JZ4740_DMA_CHANNEL(_id) { .id = _id }
|
|
||||||
|
|
||||||
struct jz4740_dma_chan jz4740_dma_channels[] = {
|
|
||||||
JZ4740_DMA_CHANNEL(0),
|
|
||||||
JZ4740_DMA_CHANNEL(1),
|
|
||||||
JZ4740_DMA_CHANNEL(2),
|
|
||||||
JZ4740_DMA_CHANNEL(3),
|
|
||||||
JZ4740_DMA_CHANNEL(4),
|
|
||||||
JZ4740_DMA_CHANNEL(5),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
struct jz4740_dma_chan *dma = NULL;
|
|
||||||
|
|
||||||
spin_lock(&jz4740_dma_lock);
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) {
|
|
||||||
if (!jz4740_dma_channels[i].used) {
|
|
||||||
dma = &jz4740_dma_channels[i];
|
|
||||||
dma->used = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&jz4740_dma_lock);
|
|
||||||
|
|
||||||
if (!dma)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dma->dev = dev;
|
|
||||||
dma->name = name;
|
|
||||||
|
|
||||||
return dma;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_request);
|
|
||||||
|
|
||||||
void jz4740_dma_configure(struct jz4740_dma_chan *dma,
|
|
||||||
const struct jz4740_dma_config *config)
|
|
||||||
{
|
|
||||||
uint32_t cmd;
|
|
||||||
|
|
||||||
switch (config->transfer_size) {
|
|
||||||
case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
|
|
||||||
dma->transfer_shift = 1;
|
|
||||||
break;
|
|
||||||
case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
|
|
||||||
dma->transfer_shift = 2;
|
|
||||||
break;
|
|
||||||
case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
|
|
||||||
dma->transfer_shift = 4;
|
|
||||||
break;
|
|
||||||
case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
|
|
||||||
dma->transfer_shift = 5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dma->transfer_shift = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET;
|
|
||||||
cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
|
|
||||||
cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
|
|
||||||
cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
|
|
||||||
cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET;
|
|
||||||
cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
|
|
||||||
|
|
||||||
jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd);
|
|
||||||
jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0);
|
|
||||||
jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_configure);
|
|
||||||
|
|
||||||
void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src)
|
|
||||||
{
|
|
||||||
jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr);
|
|
||||||
|
|
||||||
void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst)
|
|
||||||
{
|
|
||||||
jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr);
|
|
||||||
|
|
||||||
void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count)
|
|
||||||
{
|
|
||||||
count >>= dma->transfer_shift;
|
|
||||||
jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count);
|
|
||||||
|
|
||||||
void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
|
|
||||||
jz4740_dma_complete_callback_t cb)
|
|
||||||
{
|
|
||||||
dma->complete_cb = cb;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb);
|
|
||||||
|
|
||||||
void jz4740_dma_free(struct jz4740_dma_chan *dma)
|
|
||||||
{
|
|
||||||
dma->dev = NULL;
|
|
||||||
dma->complete_cb = NULL;
|
|
||||||
dma->used = 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_free);
|
|
||||||
|
|
||||||
void jz4740_dma_enable(struct jz4740_dma_chan *dma)
|
|
||||||
{
|
|
||||||
jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id),
|
|
||||||
JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE,
|
|
||||||
JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC |
|
|
||||||
JZ_DMA_STATUS_CTRL_ENABLE);
|
|
||||||
|
|
||||||
jz4740_dma_write_mask(JZ_REG_DMA_CTRL,
|
|
||||||
JZ_DMA_CTRL_ENABLE,
|
|
||||||
JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_enable);
|
|
||||||
|
|
||||||
void jz4740_dma_disable(struct jz4740_dma_chan *dma)
|
|
||||||
{
|
|
||||||
jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
|
|
||||||
JZ_DMA_STATUS_CTRL_ENABLE);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_disable);
|
|
||||||
|
|
||||||
uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma)
|
|
||||||
{
|
|
||||||
uint32_t residue;
|
|
||||||
residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id));
|
|
||||||
return residue << dma->transfer_shift;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(jz4740_dma_get_residue);
|
|
||||||
|
|
||||||
static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma)
|
|
||||||
{
|
|
||||||
(void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id));
|
|
||||||
|
|
||||||
jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
|
|
||||||
JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
|
|
||||||
|
|
||||||
if (dma->complete_cb)
|
|
||||||
dma->complete_cb(dma, 0, dma->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
uint32_t irq_status;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ);
|
|
||||||
|
|
||||||
for (i = 0; i < 6; ++i) {
|
|
||||||
if (irq_status & (1 << i))
|
|
||||||
jz4740_dma_chan_irq(&jz4740_dma_channels[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jz4740_dma_init(void)
|
|
||||||
{
|
|
||||||
unsigned int ret;
|
|
||||||
|
|
||||||
jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400);
|
|
||||||
|
|
||||||
if (!jz4740_dma_base)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
spin_lock_init(&jz4740_dma_lock);
|
|
||||||
|
|
||||||
ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
arch_initcall(jz4740_dma_init);
|
|
@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = {
|
|||||||
.name = "jz4740-pwm",
|
.name = "jz4740-pwm",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* DMA */
|
||||||
|
static struct resource jz4740_dma_resources[] = {
|
||||||
|
{
|
||||||
|
.start = JZ4740_DMAC_BASE_ADDR,
|
||||||
|
.end = JZ4740_DMAC_BASE_ADDR + 0x400 - 1,
|
||||||
|
.flags = IORESOURCE_MEM,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.start = JZ4740_IRQ_DMAC,
|
||||||
|
.end = JZ4740_IRQ_DMAC,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct platform_device jz4740_dma_device = {
|
||||||
|
.name = "jz4740-dma",
|
||||||
|
.id = -1,
|
||||||
|
.num_resources = ARRAY_SIZE(jz4740_dma_resources),
|
||||||
|
.resource = jz4740_dma_resources,
|
||||||
|
};
|
||||||
|
@ -79,25 +79,7 @@ config INTEL_IOP_ADMA
|
|||||||
help
|
help
|
||||||
Enable support for the Intel(R) IOP Series RAID engines.
|
Enable support for the Intel(R) IOP Series RAID engines.
|
||||||
|
|
||||||
config DW_DMAC
|
source "drivers/dma/dw/Kconfig"
|
||||||
tristate "Synopsys DesignWare AHB DMA support"
|
|
||||||
depends on GENERIC_HARDIRQS
|
|
||||||
select DMA_ENGINE
|
|
||||||
default y if CPU_AT32AP7000
|
|
||||||
help
|
|
||||||
Support the Synopsys DesignWare AHB DMA controller. This
|
|
||||||
can be integrated in chips such as the Atmel AT32ap7000.
|
|
||||||
|
|
||||||
config DW_DMAC_BIG_ENDIAN_IO
|
|
||||||
bool "Use big endian I/O register access"
|
|
||||||
default y if AVR32
|
|
||||||
depends on DW_DMAC
|
|
||||||
help
|
|
||||||
Say yes here to use big endian I/O access when reading and writing
|
|
||||||
to the DMA controller registers. This is needed on some platforms,
|
|
||||||
like the Atmel AVR32 architecture.
|
|
||||||
|
|
||||||
If unsure, use the default setting.
|
|
||||||
|
|
||||||
config AT_HDMAC
|
config AT_HDMAC
|
||||||
tristate "Atmel AHB DMA support"
|
tristate "Atmel AHB DMA support"
|
||||||
@ -312,6 +294,12 @@ config MMP_PDMA
|
|||||||
help
|
help
|
||||||
Support the MMP PDMA engine for PXA and MMP platfrom.
|
Support the MMP PDMA engine for PXA and MMP platfrom.
|
||||||
|
|
||||||
|
config DMA_JZ4740
|
||||||
|
tristate "JZ4740 DMA support"
|
||||||
|
depends on MACH_JZ4740
|
||||||
|
select DMA_ENGINE
|
||||||
|
select DMA_VIRTUAL_CHANNELS
|
||||||
|
|
||||||
config DMA_ENGINE
|
config DMA_ENGINE
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ obj-$(CONFIG_FSL_DMA) += fsldma.o
|
|||||||
obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o
|
obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o
|
||||||
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
|
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
|
||||||
obj-$(CONFIG_MV_XOR) += mv_xor.o
|
obj-$(CONFIG_MV_XOR) += mv_xor.o
|
||||||
obj-$(CONFIG_DW_DMAC) += dw_dmac.o
|
obj-$(CONFIG_DW_DMAC_CORE) += dw/
|
||||||
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
|
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
|
||||||
obj-$(CONFIG_MX3_IPU) += ipu/
|
obj-$(CONFIG_MX3_IPU) += ipu/
|
||||||
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
|
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
|
||||||
@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
|
|||||||
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
|
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
|
||||||
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
|
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
|
||||||
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
|
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
|
||||||
|
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
|
||||||
|
@ -299,8 +299,8 @@ static int pl08x_request_mux(struct pl08x_dma_chan *plchan)
|
|||||||
const struct pl08x_platform_data *pd = plchan->host->pd;
|
const struct pl08x_platform_data *pd = plchan->host->pd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (plchan->mux_use++ == 0 && pd->get_signal) {
|
if (plchan->mux_use++ == 0 && pd->get_xfer_signal) {
|
||||||
ret = pd->get_signal(plchan->cd);
|
ret = pd->get_xfer_signal(plchan->cd);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
plchan->mux_use = 0;
|
plchan->mux_use = 0;
|
||||||
return ret;
|
return ret;
|
||||||
@ -318,8 +318,8 @@ static void pl08x_release_mux(struct pl08x_dma_chan *plchan)
|
|||||||
if (plchan->signal >= 0) {
|
if (plchan->signal >= 0) {
|
||||||
WARN_ON(plchan->mux_use == 0);
|
WARN_ON(plchan->mux_use == 0);
|
||||||
|
|
||||||
if (--plchan->mux_use == 0 && pd->put_signal) {
|
if (--plchan->mux_use == 0 && pd->put_xfer_signal) {
|
||||||
pd->put_signal(plchan->cd, plchan->signal);
|
pd->put_xfer_signal(plchan->cd, plchan->signal);
|
||||||
plchan->signal = -1;
|
plchan->signal = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* found on AT91SAM9263.
|
* found on AT91SAM9263.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <dt-bindings/dma/at91.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
@ -54,6 +55,7 @@ MODULE_PARM_DESC(init_nr_desc_per_channel,
|
|||||||
|
|
||||||
/* prototypes */
|
/* prototypes */
|
||||||
static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx);
|
static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx);
|
||||||
|
static void atc_issue_pending(struct dma_chan *chan);
|
||||||
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
@ -230,6 +232,95 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first)
|
|||||||
vdbg_dump_regs(atchan);
|
vdbg_dump_regs(atchan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* atc_get_current_descriptors -
|
||||||
|
* locate the descriptor which equal to physical address in DSCR
|
||||||
|
* @atchan: the channel we want to start
|
||||||
|
* @dscr_addr: physical descriptor address in DSCR
|
||||||
|
*/
|
||||||
|
static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan,
|
||||||
|
u32 dscr_addr)
|
||||||
|
{
|
||||||
|
struct at_desc *desc, *_desc, *child, *desc_cur = NULL;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) {
|
||||||
|
if (desc->lli.dscr == dscr_addr) {
|
||||||
|
desc_cur = desc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(child, &desc->tx_list, desc_node) {
|
||||||
|
if (child->lli.dscr == dscr_addr) {
|
||||||
|
desc_cur = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc_cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* atc_get_bytes_left -
|
||||||
|
* Get the number of bytes residue in dma buffer,
|
||||||
|
* @chan: the channel we want to start
|
||||||
|
*/
|
||||||
|
static int atc_get_bytes_left(struct dma_chan *chan)
|
||||||
|
{
|
||||||
|
struct at_dma_chan *atchan = to_at_dma_chan(chan);
|
||||||
|
struct at_dma *atdma = to_at_dma(chan->device);
|
||||||
|
int chan_id = atchan->chan_common.chan_id;
|
||||||
|
struct at_desc *desc_first = atc_first_active(atchan);
|
||||||
|
struct at_desc *desc_cur;
|
||||||
|
int ret = 0, count = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize necessary values in the first time.
|
||||||
|
* remain_desc record remain desc length.
|
||||||
|
*/
|
||||||
|
if (atchan->remain_desc == 0)
|
||||||
|
/* First descriptor embedds the transaction length */
|
||||||
|
atchan->remain_desc = desc_first->len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This happens when current descriptor transfer complete.
|
||||||
|
* The residual buffer size should reduce current descriptor length.
|
||||||
|
*/
|
||||||
|
if (unlikely(test_bit(ATC_IS_BTC, &atchan->status))) {
|
||||||
|
clear_bit(ATC_IS_BTC, &atchan->status);
|
||||||
|
desc_cur = atc_get_current_descriptors(atchan,
|
||||||
|
channel_readl(atchan, DSCR));
|
||||||
|
if (!desc_cur) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
atchan->remain_desc -= (desc_cur->lli.ctrla & ATC_BTSIZE_MAX)
|
||||||
|
<< (desc_first->tx_width);
|
||||||
|
if (atchan->remain_desc < 0) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
ret = atchan->remain_desc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Get residual bytes when current
|
||||||
|
* descriptor transfer in progress.
|
||||||
|
*/
|
||||||
|
count = (channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX)
|
||||||
|
<< (desc_first->tx_width);
|
||||||
|
ret = atchan->remain_desc - count;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Check fifo empty.
|
||||||
|
*/
|
||||||
|
if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id)))
|
||||||
|
atc_issue_pending(chan);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* atc_chain_complete - finish work for one transaction chain
|
* atc_chain_complete - finish work for one transaction chain
|
||||||
* @atchan: channel we work on
|
* @atchan: channel we work on
|
||||||
@ -326,37 +417,6 @@ static void atc_complete_all(struct at_dma_chan *atchan)
|
|||||||
atc_chain_complete(atchan, desc);
|
atc_chain_complete(atchan, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* atc_cleanup_descriptors - cleanup up finished descriptors in active_list
|
|
||||||
* @atchan: channel to be cleaned up
|
|
||||||
*
|
|
||||||
* Called with atchan->lock held and bh disabled
|
|
||||||
*/
|
|
||||||
static void atc_cleanup_descriptors(struct at_dma_chan *atchan)
|
|
||||||
{
|
|
||||||
struct at_desc *desc, *_desc;
|
|
||||||
struct at_desc *child;
|
|
||||||
|
|
||||||
dev_vdbg(chan2dev(&atchan->chan_common), "cleanup descriptors\n");
|
|
||||||
|
|
||||||
list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) {
|
|
||||||
if (!(desc->lli.ctrla & ATC_DONE))
|
|
||||||
/* This one is currently in progress */
|
|
||||||
return;
|
|
||||||
|
|
||||||
list_for_each_entry(child, &desc->tx_list, desc_node)
|
|
||||||
if (!(child->lli.ctrla & ATC_DONE))
|
|
||||||
/* Currently in progress */
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* No descriptors so far seem to be in progress, i.e.
|
|
||||||
* this chain must be done.
|
|
||||||
*/
|
|
||||||
atc_chain_complete(atchan, desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* atc_advance_work - at the end of a transaction, move forward
|
* atc_advance_work - at the end of a transaction, move forward
|
||||||
* @atchan: channel where the transaction ended
|
* @atchan: channel where the transaction ended
|
||||||
@ -496,6 +556,8 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
|
|||||||
/* Give information to tasklet */
|
/* Give information to tasklet */
|
||||||
set_bit(ATC_IS_ERROR, &atchan->status);
|
set_bit(ATC_IS_ERROR, &atchan->status);
|
||||||
}
|
}
|
||||||
|
if (pending & AT_DMA_BTC(i))
|
||||||
|
set_bit(ATC_IS_BTC, &atchan->status);
|
||||||
tasklet_schedule(&atchan->tasklet);
|
tasklet_schedule(&atchan->tasklet);
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
@ -615,6 +677,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
|||||||
/* First descriptor of the chain embedds additional information */
|
/* First descriptor of the chain embedds additional information */
|
||||||
first->txd.cookie = -EBUSY;
|
first->txd.cookie = -EBUSY;
|
||||||
first->len = len;
|
first->len = len;
|
||||||
|
first->tx_width = src_width;
|
||||||
|
|
||||||
/* set end-of-link to the last link descriptor of list*/
|
/* set end-of-link to the last link descriptor of list*/
|
||||||
set_desc_eol(desc);
|
set_desc_eol(desc);
|
||||||
@ -761,6 +824,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
|||||||
/* First descriptor of the chain embedds additional information */
|
/* First descriptor of the chain embedds additional information */
|
||||||
first->txd.cookie = -EBUSY;
|
first->txd.cookie = -EBUSY;
|
||||||
first->len = total_len;
|
first->len = total_len;
|
||||||
|
first->tx_width = reg_width;
|
||||||
|
|
||||||
/* first link descriptor of list is responsible of flags */
|
/* first link descriptor of list is responsible of flags */
|
||||||
first->txd.flags = flags; /* client is in control of this ack */
|
first->txd.flags = flags; /* client is in control of this ack */
|
||||||
@ -919,6 +983,7 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
|
|||||||
/* First descriptor of the chain embedds additional information */
|
/* First descriptor of the chain embedds additional information */
|
||||||
first->txd.cookie = -EBUSY;
|
first->txd.cookie = -EBUSY;
|
||||||
first->len = buf_len;
|
first->len = buf_len;
|
||||||
|
first->tx_width = reg_width;
|
||||||
|
|
||||||
return &first->txd;
|
return &first->txd;
|
||||||
|
|
||||||
@ -1032,34 +1097,36 @@ atc_tx_status(struct dma_chan *chan,
|
|||||||
struct dma_tx_state *txstate)
|
struct dma_tx_state *txstate)
|
||||||
{
|
{
|
||||||
struct at_dma_chan *atchan = to_at_dma_chan(chan);
|
struct at_dma_chan *atchan = to_at_dma_chan(chan);
|
||||||
dma_cookie_t last_used;
|
|
||||||
dma_cookie_t last_complete;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
enum dma_status ret;
|
enum dma_status ret;
|
||||||
|
int bytes = 0;
|
||||||
|
|
||||||
|
ret = dma_cookie_status(chan, cookie, txstate);
|
||||||
|
if (ret == DMA_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* There's no point calculating the residue if there's
|
||||||
|
* no txstate to store the value.
|
||||||
|
*/
|
||||||
|
if (!txstate)
|
||||||
|
return DMA_ERROR;
|
||||||
|
|
||||||
spin_lock_irqsave(&atchan->lock, flags);
|
spin_lock_irqsave(&atchan->lock, flags);
|
||||||
|
|
||||||
ret = dma_cookie_status(chan, cookie, txstate);
|
/* Get number of bytes left in the active transactions */
|
||||||
if (ret != DMA_SUCCESS) {
|
bytes = atc_get_bytes_left(chan);
|
||||||
atc_cleanup_descriptors(atchan);
|
|
||||||
|
|
||||||
ret = dma_cookie_status(chan, cookie, txstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
last_complete = chan->completed_cookie;
|
|
||||||
last_used = chan->cookie;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&atchan->lock, flags);
|
spin_unlock_irqrestore(&atchan->lock, flags);
|
||||||
|
|
||||||
if (ret != DMA_SUCCESS)
|
if (unlikely(bytes < 0)) {
|
||||||
dma_set_residue(txstate, atc_first_active(atchan)->len);
|
dev_vdbg(chan2dev(chan), "get residual bytes error\n");
|
||||||
|
return DMA_ERROR;
|
||||||
|
} else {
|
||||||
|
dma_set_residue(txstate, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
if (atc_chan_is_paused(atchan))
|
dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d residue = %d\n",
|
||||||
ret = DMA_PAUSED;
|
ret, cookie, bytes);
|
||||||
|
|
||||||
dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n",
|
|
||||||
ret, cookie, last_complete ? last_complete : 0,
|
|
||||||
last_used ? last_used : 0);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1120,7 +1187,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
|
|||||||
*/
|
*/
|
||||||
BUG_ON(!atslave->dma_dev || atslave->dma_dev != atdma->dma_common.dev);
|
BUG_ON(!atslave->dma_dev || atslave->dma_dev != atdma->dma_common.dev);
|
||||||
|
|
||||||
/* if cfg configuration specified take it instad of default */
|
/* if cfg configuration specified take it instead of default */
|
||||||
if (atslave->cfg)
|
if (atslave->cfg)
|
||||||
cfg = atslave->cfg;
|
cfg = atslave->cfg;
|
||||||
}
|
}
|
||||||
@ -1143,6 +1210,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
|
|||||||
|
|
||||||
spin_lock_irqsave(&atchan->lock, flags);
|
spin_lock_irqsave(&atchan->lock, flags);
|
||||||
atchan->descs_allocated = i;
|
atchan->descs_allocated = i;
|
||||||
|
atchan->remain_desc = 0;
|
||||||
list_splice(&tmp_list, &atchan->free_list);
|
list_splice(&tmp_list, &atchan->free_list);
|
||||||
dma_cookie_init(chan);
|
dma_cookie_init(chan);
|
||||||
spin_unlock_irqrestore(&atchan->lock, flags);
|
spin_unlock_irqrestore(&atchan->lock, flags);
|
||||||
@ -1185,6 +1253,7 @@ static void atc_free_chan_resources(struct dma_chan *chan)
|
|||||||
list_splice_init(&atchan->free_list, &list);
|
list_splice_init(&atchan->free_list, &list);
|
||||||
atchan->descs_allocated = 0;
|
atchan->descs_allocated = 0;
|
||||||
atchan->status = 0;
|
atchan->status = 0;
|
||||||
|
atchan->remain_desc = 0;
|
||||||
|
|
||||||
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
|
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
|
||||||
}
|
}
|
||||||
@ -1223,14 +1292,31 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
|
|||||||
atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL);
|
atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL);
|
||||||
if (!atslave)
|
if (!atslave)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
atslave->cfg = ATC_DST_H2SEL_HW | ATC_SRC_H2SEL_HW;
|
||||||
/*
|
/*
|
||||||
* We can fill both SRC_PER and DST_PER, one of these fields will be
|
* We can fill both SRC_PER and DST_PER, one of these fields will be
|
||||||
* ignored depending on DMA transfer direction.
|
* ignored depending on DMA transfer direction.
|
||||||
*/
|
*/
|
||||||
per_id = dma_spec->args[1];
|
per_id = dma_spec->args[1] & AT91_DMA_CFG_PER_ID_MASK;
|
||||||
atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW
|
atslave->cfg |= ATC_DST_PER_MSB(per_id) | ATC_DST_PER(per_id)
|
||||||
| ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id)
|
| ATC_SRC_PER_MSB(per_id) | ATC_SRC_PER(per_id);
|
||||||
| ATC_SRC_PER(per_id);
|
/*
|
||||||
|
* We have to translate the value we get from the device tree since
|
||||||
|
* the half FIFO configuration value had to be 0 to keep backward
|
||||||
|
* compatibility.
|
||||||
|
*/
|
||||||
|
switch (dma_spec->args[1] & AT91_DMA_CFG_FIFOCFG_MASK) {
|
||||||
|
case AT91_DMA_CFG_FIFOCFG_ALAP:
|
||||||
|
atslave->cfg |= ATC_FIFOCFG_LARGESTBURST;
|
||||||
|
break;
|
||||||
|
case AT91_DMA_CFG_FIFOCFG_ASAP:
|
||||||
|
atslave->cfg |= ATC_FIFOCFG_ENOUGHSPACE;
|
||||||
|
break;
|
||||||
|
case AT91_DMA_CFG_FIFOCFG_HALF:
|
||||||
|
default:
|
||||||
|
atslave->cfg |= ATC_FIFOCFG_HALFFIFO;
|
||||||
|
}
|
||||||
atslave->dma_dev = &dmac_pdev->dev;
|
atslave->dma_dev = &dmac_pdev->dev;
|
||||||
|
|
||||||
chan = dma_request_channel(mask, at_dma_filter, atslave);
|
chan = dma_request_channel(mask, at_dma_filter, atslave);
|
||||||
@ -1374,7 +1460,9 @@ static int __init at_dma_probe(struct platform_device *pdev)
|
|||||||
err = PTR_ERR(atdma->clk);
|
err = PTR_ERR(atdma->clk);
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
clk_enable(atdma->clk);
|
err = clk_prepare_enable(atdma->clk);
|
||||||
|
if (err)
|
||||||
|
goto err_clk_prepare;
|
||||||
|
|
||||||
/* force dma off, just in case */
|
/* force dma off, just in case */
|
||||||
at_dma_off(atdma);
|
at_dma_off(atdma);
|
||||||
@ -1472,10 +1560,10 @@ err_of_dma_controller_register:
|
|||||||
dma_async_device_unregister(&atdma->dma_common);
|
dma_async_device_unregister(&atdma->dma_common);
|
||||||
dma_pool_destroy(atdma->dma_desc_pool);
|
dma_pool_destroy(atdma->dma_desc_pool);
|
||||||
err_pool_create:
|
err_pool_create:
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
free_irq(platform_get_irq(pdev, 0), atdma);
|
free_irq(platform_get_irq(pdev, 0), atdma);
|
||||||
err_irq:
|
err_irq:
|
||||||
clk_disable(atdma->clk);
|
clk_disable_unprepare(atdma->clk);
|
||||||
|
err_clk_prepare:
|
||||||
clk_put(atdma->clk);
|
clk_put(atdma->clk);
|
||||||
err_clk:
|
err_clk:
|
||||||
iounmap(atdma->regs);
|
iounmap(atdma->regs);
|
||||||
@ -1497,7 +1585,6 @@ static int at_dma_remove(struct platform_device *pdev)
|
|||||||
dma_async_device_unregister(&atdma->dma_common);
|
dma_async_device_unregister(&atdma->dma_common);
|
||||||
|
|
||||||
dma_pool_destroy(atdma->dma_desc_pool);
|
dma_pool_destroy(atdma->dma_desc_pool);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
free_irq(platform_get_irq(pdev, 0), atdma);
|
free_irq(platform_get_irq(pdev, 0), atdma);
|
||||||
|
|
||||||
list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
|
list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
|
||||||
@ -1512,7 +1599,7 @@ static int at_dma_remove(struct platform_device *pdev)
|
|||||||
list_del(&chan->device_node);
|
list_del(&chan->device_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_disable(atdma->clk);
|
clk_disable_unprepare(atdma->clk);
|
||||||
clk_put(atdma->clk);
|
clk_put(atdma->clk);
|
||||||
|
|
||||||
iounmap(atdma->regs);
|
iounmap(atdma->regs);
|
||||||
@ -1531,7 +1618,7 @@ static void at_dma_shutdown(struct platform_device *pdev)
|
|||||||
struct at_dma *atdma = platform_get_drvdata(pdev);
|
struct at_dma *atdma = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
at_dma_off(platform_get_drvdata(pdev));
|
at_dma_off(platform_get_drvdata(pdev));
|
||||||
clk_disable(atdma->clk);
|
clk_disable_unprepare(atdma->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int at_dma_prepare(struct device *dev)
|
static int at_dma_prepare(struct device *dev)
|
||||||
@ -1588,7 +1675,7 @@ static int at_dma_suspend_noirq(struct device *dev)
|
|||||||
|
|
||||||
/* disable DMA controller */
|
/* disable DMA controller */
|
||||||
at_dma_off(atdma);
|
at_dma_off(atdma);
|
||||||
clk_disable(atdma->clk);
|
clk_disable_unprepare(atdma->clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,7 +1705,7 @@ static int at_dma_resume_noirq(struct device *dev)
|
|||||||
struct dma_chan *chan, *_chan;
|
struct dma_chan *chan, *_chan;
|
||||||
|
|
||||||
/* bring back DMA controller */
|
/* bring back DMA controller */
|
||||||
clk_enable(atdma->clk);
|
clk_prepare_enable(atdma->clk);
|
||||||
dma_writel(atdma, EN, AT_DMA_ENABLE);
|
dma_writel(atdma, EN, AT_DMA_ENABLE);
|
||||||
|
|
||||||
/* clear any pending interrupt */
|
/* clear any pending interrupt */
|
||||||
|
@ -182,6 +182,7 @@ struct at_lli {
|
|||||||
* @txd: support for the async_tx api
|
* @txd: support for the async_tx api
|
||||||
* @desc_node: node on the channed descriptors list
|
* @desc_node: node on the channed descriptors list
|
||||||
* @len: total transaction bytecount
|
* @len: total transaction bytecount
|
||||||
|
* @tx_width: transfer width
|
||||||
*/
|
*/
|
||||||
struct at_desc {
|
struct at_desc {
|
||||||
/* FIRST values the hardware uses */
|
/* FIRST values the hardware uses */
|
||||||
@ -192,6 +193,7 @@ struct at_desc {
|
|||||||
struct dma_async_tx_descriptor txd;
|
struct dma_async_tx_descriptor txd;
|
||||||
struct list_head desc_node;
|
struct list_head desc_node;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
u32 tx_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct at_desc *
|
static inline struct at_desc *
|
||||||
@ -211,6 +213,7 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd)
|
|||||||
enum atc_status {
|
enum atc_status {
|
||||||
ATC_IS_ERROR = 0,
|
ATC_IS_ERROR = 0,
|
||||||
ATC_IS_PAUSED = 1,
|
ATC_IS_PAUSED = 1,
|
||||||
|
ATC_IS_BTC = 2,
|
||||||
ATC_IS_CYCLIC = 24,
|
ATC_IS_CYCLIC = 24,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -228,6 +231,7 @@ enum atc_status {
|
|||||||
* @save_cfg: configuration register that is saved on suspend/resume cycle
|
* @save_cfg: configuration register that is saved on suspend/resume cycle
|
||||||
* @save_dscr: for cyclic operations, preserve next descriptor address in
|
* @save_dscr: for cyclic operations, preserve next descriptor address in
|
||||||
* the cyclic list on suspend/resume cycle
|
* the cyclic list on suspend/resume cycle
|
||||||
|
* @remain_desc: to save remain desc length
|
||||||
* @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG
|
* @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG
|
||||||
* @lock: serializes enqueue/dequeue operations to descriptors lists
|
* @lock: serializes enqueue/dequeue operations to descriptors lists
|
||||||
* @active_list: list of descriptors dmaengine is being running on
|
* @active_list: list of descriptors dmaengine is being running on
|
||||||
@ -246,6 +250,7 @@ struct at_dma_chan {
|
|||||||
struct tasklet_struct tasklet;
|
struct tasklet_struct tasklet;
|
||||||
u32 save_cfg;
|
u32 save_cfg;
|
||||||
u32 save_dscr;
|
u32 save_dscr;
|
||||||
|
u32 remain_desc;
|
||||||
struct dma_slave_config dma_sconfig;
|
struct dma_slave_config dma_sconfig;
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
617
drivers/dma/dma-jz4740.c
Normal file
617
drivers/dma/dma-jz4740.c
Normal file
@ -0,0 +1,617 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de>
|
||||||
|
* JZ4740 DMAC support
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
|
#include <asm/mach-jz4740/dma.h>
|
||||||
|
|
||||||
|
#include "virt-dma.h"
|
||||||
|
|
||||||
|
#define JZ_DMA_NR_CHANS 6
|
||||||
|
|
||||||
|
#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20)
|
||||||
|
#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20)
|
||||||
|
#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20)
|
||||||
|
#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20)
|
||||||
|
#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20)
|
||||||
|
#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20)
|
||||||
|
#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20)
|
||||||
|
|
||||||
|
#define JZ_REG_DMA_CTRL 0x300
|
||||||
|
#define JZ_REG_DMA_IRQ 0x304
|
||||||
|
#define JZ_REG_DMA_DOORBELL 0x308
|
||||||
|
#define JZ_REG_DMA_DOORBELL_SET 0x30C
|
||||||
|
|
||||||
|
#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31)
|
||||||
|
#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6)
|
||||||
|
#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4)
|
||||||
|
#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3)
|
||||||
|
#define JZ_DMA_STATUS_CTRL_HALT BIT(2)
|
||||||
|
#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1)
|
||||||
|
#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0)
|
||||||
|
|
||||||
|
#define JZ_DMA_CMD_SRC_INC BIT(23)
|
||||||
|
#define JZ_DMA_CMD_DST_INC BIT(22)
|
||||||
|
#define JZ_DMA_CMD_RDIL_MASK (0xf << 16)
|
||||||
|
#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14)
|
||||||
|
#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12)
|
||||||
|
#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8)
|
||||||
|
#define JZ_DMA_CMD_BLOCK_MODE BIT(7)
|
||||||
|
#define JZ_DMA_CMD_DESC_VALID BIT(4)
|
||||||
|
#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3)
|
||||||
|
#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2)
|
||||||
|
#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1)
|
||||||
|
#define JZ_DMA_CMD_LINK_ENABLE BIT(0)
|
||||||
|
|
||||||
|
#define JZ_DMA_CMD_FLAGS_OFFSET 22
|
||||||
|
#define JZ_DMA_CMD_RDIL_OFFSET 16
|
||||||
|
#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14
|
||||||
|
#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12
|
||||||
|
#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8
|
||||||
|
#define JZ_DMA_CMD_MODE_OFFSET 7
|
||||||
|
|
||||||
|
#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8)
|
||||||
|
#define JZ_DMA_CTRL_HALT BIT(3)
|
||||||
|
#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2)
|
||||||
|
#define JZ_DMA_CTRL_ENABLE BIT(0)
|
||||||
|
|
||||||
|
enum jz4740_dma_width {
|
||||||
|
JZ4740_DMA_WIDTH_32BIT = 0,
|
||||||
|
JZ4740_DMA_WIDTH_8BIT = 1,
|
||||||
|
JZ4740_DMA_WIDTH_16BIT = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum jz4740_dma_transfer_size {
|
||||||
|
JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0,
|
||||||
|
JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1,
|
||||||
|
JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2,
|
||||||
|
JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3,
|
||||||
|
JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum jz4740_dma_flags {
|
||||||
|
JZ4740_DMA_SRC_AUTOINC = 0x2,
|
||||||
|
JZ4740_DMA_DST_AUTOINC = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum jz4740_dma_mode {
|
||||||
|
JZ4740_DMA_MODE_SINGLE = 0,
|
||||||
|
JZ4740_DMA_MODE_BLOCK = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jz4740_dma_sg {
|
||||||
|
dma_addr_t addr;
|
||||||
|
unsigned int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jz4740_dma_desc {
|
||||||
|
struct virt_dma_desc vdesc;
|
||||||
|
|
||||||
|
enum dma_transfer_direction direction;
|
||||||
|
bool cyclic;
|
||||||
|
|
||||||
|
unsigned int num_sgs;
|
||||||
|
struct jz4740_dma_sg sg[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jz4740_dmaengine_chan {
|
||||||
|
struct virt_dma_chan vchan;
|
||||||
|
unsigned int id;
|
||||||
|
|
||||||
|
dma_addr_t fifo_addr;
|
||||||
|
unsigned int transfer_shift;
|
||||||
|
|
||||||
|
struct jz4740_dma_desc *desc;
|
||||||
|
unsigned int next_sg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jz4740_dma_dev {
|
||||||
|
struct dma_device ddev;
|
||||||
|
void __iomem *base;
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
|
struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct jz4740_dma_dev *jz4740_dma_chan_get_dev(
|
||||||
|
struct jz4740_dmaengine_chan *chan)
|
||||||
|
{
|
||||||
|
return container_of(chan->vchan.chan.device, struct jz4740_dma_dev,
|
||||||
|
ddev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c)
|
||||||
|
{
|
||||||
|
return container_of(c, struct jz4740_dmaengine_chan, vchan.chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc)
|
||||||
|
{
|
||||||
|
return container_of(vdesc, struct jz4740_dma_desc, vdesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t jz4740_dma_read(struct jz4740_dma_dev *dmadev,
|
||||||
|
unsigned int reg)
|
||||||
|
{
|
||||||
|
return readl(dmadev->base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void jz4740_dma_write(struct jz4740_dma_dev *dmadev,
|
||||||
|
unsigned reg, uint32_t val)
|
||||||
|
{
|
||||||
|
writel(val, dmadev->base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void jz4740_dma_write_mask(struct jz4740_dma_dev *dmadev,
|
||||||
|
unsigned int reg, uint32_t val, uint32_t mask)
|
||||||
|
{
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = jz4740_dma_read(dmadev, reg);
|
||||||
|
tmp &= ~mask;
|
||||||
|
tmp |= val;
|
||||||
|
jz4740_dma_write(dmadev, reg, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs)
|
||||||
|
{
|
||||||
|
return kzalloc(sizeof(struct jz4740_dma_desc) +
|
||||||
|
sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width)
|
||||||
|
{
|
||||||
|
switch (width) {
|
||||||
|
case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
||||||
|
return JZ4740_DMA_WIDTH_8BIT;
|
||||||
|
case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
||||||
|
return JZ4740_DMA_WIDTH_16BIT;
|
||||||
|
case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
||||||
|
return JZ4740_DMA_WIDTH_32BIT;
|
||||||
|
default:
|
||||||
|
return JZ4740_DMA_WIDTH_32BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst)
|
||||||
|
{
|
||||||
|
if (maxburst <= 1)
|
||||||
|
return JZ4740_DMA_TRANSFER_SIZE_1BYTE;
|
||||||
|
else if (maxburst <= 3)
|
||||||
|
return JZ4740_DMA_TRANSFER_SIZE_2BYTE;
|
||||||
|
else if (maxburst <= 15)
|
||||||
|
return JZ4740_DMA_TRANSFER_SIZE_4BYTE;
|
||||||
|
else if (maxburst <= 31)
|
||||||
|
return JZ4740_DMA_TRANSFER_SIZE_16BYTE;
|
||||||
|
|
||||||
|
return JZ4740_DMA_TRANSFER_SIZE_32BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_dma_slave_config(struct dma_chan *c,
|
||||||
|
const struct dma_slave_config *config)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
|
||||||
|
struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
|
||||||
|
enum jz4740_dma_width src_width;
|
||||||
|
enum jz4740_dma_width dst_width;
|
||||||
|
enum jz4740_dma_transfer_size transfer_size;
|
||||||
|
enum jz4740_dma_flags flags;
|
||||||
|
uint32_t cmd;
|
||||||
|
|
||||||
|
switch (config->direction) {
|
||||||
|
case DMA_MEM_TO_DEV:
|
||||||
|
flags = JZ4740_DMA_SRC_AUTOINC;
|
||||||
|
transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
|
||||||
|
chan->fifo_addr = config->dst_addr;
|
||||||
|
break;
|
||||||
|
case DMA_DEV_TO_MEM:
|
||||||
|
flags = JZ4740_DMA_DST_AUTOINC;
|
||||||
|
transfer_size = jz4740_dma_maxburst(config->src_maxburst);
|
||||||
|
chan->fifo_addr = config->src_addr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
src_width = jz4740_dma_width(config->src_addr_width);
|
||||||
|
dst_width = jz4740_dma_width(config->dst_addr_width);
|
||||||
|
|
||||||
|
switch (transfer_size) {
|
||||||
|
case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
|
||||||
|
chan->transfer_shift = 1;
|
||||||
|
break;
|
||||||
|
case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
|
||||||
|
chan->transfer_shift = 2;
|
||||||
|
break;
|
||||||
|
case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
|
||||||
|
chan->transfer_shift = 4;
|
||||||
|
break;
|
||||||
|
case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
|
||||||
|
chan->transfer_shift = 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
chan->transfer_shift = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = flags << JZ_DMA_CMD_FLAGS_OFFSET;
|
||||||
|
cmd |= src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
|
||||||
|
cmd |= dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
|
||||||
|
cmd |= transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
|
||||||
|
cmd |= JZ4740_DMA_MODE_SINGLE << JZ_DMA_CMD_MODE_OFFSET;
|
||||||
|
cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
|
||||||
|
|
||||||
|
jz4740_dma_write(dmadev, JZ_REG_DMA_CMD(chan->id), cmd);
|
||||||
|
jz4740_dma_write(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0);
|
||||||
|
jz4740_dma_write(dmadev, JZ_REG_DMA_REQ_TYPE(chan->id),
|
||||||
|
config->slave_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_dma_terminate_all(struct dma_chan *c)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
|
||||||
|
struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
|
||||||
|
unsigned long flags;
|
||||||
|
LIST_HEAD(head);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||||
|
jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0,
|
||||||
|
JZ_DMA_STATUS_CTRL_ENABLE);
|
||||||
|
chan->desc = NULL;
|
||||||
|
vchan_get_all_descriptors(&chan->vchan, &head);
|
||||||
|
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||||
|
|
||||||
|
vchan_dma_desc_free_list(&chan->vchan, &head);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
struct dma_slave_config *config = (struct dma_slave_config *)arg;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case DMA_SLAVE_CONFIG:
|
||||||
|
return jz4740_dma_slave_config(chan, config);
|
||||||
|
case DMA_TERMINATE_ALL:
|
||||||
|
return jz4740_dma_terminate_all(chan);
|
||||||
|
default:
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan)
|
||||||
|
{
|
||||||
|
struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
|
||||||
|
dma_addr_t src_addr, dst_addr;
|
||||||
|
struct virt_dma_desc *vdesc;
|
||||||
|
struct jz4740_dma_sg *sg;
|
||||||
|
|
||||||
|
jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0,
|
||||||
|
JZ_DMA_STATUS_CTRL_ENABLE);
|
||||||
|
|
||||||
|
if (!chan->desc) {
|
||||||
|
vdesc = vchan_next_desc(&chan->vchan);
|
||||||
|
if (!vdesc)
|
||||||
|
return 0;
|
||||||
|
chan->desc = to_jz4740_dma_desc(vdesc);
|
||||||
|
chan->next_sg = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan->next_sg == chan->desc->num_sgs)
|
||||||
|
chan->next_sg = 0;
|
||||||
|
|
||||||
|
sg = &chan->desc->sg[chan->next_sg];
|
||||||
|
|
||||||
|
if (chan->desc->direction == DMA_MEM_TO_DEV) {
|
||||||
|
src_addr = sg->addr;
|
||||||
|
dst_addr = chan->fifo_addr;
|
||||||
|
} else {
|
||||||
|
src_addr = chan->fifo_addr;
|
||||||
|
dst_addr = sg->addr;
|
||||||
|
}
|
||||||
|
jz4740_dma_write(dmadev, JZ_REG_DMA_SRC_ADDR(chan->id), src_addr);
|
||||||
|
jz4740_dma_write(dmadev, JZ_REG_DMA_DST_ADDR(chan->id), dst_addr);
|
||||||
|
jz4740_dma_write(dmadev, JZ_REG_DMA_TRANSFER_COUNT(chan->id),
|
||||||
|
sg->len >> chan->transfer_shift);
|
||||||
|
|
||||||
|
chan->next_sg++;
|
||||||
|
|
||||||
|
jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id),
|
||||||
|
JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE,
|
||||||
|
JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC |
|
||||||
|
JZ_DMA_STATUS_CTRL_ENABLE);
|
||||||
|
|
||||||
|
jz4740_dma_write_mask(dmadev, JZ_REG_DMA_CTRL,
|
||||||
|
JZ_DMA_CTRL_ENABLE,
|
||||||
|
JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
vchan_cyclic_callback(&chan->desc->vdesc);
|
||||||
|
} else {
|
||||||
|
if (chan->next_sg == chan->desc->num_sgs) {
|
||||||
|
chan->desc = NULL;
|
||||||
|
vchan_cookie_complete(&chan->desc->vdesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jz4740_dma_start_transfer(chan);
|
||||||
|
spin_unlock(&chan->vchan.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t jz4740_dma_irq(int irq, void *devid)
|
||||||
|
{
|
||||||
|
struct jz4740_dma_dev *dmadev = devid;
|
||||||
|
uint32_t irq_status;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
irq_status = readl(dmadev->base + JZ_REG_DMA_IRQ);
|
||||||
|
|
||||||
|
for (i = 0; i < 6; ++i) {
|
||||||
|
if (irq_status & (1 << i)) {
|
||||||
|
jz4740_dma_write_mask(dmadev,
|
||||||
|
JZ_REG_DMA_STATUS_CTRL(i), 0,
|
||||||
|
JZ_DMA_STATUS_CTRL_ENABLE |
|
||||||
|
JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
|
||||||
|
|
||||||
|
jz4740_dma_chan_irq(&dmadev->chan[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jz4740_dma_issue_pending(struct dma_chan *c)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||||
|
if (vchan_issue_pending(&chan->vchan) && !chan->desc)
|
||||||
|
jz4740_dma_start_transfer(chan);
|
||||||
|
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg(
|
||||||
|
struct dma_chan *c, struct scatterlist *sgl,
|
||||||
|
unsigned int sg_len, enum dma_transfer_direction direction,
|
||||||
|
unsigned long flags, void *context)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
|
||||||
|
struct jz4740_dma_desc *desc;
|
||||||
|
struct scatterlist *sg;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
desc = jz4740_dma_alloc_desc(sg_len);
|
||||||
|
if (!desc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for_each_sg(sgl, sg, sg_len, i) {
|
||||||
|
desc->sg[i].addr = sg_dma_address(sg);
|
||||||
|
desc->sg[i].len = sg_dma_len(sg);
|
||||||
|
}
|
||||||
|
|
||||||
|
desc->num_sgs = sg_len;
|
||||||
|
desc->direction = direction;
|
||||||
|
desc->cyclic = false;
|
||||||
|
|
||||||
|
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic(
|
||||||
|
struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
|
||||||
|
size_t period_len, enum dma_transfer_direction direction,
|
||||||
|
unsigned long flags, void *context)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
|
||||||
|
struct jz4740_dma_desc *desc;
|
||||||
|
unsigned int num_periods, i;
|
||||||
|
|
||||||
|
if (buf_len % period_len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
num_periods = buf_len / period_len;
|
||||||
|
|
||||||
|
desc = jz4740_dma_alloc_desc(num_periods);
|
||||||
|
if (!desc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < num_periods; i++) {
|
||||||
|
desc->sg[i].addr = buf_addr;
|
||||||
|
desc->sg[i].len = period_len;
|
||||||
|
buf_addr += period_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc->num_sgs = num_periods;
|
||||||
|
desc->direction = direction;
|
||||||
|
desc->cyclic = true;
|
||||||
|
|
||||||
|
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan,
|
||||||
|
struct jz4740_dma_desc *desc, unsigned int next_sg)
|
||||||
|
{
|
||||||
|
struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
|
||||||
|
unsigned int residue, count;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
residue = 0;
|
||||||
|
|
||||||
|
for (i = next_sg; i < desc->num_sgs; i++)
|
||||||
|
residue += desc->sg[i].len;
|
||||||
|
|
||||||
|
if (next_sg != 0) {
|
||||||
|
count = jz4740_dma_read(dmadev,
|
||||||
|
JZ_REG_DMA_TRANSFER_COUNT(chan->id));
|
||||||
|
residue += count << chan->transfer_shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
return residue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
|
||||||
|
dma_cookie_t cookie, struct dma_tx_state *state)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
|
||||||
|
struct virt_dma_desc *vdesc;
|
||||||
|
enum dma_status status;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
status = dma_cookie_status(c, cookie, state);
|
||||||
|
if (status == DMA_SUCCESS || !state)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||||
|
vdesc = vchan_find_desc(&chan->vchan, cookie);
|
||||||
|
if (cookie == chan->desc->vdesc.tx.cookie) {
|
||||||
|
state->residue = jz4740_dma_desc_residue(chan, chan->desc,
|
||||||
|
chan->next_sg);
|
||||||
|
} else if (vdesc) {
|
||||||
|
state->residue = jz4740_dma_desc_residue(chan,
|
||||||
|
to_jz4740_dma_desc(vdesc), 0);
|
||||||
|
} else {
|
||||||
|
state->residue = 0;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc)
|
||||||
|
{
|
||||||
|
kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_dma_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jz4740_dmaengine_chan *chan;
|
||||||
|
struct jz4740_dma_dev *dmadev;
|
||||||
|
struct dma_device *dd;
|
||||||
|
unsigned int i;
|
||||||
|
struct resource *res;
|
||||||
|
int ret;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
|
||||||
|
if (!dmadev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dd = &dmadev->ddev;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
dmadev->base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(dmadev->base))
|
||||||
|
return PTR_ERR(dmadev->base);
|
||||||
|
|
||||||
|
dmadev->clk = clk_get(&pdev->dev, "dma");
|
||||||
|
if (IS_ERR(dmadev->clk))
|
||||||
|
return PTR_ERR(dmadev->clk);
|
||||||
|
|
||||||
|
clk_prepare_enable(dmadev->clk);
|
||||||
|
|
||||||
|
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;
|
||||||
|
dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg;
|
||||||
|
dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic;
|
||||||
|
dd->device_control = jz4740_dma_control;
|
||||||
|
dd->dev = &pdev->dev;
|
||||||
|
dd->chancnt = JZ_DMA_NR_CHANS;
|
||||||
|
INIT_LIST_HEAD(&dd->channels);
|
||||||
|
|
||||||
|
for (i = 0; i < dd->chancnt; i++) {
|
||||||
|
chan = &dmadev->chan[i];
|
||||||
|
chan->id = i;
|
||||||
|
chan->vchan.desc_free = jz4740_dma_desc_free;
|
||||||
|
vchan_init(&chan->vchan, dd);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dma_async_device_register(dd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev);
|
||||||
|
if (ret)
|
||||||
|
goto err_unregister;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, dmadev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_unregister:
|
||||||
|
dma_async_device_unregister(dd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_dma_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev);
|
||||||
|
int irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
|
free_irq(irq, dmadev);
|
||||||
|
dma_async_device_unregister(&dmadev->ddev);
|
||||||
|
clk_disable_unprepare(dmadev->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver jz4740_dma_driver = {
|
||||||
|
.probe = jz4740_dma_probe,
|
||||||
|
.remove = jz4740_dma_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "jz4740-dma",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(jz4740_dma_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||||
|
MODULE_DESCRIPTION("JZ4740 DMA driver");
|
||||||
|
MODULE_LICENSE("GPLv2");
|
29
drivers/dma/dw/Kconfig
Normal file
29
drivers/dma/dw/Kconfig
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#
|
||||||
|
# DMA engine configuration for dw
|
||||||
|
#
|
||||||
|
|
||||||
|
config DW_DMAC_CORE
|
||||||
|
tristate "Synopsys DesignWare AHB DMA support"
|
||||||
|
depends on GENERIC_HARDIRQS
|
||||||
|
select DMA_ENGINE
|
||||||
|
|
||||||
|
config DW_DMAC
|
||||||
|
tristate "Synopsys DesignWare AHB DMA platform driver"
|
||||||
|
select DW_DMAC_CORE
|
||||||
|
select DW_DMAC_BIG_ENDIAN_IO if AVR32
|
||||||
|
default y if CPU_AT32AP7000
|
||||||
|
help
|
||||||
|
Support the Synopsys DesignWare AHB DMA controller. This
|
||||||
|
can be integrated in chips such as the Atmel AT32ap7000.
|
||||||
|
|
||||||
|
config DW_DMAC_PCI
|
||||||
|
tristate "Synopsys DesignWare AHB DMA PCI driver"
|
||||||
|
depends on PCI
|
||||||
|
select DW_DMAC_CORE
|
||||||
|
help
|
||||||
|
Support the Synopsys DesignWare AHB DMA controller on the
|
||||||
|
platfroms that enumerate it as a PCI device. For example,
|
||||||
|
Intel Medfield has integrated this GPDMA controller.
|
||||||
|
|
||||||
|
config DW_DMAC_BIG_ENDIAN_IO
|
||||||
|
bool
|
8
drivers/dma/dw/Makefile
Normal file
8
drivers/dma/dw/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o
|
||||||
|
dw_dmac_core-objs := core.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_DW_DMAC) += dw_dmac.o
|
||||||
|
dw_dmac-objs := platform.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_DW_DMAC_PCI) += dw_dmac_pci.o
|
||||||
|
dw_dmac_pci-objs := pci.o
|
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2007-2008 Atmel Corporation
|
* Copyright (C) 2007-2008 Atmel Corporation
|
||||||
* Copyright (C) 2010-2011 ST Microelectronics
|
* Copyright (C) 2010-2011 ST Microelectronics
|
||||||
|
* Copyright (C) 2013 Intel Corporation
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
@ -19,17 +20,12 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_dma.h>
|
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/acpi.h>
|
|
||||||
#include <linux/acpi_dma.h>
|
|
||||||
|
|
||||||
#include "dw_dmac_regs.h"
|
#include "../dmaengine.h"
|
||||||
#include "dmaengine.h"
|
#include "internal.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This supports the Synopsys "DesignWare AHB Central DMA Controller",
|
* This supports the Synopsys "DesignWare AHB Central DMA Controller",
|
||||||
@ -41,16 +37,6 @@
|
|||||||
* which does not support descriptor writeback.
|
* which does not support descriptor writeback.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave)
|
|
||||||
{
|
|
||||||
return slave ? slave->dst_master : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
|
|
||||||
{
|
|
||||||
return slave ? slave->src_master : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void dwc_set_masters(struct dw_dma_chan *dwc)
|
static inline void dwc_set_masters(struct dw_dma_chan *dwc)
|
||||||
{
|
{
|
||||||
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
|
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
|
||||||
@ -556,14 +542,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
|
|||||||
|
|
||||||
/* --------------------- Cyclic DMA API extensions -------------------- */
|
/* --------------------- Cyclic DMA API extensions -------------------- */
|
||||||
|
|
||||||
inline dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan)
|
dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan)
|
||||||
{
|
{
|
||||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||||
return channel_readl(dwc, SAR);
|
return channel_readl(dwc, SAR);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dw_dma_get_src_addr);
|
EXPORT_SYMBOL(dw_dma_get_src_addr);
|
||||||
|
|
||||||
inline dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan)
|
dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan)
|
||||||
{
|
{
|
||||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||||
return channel_readl(dwc, DAR);
|
return channel_readl(dwc, DAR);
|
||||||
@ -1225,99 +1211,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
|
|||||||
dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
|
dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
struct dw_dma_of_filter_args {
|
|
||||||
struct dw_dma *dw;
|
|
||||||
unsigned int req;
|
|
||||||
unsigned int src;
|
|
||||||
unsigned int dst;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool dw_dma_of_filter(struct dma_chan *chan, void *param)
|
|
||||||
{
|
|
||||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
|
||||||
struct dw_dma_of_filter_args *fargs = param;
|
|
||||||
|
|
||||||
/* Ensure the device matches our channel */
|
|
||||||
if (chan->device != &fargs->dw->dma)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
dwc->request_line = fargs->req;
|
|
||||||
dwc->src_master = fargs->src;
|
|
||||||
dwc->dst_master = fargs->dst;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
|
|
||||||
struct of_dma *ofdma)
|
|
||||||
{
|
|
||||||
struct dw_dma *dw = ofdma->of_dma_data;
|
|
||||||
struct dw_dma_of_filter_args fargs = {
|
|
||||||
.dw = dw,
|
|
||||||
};
|
|
||||||
dma_cap_mask_t cap;
|
|
||||||
|
|
||||||
if (dma_spec->args_count != 3)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
fargs.req = dma_spec->args[0];
|
|
||||||
fargs.src = dma_spec->args[1];
|
|
||||||
fargs.dst = dma_spec->args[2];
|
|
||||||
|
|
||||||
if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS ||
|
|
||||||
fargs.src >= dw->nr_masters ||
|
|
||||||
fargs.dst >= dw->nr_masters))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dma_cap_zero(cap);
|
|
||||||
dma_cap_set(DMA_SLAVE, cap);
|
|
||||||
|
|
||||||
/* TODO: there should be a simpler way to do this */
|
|
||||||
return dma_request_channel(cap, dw_dma_of_filter, &fargs);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
|
|
||||||
{
|
|
||||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
|
||||||
struct acpi_dma_spec *dma_spec = param;
|
|
||||||
|
|
||||||
if (chan->device->dev != dma_spec->dev ||
|
|
||||||
chan->chan_id != dma_spec->chan_id)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
dwc->request_line = dma_spec->slave_id;
|
|
||||||
dwc->src_master = dwc_get_sms(NULL);
|
|
||||||
dwc->dst_master = dwc_get_dms(NULL);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dw_dma_acpi_controller_register(struct dw_dma *dw)
|
|
||||||
{
|
|
||||||
struct device *dev = dw->dma.dev;
|
|
||||||
struct acpi_dma_filter_info *info;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
|
||||||
if (!info)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dma_cap_zero(info->dma_cap);
|
|
||||||
dma_cap_set(DMA_SLAVE, info->dma_cap);
|
|
||||||
info->filter_fn = dw_dma_acpi_filter;
|
|
||||||
|
|
||||||
ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate,
|
|
||||||
info);
|
|
||||||
if (ret)
|
|
||||||
dev_err(dev, "could not register acpi_dma_controller\n");
|
|
||||||
}
|
|
||||||
#else /* !CONFIG_ACPI */
|
|
||||||
static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {}
|
|
||||||
#endif /* !CONFIG_ACPI */
|
|
||||||
|
|
||||||
/* --------------------- Cyclic DMA API extensions -------------------- */
|
/* --------------------- Cyclic DMA API extensions -------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1598,104 +1491,24 @@ static void dw_dma_off(struct dw_dma *dw)
|
|||||||
dw->chan[i].initialized = false;
|
dw->chan[i].initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
|
||||||
static struct dw_dma_platform_data *
|
|
||||||
dw_dma_parse_dt(struct platform_device *pdev)
|
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
struct dw_dma_platform_data *pdata;
|
|
||||||
u32 tmp, arr[4];
|
|
||||||
|
|
||||||
if (!np) {
|
|
||||||
dev_err(&pdev->dev, "Missing DT data\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
|
||||||
if (!pdata)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (of_property_read_bool(np, "is_private"))
|
|
||||||
pdata->is_private = true;
|
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "chan_allocation_order", &tmp))
|
|
||||||
pdata->chan_allocation_order = (unsigned char)tmp;
|
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "chan_priority", &tmp))
|
|
||||||
pdata->chan_priority = tmp;
|
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "block_size", &tmp))
|
|
||||||
pdata->block_size = tmp;
|
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "dma-masters", &tmp)) {
|
|
||||||
if (tmp > 4)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
pdata->nr_masters = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!of_property_read_u32_array(np, "data_width", arr,
|
|
||||||
pdata->nr_masters))
|
|
||||||
for (tmp = 0; tmp < pdata->nr_masters; tmp++)
|
|
||||||
pdata->data_width[tmp] = arr[tmp];
|
|
||||||
|
|
||||||
return pdata;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static inline struct dw_dma_platform_data *
|
|
||||||
dw_dma_parse_dt(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int dw_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct dw_dma_platform_data *pdata;
|
|
||||||
struct resource *io;
|
|
||||||
struct dw_dma *dw;
|
struct dw_dma *dw;
|
||||||
size_t size;
|
size_t size;
|
||||||
void __iomem *regs;
|
|
||||||
bool autocfg;
|
bool autocfg;
|
||||||
unsigned int dw_params;
|
unsigned int dw_params;
|
||||||
unsigned int nr_channels;
|
unsigned int nr_channels;
|
||||||
unsigned int max_blk_size = 0;
|
unsigned int max_blk_size = 0;
|
||||||
int irq;
|
|
||||||
int err;
|
int err;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
dw_params = dma_read_byaddr(chip->regs, DW_PARAMS);
|
||||||
if (!io)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
|
||||||
if (irq < 0)
|
|
||||||
return irq;
|
|
||||||
|
|
||||||
regs = devm_ioremap_resource(&pdev->dev, io);
|
|
||||||
if (IS_ERR(regs))
|
|
||||||
return PTR_ERR(regs);
|
|
||||||
|
|
||||||
/* Apply default dma_mask if needed */
|
|
||||||
if (!pdev->dev.dma_mask) {
|
|
||||||
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
|
||||||
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
dw_params = dma_read_byaddr(regs, DW_PARAMS);
|
|
||||||
autocfg = dw_params >> DW_PARAMS_EN & 0x1;
|
autocfg = dw_params >> DW_PARAMS_EN & 0x1;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params);
|
dev_dbg(chip->dev, "DW_PARAMS: 0x%08x\n", dw_params);
|
||||||
|
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
|
||||||
if (!pdata)
|
|
||||||
pdata = dw_dma_parse_dt(pdev);
|
|
||||||
|
|
||||||
if (!pdata && autocfg) {
|
if (!pdata && autocfg) {
|
||||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -1712,16 +1525,17 @@ static int dw_probe(struct platform_device *pdev)
|
|||||||
nr_channels = pdata->nr_channels;
|
nr_channels = pdata->nr_channels;
|
||||||
|
|
||||||
size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan);
|
size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan);
|
||||||
dw = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
|
dw = devm_kzalloc(chip->dev, size, GFP_KERNEL);
|
||||||
if (!dw)
|
if (!dw)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
dw->clk = devm_clk_get(&pdev->dev, "hclk");
|
dw->clk = devm_clk_get(chip->dev, "hclk");
|
||||||
if (IS_ERR(dw->clk))
|
if (IS_ERR(dw->clk))
|
||||||
return PTR_ERR(dw->clk);
|
return PTR_ERR(dw->clk);
|
||||||
clk_prepare_enable(dw->clk);
|
clk_prepare_enable(dw->clk);
|
||||||
|
|
||||||
dw->regs = regs;
|
dw->regs = chip->regs;
|
||||||
|
chip->dw = dw;
|
||||||
|
|
||||||
/* Get hardware configuration parameters */
|
/* Get hardware configuration parameters */
|
||||||
if (autocfg) {
|
if (autocfg) {
|
||||||
@ -1746,18 +1560,16 @@ static int dw_probe(struct platform_device *pdev)
|
|||||||
/* Disable BLOCK interrupts as well */
|
/* Disable BLOCK interrupts as well */
|
||||||
channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
|
channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
|
||||||
|
|
||||||
err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0,
|
err = devm_request_irq(chip->dev, chip->irq, dw_dma_interrupt, 0,
|
||||||
"dw_dmac", dw);
|
"dw_dmac", dw);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, dw);
|
|
||||||
|
|
||||||
/* Create a pool of consistent memory blocks for hardware descriptors */
|
/* Create a pool of consistent memory blocks for hardware descriptors */
|
||||||
dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev,
|
dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", chip->dev,
|
||||||
sizeof(struct dw_desc), 4, 0);
|
sizeof(struct dw_desc), 4, 0);
|
||||||
if (!dw->desc_pool) {
|
if (!dw->desc_pool) {
|
||||||
dev_err(&pdev->dev, "No memory for descriptors dma pool\n");
|
dev_err(chip->dev, "No memory for descriptors dma pool\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1798,11 +1610,11 @@ static int dw_probe(struct platform_device *pdev)
|
|||||||
/* Hardware configuration */
|
/* Hardware configuration */
|
||||||
if (autocfg) {
|
if (autocfg) {
|
||||||
unsigned int dwc_params;
|
unsigned int dwc_params;
|
||||||
|
void __iomem *addr = chip->regs + r * sizeof(u32);
|
||||||
|
|
||||||
dwc_params = dma_read_byaddr(regs + r * sizeof(u32),
|
dwc_params = dma_read_byaddr(addr, DWC_PARAMS);
|
||||||
DWC_PARAMS);
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i,
|
dev_dbg(chip->dev, "DWC_PARAMS[%d]: 0x%08x\n", i,
|
||||||
dwc_params);
|
dwc_params);
|
||||||
|
|
||||||
/* Decode maximum block size for given channel. The
|
/* Decode maximum block size for given channel. The
|
||||||
@ -1834,7 +1646,7 @@ static int dw_probe(struct platform_device *pdev)
|
|||||||
dma_cap_set(DMA_SLAVE, dw->dma.cap_mask);
|
dma_cap_set(DMA_SLAVE, dw->dma.cap_mask);
|
||||||
if (pdata->is_private)
|
if (pdata->is_private)
|
||||||
dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
|
dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
|
||||||
dw->dma.dev = &pdev->dev;
|
dw->dma.dev = chip->dev;
|
||||||
dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources;
|
dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources;
|
||||||
dw->dma.device_free_chan_resources = dwc_free_chan_resources;
|
dw->dma.device_free_chan_resources = dwc_free_chan_resources;
|
||||||
|
|
||||||
@ -1848,32 +1660,20 @@ static int dw_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
dma_writel(dw, CFG, DW_CFG_DMA_EN);
|
dma_writel(dw, CFG, DW_CFG_DMA_EN);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n",
|
dev_info(chip->dev, "DesignWare DMA Controller, %d channels\n",
|
||||||
nr_channels);
|
nr_channels);
|
||||||
|
|
||||||
dma_async_device_register(&dw->dma);
|
dma_async_device_register(&dw->dma);
|
||||||
|
|
||||||
if (pdev->dev.of_node) {
|
|
||||||
err = of_dma_controller_register(pdev->dev.of_node,
|
|
||||||
dw_dma_of_xlate, dw);
|
|
||||||
if (err)
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"could not register of_dma_controller\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ACPI_HANDLE(&pdev->dev))
|
|
||||||
dw_dma_acpi_controller_register(dw);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dw_dma_probe);
|
||||||
|
|
||||||
static int dw_remove(struct platform_device *pdev)
|
int dw_dma_remove(struct dw_dma_chip *chip)
|
||||||
{
|
{
|
||||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
struct dw_dma *dw = chip->dw;
|
||||||
struct dw_dma_chan *dwc, *_dwc;
|
struct dw_dma_chan *dwc, *_dwc;
|
||||||
|
|
||||||
if (pdev->dev.of_node)
|
|
||||||
of_dma_controller_free(pdev->dev.of_node);
|
|
||||||
dw_dma_off(dw);
|
dw_dma_off(dw);
|
||||||
dma_async_device_unregister(&dw->dma);
|
dma_async_device_unregister(&dw->dma);
|
||||||
|
|
||||||
@ -1887,86 +1687,44 @@ static int dw_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dw_dma_remove);
|
||||||
|
|
||||||
static void dw_shutdown(struct platform_device *pdev)
|
void dw_dma_shutdown(struct dw_dma_chip *chip)
|
||||||
{
|
{
|
||||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
struct dw_dma *dw = chip->dw;
|
||||||
|
|
||||||
dw_dma_off(dw);
|
dw_dma_off(dw);
|
||||||
clk_disable_unprepare(dw->clk);
|
clk_disable_unprepare(dw->clk);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dw_dma_shutdown);
|
||||||
|
|
||||||
static int dw_suspend_noirq(struct device *dev)
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|
||||||
|
int dw_dma_suspend(struct dw_dma_chip *chip)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct dw_dma *dw = chip->dw;
|
||||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
dw_dma_off(dw);
|
dw_dma_off(dw);
|
||||||
clk_disable_unprepare(dw->clk);
|
clk_disable_unprepare(dw->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dw_dma_suspend);
|
||||||
|
|
||||||
static int dw_resume_noirq(struct device *dev)
|
int dw_dma_resume(struct dw_dma_chip *chip)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct dw_dma *dw = chip->dw;
|
||||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
clk_prepare_enable(dw->clk);
|
clk_prepare_enable(dw->clk);
|
||||||
dma_writel(dw, CFG, DW_CFG_DMA_EN);
|
dma_writel(dw, CFG, DW_CFG_DMA_EN);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dw_dma_resume);
|
||||||
|
|
||||||
static const struct dev_pm_ops dw_dev_pm_ops = {
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
.suspend_noirq = dw_suspend_noirq,
|
|
||||||
.resume_noirq = dw_resume_noirq,
|
|
||||||
.freeze_noirq = dw_suspend_noirq,
|
|
||||||
.thaw_noirq = dw_resume_noirq,
|
|
||||||
.restore_noirq = dw_resume_noirq,
|
|
||||||
.poweroff_noirq = dw_suspend_noirq,
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static const struct of_device_id dw_dma_of_id_table[] = {
|
|
||||||
{ .compatible = "snps,dma-spear1340" },
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
static const struct acpi_device_id dw_dma_acpi_id_table[] = {
|
|
||||||
{ "INTL9C60", 0 },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver dw_driver = {
|
|
||||||
.probe = dw_probe,
|
|
||||||
.remove = dw_remove,
|
|
||||||
.shutdown = dw_shutdown,
|
|
||||||
.driver = {
|
|
||||||
.name = "dw_dmac",
|
|
||||||
.pm = &dw_dev_pm_ops,
|
|
||||||
.of_match_table = of_match_ptr(dw_dma_of_id_table),
|
|
||||||
.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init dw_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_register(&dw_driver);
|
|
||||||
}
|
|
||||||
subsys_initcall(dw_init);
|
|
||||||
|
|
||||||
static void __exit dw_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&dw_driver);
|
|
||||||
}
|
|
||||||
module_exit(dw_exit);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver");
|
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver");
|
||||||
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
|
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
|
||||||
MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
|
MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
|
70
drivers/dma/dw/internal.h
Normal file
70
drivers/dma/dw/internal.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Driver for the Synopsys DesignWare DMA Controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Intel Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DW_DMAC_INTERNAL_H
|
||||||
|
#define _DW_DMAC_INTERNAL_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/dw_dmac.h>
|
||||||
|
|
||||||
|
#include "regs.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct dw_dma_chip - representation of DesignWare DMA controller hardware
|
||||||
|
* @dev: struct device of the DMA controller
|
||||||
|
* @irq: irq line
|
||||||
|
* @regs: memory mapped I/O space
|
||||||
|
* @dw: struct dw_dma that is filed by dw_dma_probe()
|
||||||
|
*/
|
||||||
|
struct dw_dma_chip {
|
||||||
|
struct device *dev;
|
||||||
|
int irq;
|
||||||
|
void __iomem *regs;
|
||||||
|
struct dw_dma *dw;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Export to the platform drivers */
|
||||||
|
int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata);
|
||||||
|
int dw_dma_remove(struct dw_dma_chip *chip);
|
||||||
|
|
||||||
|
void dw_dma_shutdown(struct dw_dma_chip *chip);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|
||||||
|
int dw_dma_suspend(struct dw_dma_chip *chip);
|
||||||
|
int dw_dma_resume(struct dw_dma_chip *chip);
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dwc_get_dms - get destination master
|
||||||
|
* @slave: pointer to the custom slave configuration
|
||||||
|
*
|
||||||
|
* Returns destination master in the custom slave configuration if defined, or
|
||||||
|
* default value otherwise.
|
||||||
|
*/
|
||||||
|
static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave)
|
||||||
|
{
|
||||||
|
return slave ? slave->dst_master : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dwc_get_sms - get source master
|
||||||
|
* @slave: pointer to the custom slave configuration
|
||||||
|
*
|
||||||
|
* Returns source master in the custom slave configuration if defined, or
|
||||||
|
* default value otherwise.
|
||||||
|
*/
|
||||||
|
static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
|
||||||
|
{
|
||||||
|
return slave ? slave->src_master : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _DW_DMAC_INTERNAL_H */
|
101
drivers/dma/dw/pci.c
Normal file
101
drivers/dma/dw/pci.c
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* PCI driver for the Synopsys DesignWare DMA Controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Intel Corporation
|
||||||
|
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
static struct dw_dma_platform_data dw_pci_pdata = {
|
||||||
|
.is_private = 1,
|
||||||
|
.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
|
||||||
|
.chan_priority = CHAN_PRIORITY_ASCENDING,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
|
||||||
|
{
|
||||||
|
struct dw_dma_chip *chip;
|
||||||
|
struct dw_dma_platform_data *pdata = (void *)pid->driver_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pcim_enable_device(pdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "I/O memory remapping failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_set_master(pdev);
|
||||||
|
pci_try_set_mwi(pdev);
|
||||||
|
|
||||||
|
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
|
if (!chip)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
chip->dev = &pdev->dev;
|
||||||
|
chip->regs = pcim_iomap_table(pdev)[0];
|
||||||
|
chip->irq = pdev->irq;
|
||||||
|
|
||||||
|
ret = dw_dma_probe(chip, pdata);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pci_set_drvdata(pdev, chip);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dw_pci_remove(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct dw_dma_chip *chip = pci_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dw_dma_remove(chip);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFINE_PCI_DEVICE_TABLE(dw_pci_id_table) = {
|
||||||
|
/* Medfield */
|
||||||
|
{ PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_pdata },
|
||||||
|
{ PCI_VDEVICE(INTEL, 0x0830), (kernel_ulong_t)&dw_pci_pdata },
|
||||||
|
|
||||||
|
/* BayTrail */
|
||||||
|
{ PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_pdata },
|
||||||
|
{ PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_pdata },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(pci, dw_pci_id_table);
|
||||||
|
|
||||||
|
static struct pci_driver dw_pci_driver = {
|
||||||
|
.name = "dw_dmac_pci",
|
||||||
|
.id_table = dw_pci_id_table,
|
||||||
|
.probe = dw_pci_probe,
|
||||||
|
.remove = dw_pci_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_pci_driver(dw_pci_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller PCI driver");
|
||||||
|
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
317
drivers/dma/dw/platform.c
Normal file
317
drivers/dma/dw/platform.c
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
* Platform driver for the Synopsys DesignWare DMA Controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007-2008 Atmel Corporation
|
||||||
|
* Copyright (C) 2010-2011 ST Microelectronics
|
||||||
|
* Copyright (C) 2013 Intel Corporation
|
||||||
|
*
|
||||||
|
* Some parts of this driver are derived from the original dw_dmac.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_dma.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/acpi_dma.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
struct dw_dma_of_filter_args {
|
||||||
|
struct dw_dma *dw;
|
||||||
|
unsigned int req;
|
||||||
|
unsigned int src;
|
||||||
|
unsigned int dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool dw_dma_of_filter(struct dma_chan *chan, void *param)
|
||||||
|
{
|
||||||
|
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||||
|
struct dw_dma_of_filter_args *fargs = param;
|
||||||
|
|
||||||
|
/* Ensure the device matches our channel */
|
||||||
|
if (chan->device != &fargs->dw->dma)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dwc->request_line = fargs->req;
|
||||||
|
dwc->src_master = fargs->src;
|
||||||
|
dwc->dst_master = fargs->dst;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
|
||||||
|
struct of_dma *ofdma)
|
||||||
|
{
|
||||||
|
struct dw_dma *dw = ofdma->of_dma_data;
|
||||||
|
struct dw_dma_of_filter_args fargs = {
|
||||||
|
.dw = dw,
|
||||||
|
};
|
||||||
|
dma_cap_mask_t cap;
|
||||||
|
|
||||||
|
if (dma_spec->args_count != 3)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fargs.req = dma_spec->args[0];
|
||||||
|
fargs.src = dma_spec->args[1];
|
||||||
|
fargs.dst = dma_spec->args[2];
|
||||||
|
|
||||||
|
if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS ||
|
||||||
|
fargs.src >= dw->nr_masters ||
|
||||||
|
fargs.dst >= dw->nr_masters))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dma_cap_zero(cap);
|
||||||
|
dma_cap_set(DMA_SLAVE, cap);
|
||||||
|
|
||||||
|
/* TODO: there should be a simpler way to do this */
|
||||||
|
return dma_request_channel(cap, dw_dma_of_filter, &fargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
|
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
|
||||||
|
{
|
||||||
|
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||||
|
struct acpi_dma_spec *dma_spec = param;
|
||||||
|
|
||||||
|
if (chan->device->dev != dma_spec->dev ||
|
||||||
|
chan->chan_id != dma_spec->chan_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dwc->request_line = dma_spec->slave_id;
|
||||||
|
dwc->src_master = dwc_get_sms(NULL);
|
||||||
|
dwc->dst_master = dwc_get_dms(NULL);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dw_dma_acpi_controller_register(struct dw_dma *dw)
|
||||||
|
{
|
||||||
|
struct device *dev = dw->dma.dev;
|
||||||
|
struct acpi_dma_filter_info *info;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||||
|
if (!info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dma_cap_zero(info->dma_cap);
|
||||||
|
dma_cap_set(DMA_SLAVE, info->dma_cap);
|
||||||
|
info->filter_fn = dw_dma_acpi_filter;
|
||||||
|
|
||||||
|
ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate,
|
||||||
|
info);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "could not register acpi_dma_controller\n");
|
||||||
|
}
|
||||||
|
#else /* !CONFIG_ACPI */
|
||||||
|
static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {}
|
||||||
|
#endif /* !CONFIG_ACPI */
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static struct dw_dma_platform_data *
|
||||||
|
dw_dma_parse_dt(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct dw_dma_platform_data *pdata;
|
||||||
|
u32 tmp, arr[4];
|
||||||
|
|
||||||
|
if (!np) {
|
||||||
|
dev_err(&pdev->dev, "Missing DT data\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
|
if (!pdata)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (of_property_read_bool(np, "is_private"))
|
||||||
|
pdata->is_private = true;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "chan_allocation_order", &tmp))
|
||||||
|
pdata->chan_allocation_order = (unsigned char)tmp;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "chan_priority", &tmp))
|
||||||
|
pdata->chan_priority = tmp;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "block_size", &tmp))
|
||||||
|
pdata->block_size = tmp;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "dma-masters", &tmp)) {
|
||||||
|
if (tmp > 4)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pdata->nr_masters = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!of_property_read_u32_array(np, "data_width", arr,
|
||||||
|
pdata->nr_masters))
|
||||||
|
for (tmp = 0; tmp < pdata->nr_masters; tmp++)
|
||||||
|
pdata->data_width[tmp] = arr[tmp];
|
||||||
|
|
||||||
|
return pdata;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline struct dw_dma_platform_data *
|
||||||
|
dw_dma_parse_dt(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int dw_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct dw_dma_chip *chip;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct resource *mem;
|
||||||
|
struct dw_dma_platform_data *pdata;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||||
|
if (!chip)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
chip->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (chip->irq < 0)
|
||||||
|
return chip->irq;
|
||||||
|
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
chip->regs = devm_ioremap_resource(dev, mem);
|
||||||
|
if (IS_ERR(chip->regs))
|
||||||
|
return PTR_ERR(chip->regs);
|
||||||
|
|
||||||
|
/* Apply default dma_mask if needed */
|
||||||
|
if (!dev->dma_mask) {
|
||||||
|
dev->dma_mask = &dev->coherent_dma_mask;
|
||||||
|
dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata = dev_get_platdata(dev);
|
||||||
|
if (!pdata)
|
||||||
|
pdata = dw_dma_parse_dt(pdev);
|
||||||
|
|
||||||
|
chip->dev = dev;
|
||||||
|
|
||||||
|
err = dw_dma_probe(chip, pdata);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, chip);
|
||||||
|
|
||||||
|
if (pdev->dev.of_node) {
|
||||||
|
err = of_dma_controller_register(pdev->dev.of_node,
|
||||||
|
dw_dma_of_xlate, chip->dw);
|
||||||
|
if (err)
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"could not register of_dma_controller\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACPI_HANDLE(&pdev->dev))
|
||||||
|
dw_dma_acpi_controller_register(chip->dw);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (pdev->dev.of_node)
|
||||||
|
of_dma_controller_free(pdev->dev.of_node);
|
||||||
|
|
||||||
|
return dw_dma_remove(chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dw_shutdown(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
dw_dma_shutdown(chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id dw_dma_of_id_table[] = {
|
||||||
|
{ .compatible = "snps,dma-spear1340" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
|
static const struct acpi_device_id dw_dma_acpi_id_table[] = {
|
||||||
|
{ "INTL9C60", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|
||||||
|
static int dw_suspend_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
return dw_dma_suspend(chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_resume_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
return dw_dma_resume(chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
#define dw_suspend_noirq NULL
|
||||||
|
#define dw_resume_noirq NULL
|
||||||
|
|
||||||
|
#endif /* !CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static const struct dev_pm_ops dw_dev_pm_ops = {
|
||||||
|
.suspend_noirq = dw_suspend_noirq,
|
||||||
|
.resume_noirq = dw_resume_noirq,
|
||||||
|
.freeze_noirq = dw_suspend_noirq,
|
||||||
|
.thaw_noirq = dw_resume_noirq,
|
||||||
|
.restore_noirq = dw_resume_noirq,
|
||||||
|
.poweroff_noirq = dw_suspend_noirq,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver dw_driver = {
|
||||||
|
.probe = dw_probe,
|
||||||
|
.remove = dw_remove,
|
||||||
|
.shutdown = dw_shutdown,
|
||||||
|
.driver = {
|
||||||
|
.name = "dw_dmac",
|
||||||
|
.pm = &dw_dev_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(dw_dma_of_id_table),
|
||||||
|
.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dw_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&dw_driver);
|
||||||
|
}
|
||||||
|
subsys_initcall(dw_init);
|
||||||
|
|
||||||
|
static void __exit dw_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&dw_driver);
|
||||||
|
}
|
||||||
|
module_exit(dw_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");
|
@ -9,6 +9,7 @@
|
|||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dw_dmac.h>
|
#include <linux/dw_dmac.h>
|
||||||
|
|
||||||
@ -100,6 +101,12 @@ struct dw_dma_regs {
|
|||||||
u32 DW_PARAMS;
|
u32 DW_PARAMS;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Big endian I/O access when reading and writing to the DMA controller
|
||||||
|
* registers. This is needed on some platforms, like the Atmel AVR32
|
||||||
|
* architecture.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
|
#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
|
||||||
#define dma_readl_native ioread32be
|
#define dma_readl_native ioread32be
|
||||||
#define dma_writel_native iowrite32be
|
#define dma_writel_native iowrite32be
|
@ -1368,7 +1368,7 @@ static int fsldma_of_probe(struct platform_device *op)
|
|||||||
|
|
||||||
dma_set_mask(&(op->dev), DMA_BIT_MASK(36));
|
dma_set_mask(&(op->dev), DMA_BIT_MASK(36));
|
||||||
|
|
||||||
dev_set_drvdata(&op->dev, fdev);
|
platform_set_drvdata(op, fdev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We cannot use of_platform_bus_probe() because there is no
|
* We cannot use of_platform_bus_probe() because there is no
|
||||||
@ -1417,7 +1417,7 @@ static int fsldma_of_remove(struct platform_device *op)
|
|||||||
struct fsldma_device *fdev;
|
struct fsldma_device *fdev;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
fdev = dev_get_drvdata(&op->dev);
|
fdev = platform_get_drvdata(op);
|
||||||
dma_async_device_unregister(&fdev->common);
|
dma_async_device_unregister(&fdev->common);
|
||||||
|
|
||||||
fsldma_free_irqs(fdev);
|
fsldma_free_irqs(fdev);
|
||||||
@ -1428,7 +1428,6 @@ static int fsldma_of_remove(struct platform_device *op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
iounmap(fdev->regs);
|
iounmap(fdev->regs);
|
||||||
dev_set_drvdata(&op->dev, NULL);
|
|
||||||
kfree(fdev);
|
kfree(fdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_dma.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <linux/platform_data/dma-imx.h>
|
#include <linux/platform_data/dma-imx.h>
|
||||||
@ -186,6 +188,11 @@ struct imxdma_engine {
|
|||||||
enum imx_dma_type devtype;
|
enum imx_dma_type devtype;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct imxdma_filter_data {
|
||||||
|
struct imxdma_engine *imxdma;
|
||||||
|
int request;
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_device_id imx_dma_devtype[] = {
|
static struct platform_device_id imx_dma_devtype[] = {
|
||||||
{
|
{
|
||||||
.name = "imx1-dma",
|
.name = "imx1-dma",
|
||||||
@ -202,6 +209,22 @@ static struct platform_device_id imx_dma_devtype[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(platform, imx_dma_devtype);
|
MODULE_DEVICE_TABLE(platform, imx_dma_devtype);
|
||||||
|
|
||||||
|
static const struct of_device_id imx_dma_of_dev_id[] = {
|
||||||
|
{
|
||||||
|
.compatible = "fsl,imx1-dma",
|
||||||
|
.data = &imx_dma_devtype[IMX1_DMA],
|
||||||
|
}, {
|
||||||
|
.compatible = "fsl,imx21-dma",
|
||||||
|
.data = &imx_dma_devtype[IMX21_DMA],
|
||||||
|
}, {
|
||||||
|
.compatible = "fsl,imx27-dma",
|
||||||
|
.data = &imx_dma_devtype[IMX27_DMA],
|
||||||
|
}, {
|
||||||
|
/* sentinel */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, imx_dma_of_dev_id);
|
||||||
|
|
||||||
static inline int is_imx1_dma(struct imxdma_engine *imxdma)
|
static inline int is_imx1_dma(struct imxdma_engine *imxdma)
|
||||||
{
|
{
|
||||||
return imxdma->devtype == IMX1_DMA;
|
return imxdma->devtype == IMX1_DMA;
|
||||||
@ -996,17 +1019,55 @@ static void imxdma_issue_pending(struct dma_chan *chan)
|
|||||||
spin_unlock_irqrestore(&imxdma->lock, flags);
|
spin_unlock_irqrestore(&imxdma->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool imxdma_filter_fn(struct dma_chan *chan, void *param)
|
||||||
|
{
|
||||||
|
struct imxdma_filter_data *fdata = param;
|
||||||
|
struct imxdma_channel *imxdma_chan = to_imxdma_chan(chan);
|
||||||
|
|
||||||
|
if (chan->device->dev != fdata->imxdma->dev)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
imxdma_chan->dma_request = fdata->request;
|
||||||
|
chan->private = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dma_chan *imxdma_xlate(struct of_phandle_args *dma_spec,
|
||||||
|
struct of_dma *ofdma)
|
||||||
|
{
|
||||||
|
int count = dma_spec->args_count;
|
||||||
|
struct imxdma_engine *imxdma = ofdma->of_dma_data;
|
||||||
|
struct imxdma_filter_data fdata = {
|
||||||
|
.imxdma = imxdma,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (count != 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fdata.request = dma_spec->args[0];
|
||||||
|
|
||||||
|
return dma_request_channel(imxdma->dma_device.cap_mask,
|
||||||
|
imxdma_filter_fn, &fdata);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init imxdma_probe(struct platform_device *pdev)
|
static int __init imxdma_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct imxdma_engine *imxdma;
|
struct imxdma_engine *imxdma;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
const struct of_device_id *of_id;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
int irq, irq_err;
|
int irq, irq_err;
|
||||||
|
|
||||||
|
of_id = of_match_device(imx_dma_of_dev_id, &pdev->dev);
|
||||||
|
if (of_id)
|
||||||
|
pdev->id_entry = of_id->data;
|
||||||
|
|
||||||
imxdma = devm_kzalloc(&pdev->dev, sizeof(*imxdma), GFP_KERNEL);
|
imxdma = devm_kzalloc(&pdev->dev, sizeof(*imxdma), GFP_KERNEL);
|
||||||
if (!imxdma)
|
if (!imxdma)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
imxdma->dev = &pdev->dev;
|
||||||
imxdma->devtype = pdev->id_entry->driver_data;
|
imxdma->devtype = pdev->id_entry->driver_data;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
@ -1111,7 +1172,6 @@ static int __init imxdma_probe(struct platform_device *pdev)
|
|||||||
&imxdma->dma_device.channels);
|
&imxdma->dma_device.channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
imxdma->dev = &pdev->dev;
|
|
||||||
imxdma->dma_device.dev = &pdev->dev;
|
imxdma->dma_device.dev = &pdev->dev;
|
||||||
|
|
||||||
imxdma->dma_device.device_alloc_chan_resources = imxdma_alloc_chan_resources;
|
imxdma->dma_device.device_alloc_chan_resources = imxdma_alloc_chan_resources;
|
||||||
@ -1136,8 +1196,19 @@ static int __init imxdma_probe(struct platform_device *pdev)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pdev->dev.of_node) {
|
||||||
|
ret = of_dma_controller_register(pdev->dev.of_node,
|
||||||
|
imxdma_xlate, imxdma);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to register of_dma_controller\n");
|
||||||
|
goto err_of_dma_controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_of_dma_controller:
|
||||||
|
dma_async_device_unregister(&imxdma->dma_device);
|
||||||
err:
|
err:
|
||||||
clk_disable_unprepare(imxdma->dma_ipg);
|
clk_disable_unprepare(imxdma->dma_ipg);
|
||||||
clk_disable_unprepare(imxdma->dma_ahb);
|
clk_disable_unprepare(imxdma->dma_ahb);
|
||||||
@ -1150,6 +1221,9 @@ static int imxdma_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
dma_async_device_unregister(&imxdma->dma_device);
|
dma_async_device_unregister(&imxdma->dma_device);
|
||||||
|
|
||||||
|
if (pdev->dev.of_node)
|
||||||
|
of_dma_controller_free(pdev->dev.of_node);
|
||||||
|
|
||||||
clk_disable_unprepare(imxdma->dma_ipg);
|
clk_disable_unprepare(imxdma->dma_ipg);
|
||||||
clk_disable_unprepare(imxdma->dma_ahb);
|
clk_disable_unprepare(imxdma->dma_ahb);
|
||||||
|
|
||||||
@ -1159,6 +1233,7 @@ static int imxdma_remove(struct platform_device *pdev)
|
|||||||
static struct platform_driver imxdma_driver = {
|
static struct platform_driver imxdma_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "imx-dma",
|
.name = "imx-dma",
|
||||||
|
.of_match_table = imx_dma_of_dev_id,
|
||||||
},
|
},
|
||||||
.id_table = imx_dma_devtype,
|
.id_table = imx_dma_devtype,
|
||||||
.remove = imxdma_remove,
|
.remove = imxdma_remove,
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_dma.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <linux/platform_data/dma-imx-sdma.h>
|
#include <linux/platform_data/dma-imx-sdma.h>
|
||||||
@ -1296,6 +1297,35 @@ err_dma_alloc:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param)
|
||||||
|
{
|
||||||
|
struct imx_dma_data *data = fn_param;
|
||||||
|
|
||||||
|
if (!imx_dma_is_general_purpose(chan))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
chan->private = data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec,
|
||||||
|
struct of_dma *ofdma)
|
||||||
|
{
|
||||||
|
struct sdma_engine *sdma = ofdma->of_dma_data;
|
||||||
|
dma_cap_mask_t mask = sdma->dma_device.cap_mask;
|
||||||
|
struct imx_dma_data data;
|
||||||
|
|
||||||
|
if (dma_spec->args_count != 3)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data.dma_request = dma_spec->args[0];
|
||||||
|
data.peripheral_type = dma_spec->args[1];
|
||||||
|
data.priority = dma_spec->args[2];
|
||||||
|
|
||||||
|
return dma_request_channel(mask, sdma_filter_fn, &data);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init sdma_probe(struct platform_device *pdev)
|
static int __init sdma_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
const struct of_device_id *of_id =
|
const struct of_device_id *of_id =
|
||||||
@ -1443,10 +1473,20 @@ static int __init sdma_probe(struct platform_device *pdev)
|
|||||||
goto err_init;
|
goto err_init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (np) {
|
||||||
|
ret = of_dma_controller_register(np, sdma_xlate, sdma);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to register controller\n");
|
||||||
|
goto err_register;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dev_info(sdma->dev, "initialized\n");
|
dev_info(sdma->dev, "initialized\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_register:
|
||||||
|
dma_async_device_unregister(&sdma->dma_device);
|
||||||
err_init:
|
err_init:
|
||||||
kfree(sdma->script_addrs);
|
kfree(sdma->script_addrs);
|
||||||
err_alloc:
|
err_alloc:
|
||||||
|
@ -154,6 +154,10 @@ static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac)
|
|||||||
{
|
{
|
||||||
writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
|
writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
|
||||||
tdmac->reg_base + TDCR);
|
tdmac->reg_base + TDCR);
|
||||||
|
|
||||||
|
/* disable irq */
|
||||||
|
writel(0, tdmac->reg_base + TDIMR);
|
||||||
|
|
||||||
tdmac->status = DMA_SUCCESS;
|
tdmac->status = DMA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,7 +693,7 @@ static bool mxs_dma_filter_fn(struct dma_chan *chan, void *fn_param)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec,
|
static struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec,
|
||||||
struct of_dma *ofdma)
|
struct of_dma *ofdma)
|
||||||
{
|
{
|
||||||
struct mxs_dma_engine *mxs_dma = ofdma->of_dma_data;
|
struct mxs_dma_engine *mxs_dma = ofdma->of_dma_data;
|
||||||
|
@ -35,8 +35,7 @@ static struct of_dma *of_dma_find_controller(struct of_phandle_args *dma_spec)
|
|||||||
struct of_dma *ofdma;
|
struct of_dma *ofdma;
|
||||||
|
|
||||||
list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers)
|
list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers)
|
||||||
if ((ofdma->of_node == dma_spec->np) &&
|
if (ofdma->of_node == dma_spec->np)
|
||||||
(ofdma->of_dma_nbcells == dma_spec->args_count))
|
|
||||||
return ofdma;
|
return ofdma;
|
||||||
|
|
||||||
pr_debug("%s: can't find DMA controller %s\n", __func__,
|
pr_debug("%s: can't find DMA controller %s\n", __func__,
|
||||||
@ -64,8 +63,6 @@ int of_dma_controller_register(struct device_node *np,
|
|||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct of_dma *ofdma;
|
struct of_dma *ofdma;
|
||||||
int nbcells;
|
|
||||||
const __be32 *prop;
|
|
||||||
|
|
||||||
if (!np || !of_dma_xlate) {
|
if (!np || !of_dma_xlate) {
|
||||||
pr_err("%s: not enough information provided\n", __func__);
|
pr_err("%s: not enough information provided\n", __func__);
|
||||||
@ -76,19 +73,7 @@ int of_dma_controller_register(struct device_node *np,
|
|||||||
if (!ofdma)
|
if (!ofdma)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
prop = of_get_property(np, "#dma-cells", NULL);
|
|
||||||
if (prop)
|
|
||||||
nbcells = be32_to_cpup(prop);
|
|
||||||
|
|
||||||
if (!prop || !nbcells) {
|
|
||||||
pr_err("%s: #dma-cells property is missing or invalid\n",
|
|
||||||
__func__);
|
|
||||||
kfree(ofdma);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ofdma->of_node = np;
|
ofdma->of_node = np;
|
||||||
ofdma->of_dma_nbcells = nbcells;
|
|
||||||
ofdma->of_dma_xlate = of_dma_xlate;
|
ofdma->of_dma_xlate = of_dma_xlate;
|
||||||
ofdma->of_dma_data = data;
|
ofdma->of_dma_data = data;
|
||||||
|
|
||||||
|
@ -157,7 +157,6 @@ enum pl330_reqtype {
|
|||||||
#define PERIPH_REV_R0P0 0
|
#define PERIPH_REV_R0P0 0
|
||||||
#define PERIPH_REV_R1P0 1
|
#define PERIPH_REV_R1P0 1
|
||||||
#define PERIPH_REV_R1P1 2
|
#define PERIPH_REV_R1P1 2
|
||||||
#define PCELL_ID 0xff0
|
|
||||||
|
|
||||||
#define CR0_PERIPH_REQ_SET (1 << 0)
|
#define CR0_PERIPH_REQ_SET (1 << 0)
|
||||||
#define CR0_BOOT_EN_SET (1 << 1)
|
#define CR0_BOOT_EN_SET (1 << 1)
|
||||||
@ -193,8 +192,6 @@ enum pl330_reqtype {
|
|||||||
#define INTEG_CFG 0x0
|
#define INTEG_CFG 0x0
|
||||||
#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12))
|
#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12))
|
||||||
|
|
||||||
#define PCELL_ID_VAL 0xb105f00d
|
|
||||||
|
|
||||||
#define PL330_STATE_STOPPED (1 << 0)
|
#define PL330_STATE_STOPPED (1 << 0)
|
||||||
#define PL330_STATE_EXECUTING (1 << 1)
|
#define PL330_STATE_EXECUTING (1 << 1)
|
||||||
#define PL330_STATE_WFE (1 << 2)
|
#define PL330_STATE_WFE (1 << 2)
|
||||||
@ -292,7 +289,6 @@ static unsigned cmd_line;
|
|||||||
/* Populated by the PL330 core driver for DMA API driver's info */
|
/* Populated by the PL330 core driver for DMA API driver's info */
|
||||||
struct pl330_config {
|
struct pl330_config {
|
||||||
u32 periph_id;
|
u32 periph_id;
|
||||||
u32 pcell_id;
|
|
||||||
#define DMAC_MODE_NS (1 << 0)
|
#define DMAC_MODE_NS (1 << 0)
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
unsigned int data_bus_width:10; /* In number of bits */
|
unsigned int data_bus_width:10; /* In number of bits */
|
||||||
@ -505,7 +501,7 @@ struct pl330_dmac {
|
|||||||
/* Maximum possible events/irqs */
|
/* Maximum possible events/irqs */
|
||||||
int events[32];
|
int events[32];
|
||||||
/* BUS address of MicroCode buffer */
|
/* BUS address of MicroCode buffer */
|
||||||
u32 mcode_bus;
|
dma_addr_t mcode_bus;
|
||||||
/* CPU address of MicroCode buffer */
|
/* CPU address of MicroCode buffer */
|
||||||
void *mcode_cpu;
|
void *mcode_cpu;
|
||||||
/* List of all Channel threads */
|
/* List of all Channel threads */
|
||||||
@ -650,19 +646,6 @@ static inline bool _manager_ns(struct pl330_thread *thrd)
|
|||||||
return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false;
|
return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 get_id(struct pl330_info *pi, u32 off)
|
|
||||||
{
|
|
||||||
void __iomem *regs = pi->base;
|
|
||||||
u32 id = 0;
|
|
||||||
|
|
||||||
id |= (readb(regs + off + 0x0) << 0);
|
|
||||||
id |= (readb(regs + off + 0x4) << 8);
|
|
||||||
id |= (readb(regs + off + 0x8) << 16);
|
|
||||||
id |= (readb(regs + off + 0xc) << 24);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 get_revision(u32 periph_id)
|
static inline u32 get_revision(u32 periph_id)
|
||||||
{
|
{
|
||||||
return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK;
|
return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK;
|
||||||
@ -1986,9 +1969,6 @@ static void read_dmac_config(struct pl330_info *pi)
|
|||||||
pi->pcfg.num_events = val;
|
pi->pcfg.num_events = val;
|
||||||
|
|
||||||
pi->pcfg.irq_ns = readl(regs + CR3);
|
pi->pcfg.irq_ns = readl(regs + CR3);
|
||||||
|
|
||||||
pi->pcfg.periph_id = get_id(pi, PERIPH_ID);
|
|
||||||
pi->pcfg.pcell_id = get_id(pi, PCELL_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _reset_thread(struct pl330_thread *thrd)
|
static inline void _reset_thread(struct pl330_thread *thrd)
|
||||||
@ -2098,10 +2078,8 @@ static int pl330_add(struct pl330_info *pi)
|
|||||||
regs = pi->base;
|
regs = pi->base;
|
||||||
|
|
||||||
/* Check if we can handle this DMAC */
|
/* Check if we can handle this DMAC */
|
||||||
if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL
|
if ((pi->pcfg.periph_id & 0xfffff) != PERIPH_ID_VAL) {
|
||||||
|| get_id(pi, PCELL_ID) != PCELL_ID_VAL) {
|
dev_err(pi->dev, "PERIPH_ID 0x%x !\n", pi->pcfg.periph_id);
|
||||||
dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n",
|
|
||||||
get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID));
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2916,6 +2894,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
pi->pcfg.periph_id = adev->periphid;
|
||||||
ret = pl330_add(pi);
|
ret = pl330_add(pi);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto probe_err1;
|
goto probe_err1;
|
||||||
|
@ -4434,7 +4434,7 @@ static int ppc440spe_adma_probe(struct platform_device *ofdev)
|
|||||||
adev->dev = &ofdev->dev;
|
adev->dev = &ofdev->dev;
|
||||||
adev->common.dev = &ofdev->dev;
|
adev->common.dev = &ofdev->dev;
|
||||||
INIT_LIST_HEAD(&adev->common.channels);
|
INIT_LIST_HEAD(&adev->common.channels);
|
||||||
dev_set_drvdata(&ofdev->dev, adev);
|
platform_set_drvdata(ofdev, adev);
|
||||||
|
|
||||||
/* create a channel */
|
/* create a channel */
|
||||||
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
|
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
|
||||||
@ -4547,14 +4547,13 @@ out:
|
|||||||
*/
|
*/
|
||||||
static int ppc440spe_adma_remove(struct platform_device *ofdev)
|
static int ppc440spe_adma_remove(struct platform_device *ofdev)
|
||||||
{
|
{
|
||||||
struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev);
|
struct ppc440spe_adma_device *adev = platform_get_drvdata(ofdev);
|
||||||
struct device_node *np = ofdev->dev.of_node;
|
struct device_node *np = ofdev->dev.of_node;
|
||||||
struct resource res;
|
struct resource res;
|
||||||
struct dma_chan *chan, *_chan;
|
struct dma_chan *chan, *_chan;
|
||||||
struct ppc_dma_chan_ref *ref, *_ref;
|
struct ppc_dma_chan_ref *ref, *_ref;
|
||||||
struct ppc440spe_adma_chan *ppc440spe_chan;
|
struct ppc440spe_adma_chan *ppc440spe_chan;
|
||||||
|
|
||||||
dev_set_drvdata(&ofdev->dev, NULL);
|
|
||||||
if (adev->id < PPC440SPE_ADMA_ENGINES_NUM)
|
if (adev->id < PPC440SPE_ADMA_ENGINES_NUM)
|
||||||
ppc440spe_adma_devices[adev->id] = -1;
|
ppc440spe_adma_devices[adev->id] = -1;
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o
|
obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o shdma-of.o
|
||||||
obj-$(CONFIG_SH_DMAE) += shdma.o
|
obj-$(CONFIG_SH_DMAE) += shdma.o
|
||||||
obj-$(CONFIG_SUDMAC) += sudmac.o
|
obj-$(CONFIG_SUDMAC) += sudmac.o
|
||||||
|
@ -175,7 +175,18 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id)
|
|||||||
{
|
{
|
||||||
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
|
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
|
||||||
const struct shdma_ops *ops = sdev->ops;
|
const struct shdma_ops *ops = sdev->ops;
|
||||||
int ret;
|
int ret, match;
|
||||||
|
|
||||||
|
if (schan->dev->of_node) {
|
||||||
|
match = schan->hw_req;
|
||||||
|
ret = ops->set_slave(schan, match, true);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
slave_id = schan->slave_id;
|
||||||
|
} else {
|
||||||
|
match = slave_id;
|
||||||
|
}
|
||||||
|
|
||||||
if (slave_id < 0 || slave_id >= slave_num)
|
if (slave_id < 0 || slave_id >= slave_num)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -183,7 +194,7 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id)
|
|||||||
if (test_and_set_bit(slave_id, shdma_slave_used))
|
if (test_and_set_bit(slave_id, shdma_slave_used))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
ret = ops->set_slave(schan, slave_id, false);
|
ret = ops->set_slave(schan, match, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
clear_bit(slave_id, shdma_slave_used);
|
clear_bit(slave_id, shdma_slave_used);
|
||||||
return ret;
|
return ret;
|
||||||
@ -206,23 +217,26 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id)
|
|||||||
* services would have to provide their own filters, which first would check
|
* services would have to provide their own filters, which first would check
|
||||||
* the device driver, similar to how other DMAC drivers, e.g., sa11x0-dma.c, do
|
* the device driver, similar to how other DMAC drivers, e.g., sa11x0-dma.c, do
|
||||||
* this, and only then, in case of a match, call this common filter.
|
* this, and only then, in case of a match, call this common filter.
|
||||||
|
* NOTE 2: This filter function is also used in the DT case by shdma_of_xlate().
|
||||||
|
* In that case the MID-RID value is used for slave channel filtering and is
|
||||||
|
* passed to this function in the "arg" parameter.
|
||||||
*/
|
*/
|
||||||
bool shdma_chan_filter(struct dma_chan *chan, void *arg)
|
bool shdma_chan_filter(struct dma_chan *chan, void *arg)
|
||||||
{
|
{
|
||||||
struct shdma_chan *schan = to_shdma_chan(chan);
|
struct shdma_chan *schan = to_shdma_chan(chan);
|
||||||
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
|
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
|
||||||
const struct shdma_ops *ops = sdev->ops;
|
const struct shdma_ops *ops = sdev->ops;
|
||||||
int slave_id = (int)arg;
|
int match = (int)arg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (slave_id < 0)
|
if (match < 0)
|
||||||
/* No slave requested - arbitrary channel */
|
/* No slave requested - arbitrary channel */
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (slave_id >= slave_num)
|
if (!schan->dev->of_node && match >= slave_num)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ret = ops->set_slave(schan, slave_id, true);
|
ret = ops->set_slave(schan, match, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
82
drivers/dma/sh/shdma-of.c
Normal file
82
drivers/dma/sh/shdma-of.c
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* SHDMA Device Tree glue
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Renesas Electronics Inc.
|
||||||
|
* Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||||
|
*
|
||||||
|
* 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/dmaengine.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/shdma-base.h>
|
||||||
|
|
||||||
|
#define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan)
|
||||||
|
|
||||||
|
static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec,
|
||||||
|
struct of_dma *ofdma)
|
||||||
|
{
|
||||||
|
u32 id = dma_spec->args[0];
|
||||||
|
dma_cap_mask_t mask;
|
||||||
|
struct dma_chan *chan;
|
||||||
|
|
||||||
|
if (dma_spec->args_count != 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dma_cap_zero(mask);
|
||||||
|
/* Only slave DMA channels can be allocated via DT */
|
||||||
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
|
||||||
|
chan = dma_request_channel(mask, shdma_chan_filter, (void *)id);
|
||||||
|
if (chan)
|
||||||
|
to_shdma_chan(chan)->hw_req = id;
|
||||||
|
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shdma_of_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
const struct of_dev_auxdata *lookup = pdev->dev.platform_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!lookup)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = of_dma_controller_register(pdev->dev.of_node,
|
||||||
|
shdma_of_xlate, pdev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = of_platform_populate(pdev->dev.of_node, NULL, lookup, &pdev->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
of_dma_controller_free(pdev->dev.of_node);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id shdma_of_match[] = {
|
||||||
|
{ .compatible = "renesas,shdma-mux", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sh_dmae_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver shdma_of = {
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "shdma-of",
|
||||||
|
.of_match_table = shdma_of_match,
|
||||||
|
},
|
||||||
|
.probe = shdma_of_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(shdma_of);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("SH-DMA driver DT glue");
|
||||||
|
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
|
@ -301,20 +301,32 @@ static void sh_dmae_setup_xfer(struct shdma_chan *schan,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a slave channel configuration from the contoller list by either a slave
|
||||||
|
* ID in the non-DT case, or by a MID/RID value in the DT case
|
||||||
|
*/
|
||||||
static const struct sh_dmae_slave_config *dmae_find_slave(
|
static const struct sh_dmae_slave_config *dmae_find_slave(
|
||||||
struct sh_dmae_chan *sh_chan, int slave_id)
|
struct sh_dmae_chan *sh_chan, int match)
|
||||||
{
|
{
|
||||||
struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
|
struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
|
||||||
struct sh_dmae_pdata *pdata = shdev->pdata;
|
struct sh_dmae_pdata *pdata = shdev->pdata;
|
||||||
const struct sh_dmae_slave_config *cfg;
|
const struct sh_dmae_slave_config *cfg;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (slave_id >= SH_DMA_SLAVE_NUMBER)
|
if (!sh_chan->shdma_chan.dev->of_node) {
|
||||||
|
if (match >= SH_DMA_SLAVE_NUMBER)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
|
for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
|
||||||
if (cfg->slave_id == slave_id)
|
if (cfg->slave_id == match)
|
||||||
return cfg;
|
return cfg;
|
||||||
|
} else {
|
||||||
|
for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
|
||||||
|
if (cfg->mid_rid == match) {
|
||||||
|
sh_chan->shdma_chan.slave_id = cfg->slave_id;
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -729,7 +741,7 @@ static int sh_dmae_probe(struct platform_device *pdev)
|
|||||||
goto eshdma;
|
goto eshdma;
|
||||||
|
|
||||||
/* platform data */
|
/* platform data */
|
||||||
shdev->pdata = pdev->dev.platform_data;
|
shdev->pdata = pdata;
|
||||||
|
|
||||||
if (pdata->chcr_offset)
|
if (pdata->chcr_offset)
|
||||||
shdev->chcr_offset = pdata->chcr_offset;
|
shdev->chcr_offset = pdata->chcr_offset;
|
||||||
@ -920,11 +932,18 @@ static int sh_dmae_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sh_dmae_of_match[] = {
|
||||||
|
{ .compatible = "renesas,shdma", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sh_dmae_of_match);
|
||||||
|
|
||||||
static struct platform_driver sh_dmae_driver = {
|
static struct platform_driver sh_dmae_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pm = &sh_dmae_pm,
|
.pm = &sh_dmae_pm,
|
||||||
.name = SH_DMAE_DRV_NAME,
|
.name = SH_DMAE_DRV_NAME,
|
||||||
|
.of_match_table = sh_dmae_of_match,
|
||||||
},
|
},
|
||||||
.remove = sh_dmae_remove,
|
.remove = sh_dmae_remove,
|
||||||
.shutdown = sh_dmae_shutdown,
|
.shutdown = sh_dmae_shutdown,
|
||||||
|
@ -466,12 +466,29 @@ static enum dma_status
|
|||||||
sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
|
||||||
struct dma_tx_state *txstate)
|
struct dma_tx_state *txstate)
|
||||||
{
|
{
|
||||||
|
struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
|
||||||
struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
|
struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
enum dma_status ret;
|
enum dma_status ret;
|
||||||
|
struct sirfsoc_dma_desc *sdesc;
|
||||||
|
int cid = schan->chan.chan_id;
|
||||||
|
unsigned long dma_pos;
|
||||||
|
unsigned long dma_request_bytes;
|
||||||
|
unsigned long residue;
|
||||||
|
|
||||||
spin_lock_irqsave(&schan->lock, flags);
|
spin_lock_irqsave(&schan->lock, flags);
|
||||||
|
|
||||||
|
sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc,
|
||||||
|
node);
|
||||||
|
dma_request_bytes = (sdesc->xlen + 1) * (sdesc->ylen + 1) *
|
||||||
|
(sdesc->width * SIRFSOC_DMA_WORD_LEN);
|
||||||
|
|
||||||
ret = dma_cookie_status(chan, cookie, txstate);
|
ret = dma_cookie_status(chan, cookie, txstate);
|
||||||
|
dma_pos = readl_relaxed(sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR)
|
||||||
|
<< 2;
|
||||||
|
residue = dma_request_bytes - (dma_pos - sdesc->addr);
|
||||||
|
dma_set_residue(txstate, residue);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&schan->lock, flags);
|
spin_unlock_irqrestore(&schan->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1191,6 +1191,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
|
|||||||
list_splice_init(&tdc->free_dma_desc, &dma_desc_list);
|
list_splice_init(&tdc->free_dma_desc, &dma_desc_list);
|
||||||
INIT_LIST_HEAD(&tdc->cb_desc);
|
INIT_LIST_HEAD(&tdc->cb_desc);
|
||||||
tdc->config_init = false;
|
tdc->config_init = false;
|
||||||
|
tdc->isr_handler = NULL;
|
||||||
spin_unlock_irqrestore(&tdc->lock, flags);
|
spin_unlock_irqrestore(&tdc->lock, flags);
|
||||||
|
|
||||||
while (!list_empty(&dma_desc_list)) {
|
while (!list_empty(&dma_desc_list)) {
|
||||||
@ -1334,7 +1335,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"request_irq failed with err %d channel %d\n",
|
"request_irq failed with err %d channel %d\n",
|
||||||
i, ret);
|
ret, i);
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,8 +811,6 @@ static int td_remove(struct platform_device *pdev)
|
|||||||
kfree(td);
|
kfree(td);
|
||||||
release_mem_region(iomem->start, resource_size(iomem));
|
release_mem_region(iomem->start, resource_size(iomem));
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "Removed...\n");
|
dev_dbg(&pdev->dev, "Removed...\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -76,11 +76,11 @@ struct pl08x_channel_data {
|
|||||||
* platform, all inclusive, including multiplexed channels. The available
|
* platform, all inclusive, including multiplexed channels. The available
|
||||||
* physical channels will be multiplexed around these signals as they are
|
* physical channels will be multiplexed around these signals as they are
|
||||||
* requested, just enumerate all possible channels.
|
* requested, just enumerate all possible channels.
|
||||||
* @get_signal: request a physical signal to be used for a DMA transfer
|
* @get_xfer_signal: request a physical signal to be used for a DMA transfer
|
||||||
* immediately: if there is some multiplexing or similar blocking the use
|
* immediately: if there is some multiplexing or similar blocking the use
|
||||||
* of the channel the transfer can be denied by returning less than zero,
|
* of the channel the transfer can be denied by returning less than zero,
|
||||||
* else it returns the allocated signal number
|
* else it returns the allocated signal number
|
||||||
* @put_signal: indicate to the platform that this physical signal is not
|
* @put_xfer_signal: indicate to the platform that this physical signal is not
|
||||||
* running any DMA transfer and multiplexing can be recycled
|
* running any DMA transfer and multiplexing can be recycled
|
||||||
* @lli_buses: buses which LLIs can be fetched from: PL08X_AHB1 | PL08X_AHB2
|
* @lli_buses: buses which LLIs can be fetched from: PL08X_AHB1 | PL08X_AHB2
|
||||||
* @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2
|
* @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2
|
||||||
@ -89,8 +89,8 @@ struct pl08x_platform_data {
|
|||||||
const struct pl08x_channel_data *slave_channels;
|
const struct pl08x_channel_data *slave_channels;
|
||||||
unsigned int num_slave_channels;
|
unsigned int num_slave_channels;
|
||||||
struct pl08x_channel_data memcpy_channel;
|
struct pl08x_channel_data memcpy_channel;
|
||||||
int (*get_signal)(const struct pl08x_channel_data *);
|
int (*get_xfer_signal)(const struct pl08x_channel_data *);
|
||||||
void (*put_signal)(const struct pl08x_channel_data *, int);
|
void (*put_xfer_signal)(const struct pl08x_channel_data *, int);
|
||||||
u8 lli_buses;
|
u8 lli_buses;
|
||||||
u8 mem_buses;
|
u8 mem_buses;
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,6 @@ struct device_node;
|
|||||||
struct of_dma {
|
struct of_dma {
|
||||||
struct list_head of_dma_controllers;
|
struct list_head of_dma_controllers;
|
||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
int of_dma_nbcells;
|
|
||||||
struct dma_chan *(*of_dma_xlate)
|
struct dma_chan *(*of_dma_xlate)
|
||||||
(struct of_phandle_args *, struct of_dma *);
|
(struct of_phandle_args *, struct of_dma *);
|
||||||
void *of_dma_data;
|
void *of_dma_data;
|
||||||
|
@ -35,16 +35,20 @@ struct at_dma_slave {
|
|||||||
|
|
||||||
|
|
||||||
/* Platform-configurable bits in CFG */
|
/* Platform-configurable bits in CFG */
|
||||||
|
#define ATC_PER_MSB(h) ((0x30U & (h)) >> 4) /* Extract most significant bits of a handshaking identifier */
|
||||||
|
|
||||||
#define ATC_SRC_PER(h) (0xFU & (h)) /* Channel src rq associated with periph handshaking ifc h */
|
#define ATC_SRC_PER(h) (0xFU & (h)) /* Channel src rq associated with periph handshaking ifc h */
|
||||||
#define ATC_DST_PER(h) ((0xFU & (h)) << 4) /* Channel dst rq associated with periph handshaking ifc h */
|
#define ATC_DST_PER(h) ((0xFU & (h)) << 4) /* Channel dst rq associated with periph handshaking ifc h */
|
||||||
#define ATC_SRC_REP (0x1 << 8) /* Source Replay Mod */
|
#define ATC_SRC_REP (0x1 << 8) /* Source Replay Mod */
|
||||||
#define ATC_SRC_H2SEL (0x1 << 9) /* Source Handshaking Mod */
|
#define ATC_SRC_H2SEL (0x1 << 9) /* Source Handshaking Mod */
|
||||||
#define ATC_SRC_H2SEL_SW (0x0 << 9)
|
#define ATC_SRC_H2SEL_SW (0x0 << 9)
|
||||||
#define ATC_SRC_H2SEL_HW (0x1 << 9)
|
#define ATC_SRC_H2SEL_HW (0x1 << 9)
|
||||||
|
#define ATC_SRC_PER_MSB(h) (ATC_PER_MSB(h) << 10) /* Channel src rq (most significant bits) */
|
||||||
#define ATC_DST_REP (0x1 << 12) /* Destination Replay Mod */
|
#define ATC_DST_REP (0x1 << 12) /* Destination Replay Mod */
|
||||||
#define ATC_DST_H2SEL (0x1 << 13) /* Destination Handshaking Mod */
|
#define ATC_DST_H2SEL (0x1 << 13) /* Destination Handshaking Mod */
|
||||||
#define ATC_DST_H2SEL_SW (0x0 << 13)
|
#define ATC_DST_H2SEL_SW (0x0 << 13)
|
||||||
#define ATC_DST_H2SEL_HW (0x1 << 13)
|
#define ATC_DST_H2SEL_HW (0x1 << 13)
|
||||||
|
#define ATC_DST_PER_MSB(h) (ATC_PER_MSB(h) << 14) /* Channel dst rq (most significant bits) */
|
||||||
#define ATC_SOD (0x1 << 16) /* Stop On Done */
|
#define ATC_SOD (0x1 << 16) /* Stop On Done */
|
||||||
#define ATC_LOCK_IF (0x1 << 20) /* Interface Lock */
|
#define ATC_LOCK_IF (0x1 << 20) /* Interface Lock */
|
||||||
#define ATC_LOCK_B (0x1 << 21) /* AHB Bus Lock */
|
#define ATC_LOCK_B (0x1 << 21) /* AHB Bus Lock */
|
||||||
|
@ -60,10 +60,8 @@ static inline int imx_dma_is_ipu(struct dma_chan *chan)
|
|||||||
|
|
||||||
static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
|
static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
|
||||||
{
|
{
|
||||||
return strstr(dev_name(chan->device->dev), "sdma") ||
|
return !strcmp(chan->device->dev->driver->name, "imx-sdma") ||
|
||||||
!strcmp(dev_name(chan->device->dev), "imx1-dma") ||
|
!strcmp(chan->device->dev->driver->name, "imx-dma");
|
||||||
!strcmp(dev_name(chan->device->dev), "imx21-dma") ||
|
|
||||||
!strcmp(dev_name(chan->device->dev), "imx27-dma");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -99,6 +99,4 @@ struct sh_dmae_pdata {
|
|||||||
#define CHCR_TE 0x00000002
|
#define CHCR_TE 0x00000002
|
||||||
#define CHCR_IE 0x00000004
|
#define CHCR_IE 0x00000004
|
||||||
|
|
||||||
bool shdma_chan_filter(struct dma_chan *chan, void *arg);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,6 +68,8 @@ struct shdma_chan {
|
|||||||
int id; /* Raw id of this channel */
|
int id; /* Raw id of this channel */
|
||||||
int irq; /* Channel IRQ */
|
int irq; /* Channel IRQ */
|
||||||
int slave_id; /* Client ID for slave DMA */
|
int slave_id; /* Client ID for slave DMA */
|
||||||
|
int hw_req; /* DMA request line for slave DMA - same
|
||||||
|
* as MID/RID, used with DT */
|
||||||
enum shdma_pm_state pm_state;
|
enum shdma_pm_state pm_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,5 +124,6 @@ void shdma_chan_remove(struct shdma_chan *schan);
|
|||||||
int shdma_init(struct device *dev, struct shdma_dev *sdev,
|
int shdma_init(struct device *dev, struct shdma_dev *sdev,
|
||||||
int chan_num);
|
int chan_num);
|
||||||
void shdma_cleanup(struct shdma_dev *sdev);
|
void shdma_cleanup(struct shdma_dev *sdev);
|
||||||
|
bool shdma_chan_filter(struct dma_chan *chan, void *arg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user