Commit d2b4a646 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 8dce5f3d 67eacc15
...@@ -24,8 +24,11 @@ The three cells in order are: ...@@ -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:
......
* 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: ...@@ -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>;
};
* 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 ...@@ -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) ...@@ -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 = { ...@@ -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 = { ...@@ -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 @@ ...@@ -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 { ...@@ -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; ...@@ -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 @@ ...@@ -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 = { ...@@ -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[] = { ...@@ -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] = {
......
/*
* 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 = { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
spin_lock_irqsave(&atchan->lock, flags);
ret = dma_cookie_status(chan, cookie, txstate); ret = dma_cookie_status(chan, cookie, txstate);
if (ret != DMA_SUCCESS) { if (ret == DMA_SUCCESS)
atc_cleanup_descriptors(atchan); return ret;
/*
* There's no point calculating the residue if there's
* no txstate to store the value.
*/
if (!txstate)
return DMA_ERROR;
ret = dma_cookie_status(chan, cookie, txstate); spin_lock_irqsave(&atchan->lock, flags);
}
last_complete = chan->completed_cookie; /* Get number of bytes left in the active transactions */
last_used = chan->cookie; bytes = atc_get_bytes_left(chan);
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;
if (atc_chan_is_paused(atchan)) } else {
ret = DMA_PAUSED; dma_set_residue(txstate, bytes);
}
dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n", dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d residue = %d\n",
ret, cookie, last_complete ? last_complete : 0, ret, cookie, bytes);
last_used ? last_used : 0);
return ret; return ret;
} }
...@@ -1120,7 +1187,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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 @@ static int __init at_dma_probe(struct platform_device *pdev) ...@@ -1472,10 +1560,10 @@ static int __init at_dma_probe(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);
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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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;
......
/*
* 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");
#
# 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
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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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,12 +1610,12 @@ static int dw_probe(struct platform_device *pdev) ...@@ -1798,12 +1610,12 @@ 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
* stored 4 bit value represents blocks from 0x00 for 3 * stored 4 bit value represents blocks from 0x00 for 3
...@@ -1834,7 +1646,7 @@ static int dw_probe(struct platform_device *pdev) ...@@ -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) ...@@ -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) ...@@ -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);
#ifdef CONFIG_PM_SLEEP
static int dw_suspend_noirq(struct device *dev) 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>");
/*
* 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 */
/*
* 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>");
/*
* 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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 @@ static int __init sdma_init(struct sdma_engine *sdma) ...@@ -1296,6 +1297,35 @@ static int __init sdma_init(struct sdma_engine *sdma)
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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int ppc440spe_adma_probe(struct platform_device *ofdev) ...@@ -4547,14 +4547,13 @@ static int ppc440spe_adma_probe(struct platform_device *ofdev)
*/ */
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;
......
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) ...@@ -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) ...@@ -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) ...@@ -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;
......
/*
* 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, ...@@ -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) {
return NULL; if (match >= SH_DMA_SLAVE_NUMBER)
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) ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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); ...@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment