Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
59ca37f7
Commit
59ca37f7
authored
Oct 04, 2011
by
Kukjin Kim
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'next/topic-dma-samsung' into next-samsung-devel
parents
7d84b3e9
c9312209
Changes
49
Show whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
2066 additions
and
2302 deletions
+2066
-2302
arch/arm/include/asm/hardware/pl080.h
arch/arm/include/asm/hardware/pl080.h
+4
-0
arch/arm/mach-exynos4/Kconfig
arch/arm/mach-exynos4/Kconfig
+1
-1
arch/arm/mach-exynos4/clock.c
arch/arm/mach-exynos4/clock.c
+11
-4
arch/arm/mach-exynos4/dma.c
arch/arm/mach-exynos4/dma.c
+189
-111
arch/arm/mach-exynos4/include/mach/dma.h
arch/arm/mach-exynos4/include/mach/dma.h
+2
-2
arch/arm/mach-s3c2410/include/mach/dma.h
arch/arm/mach-s3c2410/include/mach/dma.h
+13
-7
arch/arm/mach-s3c2412/dma.c
arch/arm/mach-s3c2412/dma.c
+2
-2
arch/arm/mach-s3c64xx/dma.c
arch/arm/mach-s3c64xx/dma.c
+5
-5
arch/arm/mach-s3c64xx/include/mach/dma.h
arch/arm/mach-s3c64xx/include/mach/dma.h
+6
-2
arch/arm/mach-s5p64x0/Kconfig
arch/arm/mach-s5p64x0/Kconfig
+2
-2
arch/arm/mach-s5p64x0/clock-s5p6440.c
arch/arm/mach-s5p64x0/clock-s5p6440.c
+9
-1
arch/arm/mach-s5p64x0/clock-s5p6450.c
arch/arm/mach-s5p64x0/clock-s5p6450.c
+9
-1
arch/arm/mach-s5p64x0/dma.c
arch/arm/mach-s5p64x0/dma.c
+182
-91
arch/arm/mach-s5p64x0/include/mach/dma.h
arch/arm/mach-s5p64x0/include/mach/dma.h
+2
-2
arch/arm/mach-s5pc100/Kconfig
arch/arm/mach-s5pc100/Kconfig
+1
-1
arch/arm/mach-s5pc100/clock.c
arch/arm/mach-s5pc100/clock.c
+11
-4
arch/arm/mach-s5pc100/dma.c
arch/arm/mach-s5pc100/dma.c
+212
-112
arch/arm/mach-s5pc100/include/mach/dma.h
arch/arm/mach-s5pc100/include/mach/dma.h
+2
-2
arch/arm/mach-s5pv210/Kconfig
arch/arm/mach-s5pv210/Kconfig
+1
-1
arch/arm/mach-s5pv210/clock.c
arch/arm/mach-s5pv210/clock.c
+10
-4
arch/arm/mach-s5pv210/dma.c
arch/arm/mach-s5pv210/dma.c
+205
-112
arch/arm/mach-s5pv210/include/mach/dma.h
arch/arm/mach-s5pv210/include/mach/dma.h
+2
-2
arch/arm/plat-s3c24xx/dma.c
arch/arm/plat-s3c24xx/dma.c
+5
-5
arch/arm/plat-samsung/Kconfig
arch/arm/plat-samsung/Kconfig
+6
-3
arch/arm/plat-samsung/Makefile
arch/arm/plat-samsung/Makefile
+2
-2
arch/arm/plat-samsung/dma-ops.c
arch/arm/plat-samsung/dma-ops.c
+131
-0
arch/arm/plat-samsung/include/plat/dma-ops.h
arch/arm/plat-samsung/include/plat/dma-ops.h
+63
-0
arch/arm/plat-samsung/include/plat/dma-pl330.h
arch/arm/plat-samsung/include/plat/dma-pl330.h
+15
-9
arch/arm/plat-samsung/include/plat/dma-s3c24xx.h
arch/arm/plat-samsung/include/plat/dma-s3c24xx.h
+1
-1
arch/arm/plat-samsung/include/plat/dma.h
arch/arm/plat-samsung/include/plat/dma.h
+4
-6
arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
+0
-32
arch/arm/plat-samsung/s3c-dma-ops.c
arch/arm/plat-samsung/s3c-dma-ops.c
+130
-0
arch/arm/plat-samsung/s3c-pl330.c
arch/arm/plat-samsung/s3c-pl330.c
+0
-1244
drivers/dma/Kconfig
drivers/dma/Kconfig
+2
-1
drivers/dma/amba-pl08x.c
drivers/dma/amba-pl08x.c
+197
-258
drivers/dma/at_hdmac.c
drivers/dma/at_hdmac.c
+125
-34
drivers/dma/at_hdmac_regs.h
drivers/dma/at_hdmac_regs.h
+24
-0
drivers/dma/dmatest.c
drivers/dma/dmatest.c
+21
-2
drivers/dma/imx-sdma.c
drivers/dma/imx-sdma.c
+38
-9
drivers/dma/mxs-dma.c
drivers/dma/mxs-dma.c
+24
-21
drivers/dma/pl330.c
drivers/dma/pl330.c
+207
-22
drivers/mmc/host/s3cmci.c
drivers/mmc/host/s3cmci.c
+3
-3
drivers/spi/spi-s3c64xx.c
drivers/spi/spi-s3c64xx.c
+92
-83
include/linux/amba/pl08x.h
include/linux/amba/pl08x.h
+12
-2
include/linux/amba/pl330.h
include/linux/amba/pl330.h
+1
-5
include/linux/dmaengine.h
include/linux/dmaengine.h
+11
-2
sound/soc/samsung/ac97.c
sound/soc/samsung/ac97.c
+8
-2
sound/soc/samsung/dma.c
sound/soc/samsung/dma.c
+60
-86
sound/soc/samsung/dma.h
sound/soc/samsung/dma.h
+3
-1
No files found.
arch/arm/include/asm/hardware/pl080.h
View file @
59ca37f7
...
...
@@ -21,6 +21,9 @@
* OneNAND features.
*/
#ifndef ASM_PL080_H
#define ASM_PL080_H
#define PL080_INT_STATUS (0x00)
#define PL080_TC_STATUS (0x04)
#define PL080_TC_CLEAR (0x08)
...
...
@@ -138,3 +141,4 @@ struct pl080s_lli {
u32
control1
;
};
#endif
/* ASM_PL080_H */
arch/arm/mach-exynos4/Kconfig
View file @
59ca37f7
...
...
@@ -11,7 +11,7 @@ if ARCH_EXYNOS4
config CPU_EXYNOS4210
bool
select S
3C_PL330_DMA
select S
AMSUNG_DMADEV
help
Enable EXYNOS4210 CPU support
...
...
arch/arm/mach-exynos4/clock.c
View file @
59ca37f7
...
...
@@ -43,6 +43,11 @@ static struct clk clk_sclk_usbphy1 = {
.
name
=
"sclk_usbphy1"
,
};
static
struct
clk
dummy_apb_pclk
=
{
.
name
=
"apb_pclk"
,
.
id
=
-
1
,
};
static
int
exynos4_clksrc_mask_top_ctrl
(
struct
clk
*
clk
,
int
enable
)
{
return
s5p_gatectrl
(
S5P_CLKSRC_MASK_TOP
,
clk
,
enable
);
...
...
@@ -454,13 +459,13 @@ static struct clk init_clocks_off[] = {
.
enable
=
exynos4_clk_ip_fsys_ctrl
,
.
ctrlbit
=
(
1
<<
10
),
},
{
.
name
=
"
p
dma"
,
.
devname
=
"
s3c
-pl330.0"
,
.
name
=
"dma"
,
.
devname
=
"
dma
-pl330.0"
,
.
enable
=
exynos4_clk_ip_fsys_ctrl
,
.
ctrlbit
=
(
1
<<
0
),
},
{
.
name
=
"
p
dma"
,
.
devname
=
"
s3c
-pl330.1"
,
.
name
=
"dma"
,
.
devname
=
"
dma
-pl330.1"
,
.
enable
=
exynos4_clk_ip_fsys_ctrl
,
.
ctrlbit
=
(
1
<<
1
),
},
{
...
...
@@ -1208,5 +1213,7 @@ void __init exynos4_register_clocks(void)
s3c_register_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c_disable_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c24xx_register_clock
(
&
dummy_apb_pclk
);
s3c_pwmclk_init
();
}
arch/arm/mach-exynos4/dma.c
View file @
59ca37f7
...
...
@@ -21,151 +21,229 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
#include <asm/irq.h>
#include <plat/devs.h>
#include <plat/irqs.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <plat/s3c-pl330-pdata.h>
#include <mach/dma.h>
static
u64
dma_dmamask
=
DMA_BIT_MASK
(
32
);
static
struct
resource
exynos4_pdma0_resource
[]
=
{
[
0
]
=
{
.
start
=
EXYNOS4_PA_PDMA0
,
.
end
=
EXYNOS4_PA_PDMA0
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_PDMA0
,
.
end
=
IRQ_PDMA0
,
.
flags
=
IORESOURCE_IRQ
,
struct
dma_pl330_peri
pdma0_peri
[
28
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_PCM0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ0
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ2
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0S_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART4_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART4_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS4_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS4_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_MICIN
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_PCMIN
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_PCMOUT
,
.
rqtype
=
MEMTODEV
,
},
};
static
struct
s3c_pl330_platdata
exynos4_pdma0_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_PCM0_RX
,
[
1
]
=
DMACH_PCM0_TX
,
[
2
]
=
DMACH_PCM2_RX
,
[
3
]
=
DMACH_PCM2_TX
,
[
4
]
=
DMACH_MSM_REQ0
,
[
5
]
=
DMACH_MSM_REQ2
,
[
6
]
=
DMACH_SPI0_RX
,
[
7
]
=
DMACH_SPI0_TX
,
[
8
]
=
DMACH_SPI2_RX
,
[
9
]
=
DMACH_SPI2_TX
,
[
10
]
=
DMACH_I2S0S_TX
,
[
11
]
=
DMACH_I2S0_RX
,
[
12
]
=
DMACH_I2S0_TX
,
[
13
]
=
DMACH_I2S2_RX
,
[
14
]
=
DMACH_I2S2_TX
,
[
15
]
=
DMACH_UART0_RX
,
[
16
]
=
DMACH_UART0_TX
,
[
17
]
=
DMACH_UART2_RX
,
[
18
]
=
DMACH_UART2_TX
,
[
19
]
=
DMACH_UART4_RX
,
[
20
]
=
DMACH_UART4_TX
,
[
21
]
=
DMACH_SLIMBUS0_RX
,
[
22
]
=
DMACH_SLIMBUS0_TX
,
[
23
]
=
DMACH_SLIMBUS2_RX
,
[
24
]
=
DMACH_SLIMBUS2_TX
,
[
25
]
=
DMACH_SLIMBUS4_RX
,
[
26
]
=
DMACH_SLIMBUS4_TX
,
[
27
]
=
DMACH_AC97_MICIN
,
[
28
]
=
DMACH_AC97_PCMIN
,
[
29
]
=
DMACH_AC97_PCMOUT
,
[
30
]
=
DMACH_MAX
,
[
31
]
=
DMACH_MAX
,
},
struct
dma_pl330_platdata
exynos4_pdma0_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
pdma0_peri
),
.
peri
=
pdma0_peri
,
};
static
struct
platform_device
exynos4_device_pdma0
=
{
.
name
=
"s3c-pl330"
,
.
id
=
0
,
.
num_resources
=
ARRAY_SIZE
(
exynos4_pdma0_resource
),
.
resource
=
exynos4_pdma0_resource
,
struct
amba_device
exynos4_device_pdma0
=
{
.
dev
=
{
.
init_name
=
"dma-pl330.0"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
.
platform_data
=
&
exynos4_pdma0_pdata
,
},
};
static
struct
resource
exynos4_pdma1_resource
[]
=
{
[
0
]
=
{
.
start
=
EXYNOS4_PA_PDMA1
,
.
end
=
EXYNOS4_PA_PDMA1
+
SZ_4K
,
.
res
=
{
.
start
=
EXYNOS4_PA_PDMA0
,
.
end
=
EXYNOS4_PA_PDMA0
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_PDMA1
,
.
end
=
IRQ_PDMA1
,
.
flags
=
IORESOURCE_IRQ
,
},
.
irq
=
{
IRQ_PDMA0
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
struct
s3c_pl330_platdata
exynos4_pdma1_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_PCM0_RX
,
[
1
]
=
DMACH_PCM0_TX
,
[
2
]
=
DMACH_PCM1_RX
,
[
3
]
=
DMACH_PCM1_TX
,
[
4
]
=
DMACH_MSM_REQ1
,
[
5
]
=
DMACH_MSM_REQ3
,
[
6
]
=
DMACH_SPI1_RX
,
[
7
]
=
DMACH_SPI1_TX
,
[
8
]
=
DMACH_I2S0S_TX
,
[
9
]
=
DMACH_I2S0_RX
,
[
10
]
=
DMACH_I2S0_TX
,
[
11
]
=
DMACH_I2S1_RX
,
[
12
]
=
DMACH_I2S1_TX
,
[
13
]
=
DMACH_UART0_RX
,
[
14
]
=
DMACH_UART0_TX
,
[
15
]
=
DMACH_UART1_RX
,
[
16
]
=
DMACH_UART1_TX
,
[
17
]
=
DMACH_UART3_RX
,
[
18
]
=
DMACH_UART3_TX
,
[
19
]
=
DMACH_SLIMBUS1_RX
,
[
20
]
=
DMACH_SLIMBUS1_TX
,
[
21
]
=
DMACH_SLIMBUS3_RX
,
[
22
]
=
DMACH_SLIMBUS3_TX
,
[
23
]
=
DMACH_SLIMBUS5_RX
,
[
24
]
=
DMACH_SLIMBUS5_TX
,
[
25
]
=
DMACH_SLIMBUS0AUX_RX
,
[
26
]
=
DMACH_SLIMBUS0AUX_TX
,
[
27
]
=
DMACH_SPDIF
,
[
28
]
=
DMACH_MAX
,
[
29
]
=
DMACH_MAX
,
[
30
]
=
DMACH_MAX
,
[
31
]
=
DMACH_MAX
,
struct
dma_pl330_peri
pdma1_peri
[
25
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_PCM0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ1
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ3
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0S_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS5_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SLIMBUS5_TX
,
.
rqtype
=
MEMTODEV
,
},
};
static
struct
platform_device
exynos4_device_pdma1
=
{
.
name
=
"s3c-pl330"
,
.
id
=
1
,
.
num_resources
=
ARRAY_SIZE
(
exynos4_pdma1_resource
),
.
resource
=
exynos4_pdma1_resource
,
struct
dma_pl330_platdata
exynos4_pdma1_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
pdma1_peri
),
.
peri
=
pdma1_peri
,
};
struct
amba_device
exynos4_device_pdma1
=
{
.
dev
=
{
.
init_name
=
"dma-pl330.1"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
.
platform_data
=
&
exynos4_pdma1_pdata
,
},
};
static
struct
platform_device
*
exynos4_dmacs
[]
__initdata
=
{
&
exynos4_device_pdma0
,
&
exynos4_device_pdma1
,
.
res
=
{
.
start
=
EXYNOS4_PA_PDMA1
,
.
end
=
EXYNOS4_PA_PDMA1
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
.
irq
=
{
IRQ_PDMA1
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
int
__init
exynos4_dma_init
(
void
)
{
platform_add_devices
(
exynos4_dmacs
,
ARRAY_SIZE
(
exynos4_dmacs
));
amba_device_register
(
&
exynos4_device_pdma0
,
&
iomem_resource
);
amba_device_register
(
&
exynos4_device_pdma1
,
&
iomem_resource
);
return
0
;
}
...
...
arch/arm/mach-exynos4/include/mach/dma.h
View file @
59ca37f7
...
...
@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H
#define __MACH_DMA_H
/* This platform uses the common
S3C
DMA API driver for PL330 */
#include <plat/
s3c-
dma-pl330.h>
/* This platform uses the common DMA API driver for PL330 */
#include <plat/dma-pl330.h>
#endif
/* __MACH_DMA_H */
arch/arm/mach-s3c2410/include/mach/dma.h
View file @
59ca37f7
...
...
@@ -13,7 +13,6 @@
#ifndef __ASM_ARCH_DMA_H
#define __ASM_ARCH_DMA_H __FILE__
#include <plat/dma.h>
#include <linux/sysdev.h>
#define MAX_DMA_TRANSFER_SIZE 0x100000
/* Data Unit is half word */
...
...
@@ -51,6 +50,18 @@ enum dma_ch {
DMACH_MAX
,
/* the end entry */
};
static
inline
bool
samsung_dma_has_circular
(
void
)
{
return
false
;
}
static
inline
bool
samsung_dma_is_dmadev
(
void
)
{
return
false
;
}
#include <plat/dma.h>
#define DMACH_LOW_LEVEL (1<<28)
/* use this to specifiy hardware ch no */
/* we have 4 dma channels */
...
...
@@ -163,7 +174,7 @@ struct s3c2410_dma_chan {
struct
s3c2410_dma_client
*
client
;
/* channel configuration */
enum
s3c2410_dmasrc
source
;
enum
dma_data_direction
source
;
enum
dma_ch
req_ch
;
unsigned
long
dev_addr
;
unsigned
long
load_timeout
;
...
...
@@ -196,9 +207,4 @@ struct s3c2410_dma_chan {
typedef
unsigned
long
dma_device_t
;
static
inline
bool
s3c_dma_has_circular
(
void
)
{
return
false
;
}
#endif
/* __ASM_ARCH_DMA_H */
arch/arm/mach-s3c2412/dma.c
View file @
59ca37f7
...
...
@@ -148,11 +148,11 @@ static struct s3c24xx_dma_map __initdata s3c2412_dma_mappings[] = {
static
void
s3c2412_dma_direction
(
struct
s3c2410_dma_chan
*
chan
,
struct
s3c24xx_dma_map
*
map
,
enum
s3c2410_dmasrc
dir
)
enum
dma_data_direction
dir
)
{
unsigned
long
chsel
;
if
(
dir
==
S3C2410_DMASRC_HW
)
if
(
dir
==
DMA_FROM_DEVICE
)
chsel
=
map
->
channels_rx
[
0
];
else
chsel
=
map
->
channels
[
0
];
...
...
arch/arm/mach-s3c64xx/dma.c
View file @
59ca37f7
...
...
@@ -147,14 +147,14 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan,
u32
control0
,
control1
;
switch
(
chan
->
source
)
{
case
S3C2410_DMASRC_HW
:
case
DMA_FROM_DEVICE
:
src
=
chan
->
dev_addr
;
dst
=
data
;
control0
=
PL080_CONTROL_SRC_AHB2
;
control0
|=
PL080_CONTROL_DST_INCR
;
break
;
case
S3C2410_DMASRC_MEM
:
case
DMA_TO_DEVICE
:
src
=
data
;
dst
=
chan
->
dev_addr
;
control0
=
PL080_CONTROL_DST_AHB2
;
...
...
@@ -416,7 +416,7 @@ EXPORT_SYMBOL(s3c2410_dma_enqueue);
int
s3c2410_dma_devconfig
(
enum
dma_ch
channel
,
enum
s3c2410_dmasrc
source
,
enum
dma_data_direction
source
,
unsigned
long
devaddr
)
{
struct
s3c2410_dma_chan
*
chan
=
s3c_dma_lookup_channel
(
channel
);
...
...
@@ -437,11 +437,11 @@ int s3c2410_dma_devconfig(enum dma_ch channel,
pr_debug
(
"%s: peripheral %d
\n
"
,
__func__
,
peripheral
);
switch
(
source
)
{
case
S3C2410_DMASRC_HW
:
case
DMA_FROM_DEVICE
:
config
=
2
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
config
|=
peripheral
<<
PL080_CONFIG_SRC_SEL_SHIFT
;
break
;
case
S3C2410_DMASRC_MEM
:
case
DMA_TO_DEVICE
:
config
=
1
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
config
|=
peripheral
<<
PL080_CONFIG_DST_SEL_SHIFT
;
break
;
...
...
arch/arm/mach-s3c64xx/include/mach/dma.h
View file @
59ca37f7
...
...
@@ -58,11 +58,15 @@ enum dma_ch {
DMACH_MAX
/* the end */
};
static
__inline__
bool
s3c
_dma_has_circular
(
void
)
static
inline
bool
samsung
_dma_has_circular
(
void
)
{
return
true
;
}
static
inline
bool
samsung_dma_is_dmadev
(
void
)
{
return
false
;
}
#define S3C2410_DMAF_CIRCULAR (1 << 0)
#include <plat/dma.h>
...
...
@@ -95,7 +99,7 @@ struct s3c2410_dma_chan {
unsigned
char
peripheral
;
unsigned
int
flags
;
enum
s3c2410_dmasrc
source
;
enum
dma_data_direction
source
;
dma_addr_t
dev_addr
;
...
...
arch/arm/mach-s5p64x0/Kconfig
View file @
59ca37f7
...
...
@@ -9,14 +9,14 @@ if ARCH_S5P64X0
config CPU_S5P6440
bool
select S
3C_PL330_DMA
select S
AMSUNG_DMADEV
select S5P_HRT
help
Enable S5P6440 CPU support
config CPU_S5P6450
bool
select S
3C_PL330_DMA
select S
AMSUNG_DMADEV
select S5P_HRT
help
Enable S5P6450 CPU support
...
...
arch/arm/mach-s5p64x0/clock-s5p6440.c
View file @
59ca37f7
...
...
@@ -146,7 +146,8 @@ static struct clk init_clocks_off[] = {
.
enable
=
s5p64x0_hclk0_ctrl
,
.
ctrlbit
=
(
1
<<
8
),
},
{
.
name
=
"pdma"
,
.
name
=
"dma"
,
.
devname
=
"dma-pl330"
,
.
parent
=
&
clk_hclk_low
.
clk
,
.
enable
=
s5p64x0_hclk0_ctrl
,
.
ctrlbit
=
(
1
<<
12
),
...
...
@@ -499,6 +500,11 @@ static struct clksrc_clk *sysclks[] = {
&
clk_pclk_low
,
};
static
struct
clk
dummy_apb_pclk
=
{
.
name
=
"apb_pclk"
,
.
id
=
-
1
,
};
void
__init_or_cpufreq
s5p6440_setup_clocks
(
void
)
{
struct
clk
*
xtal_clk
;
...
...
@@ -581,5 +587,7 @@ void __init s5p6440_register_clocks(void)
s3c_register_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c_disable_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c24xx_register_clock
(
&
dummy_apb_pclk
);
s3c_pwmclk_init
();
}
arch/arm/mach-s5p64x0/clock-s5p6450.c
View file @
59ca37f7
...
...
@@ -179,7 +179,8 @@ static struct clk init_clocks_off[] = {
.
enable
=
s5p64x0_hclk0_ctrl
,
.
ctrlbit
=
(
1
<<
3
),
},
{
.
name
=
"pdma"
,
.
name
=
"dma"
,
.
devname
=
"dma-pl330"
,
.
parent
=
&
clk_hclk_low
.
clk
,
.
enable
=
s5p64x0_hclk0_ctrl
,
.
ctrlbit
=
(
1
<<
12
),
...
...
@@ -553,6 +554,11 @@ static struct clksrc_clk *sysclks[] = {
&
clk_sclk_audio0
,
};
static
struct
clk
dummy_apb_pclk
=
{
.
name
=
"apb_pclk"
,
.
id
=
-
1
,
};
void
__init_or_cpufreq
s5p6450_setup_clocks
(
void
)
{
struct
clk
*
xtal_clk
;
...
...
@@ -632,5 +638,7 @@ void __init s5p6450_register_clocks(void)
s3c_register_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c_disable_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c24xx_register_clock
(
&
dummy_apb_pclk
);
s3c_pwmclk_init
();
}
arch/arm/mach-s5p64x0/dma.c
View file @
59ca37f7
...
...
@@ -21,128 +21,219 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
#include <asm/irq.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <mach/regs-clock.h>
#include <mach/dma.h>
#include <plat/devs.h>
#include <plat/
s3c-pl330-pdata
.h>
#include <plat/
irqs
.h>
static
u64
dma_dmamask
=
DMA_BIT_MASK
(
32
);
static
struct
resource
s5p64x0_pdma_resource
[]
=
{
[
0
]
=
{
.
start
=
S5P64X0_PA_PDMA
,
.
end
=
S5P64X0_PA_PDMA
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_DMA0
,
.
end
=
IRQ_DMA0
,
.
flags
=
IORESOURCE_IRQ
,
struct
dma_pl330_peri
s5p6440_pdma_peri
[
22
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
DMACH_MAX
,
},
{
.
peri_id
=
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
};
static
struct
s3c_pl330_platdata
s5p6440_pdma_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_UART0_RX
,
[
1
]
=
DMACH_UART0_TX
,
[
2
]
=
DMACH_UART1_RX
,
[
3
]
=
DMACH_UART1_TX
,
[
4
]
=
DMACH_UART2_RX
,
[
5
]
=
DMACH_UART2_TX
,
[
6
]
=
DMACH_UART3_RX
,
[
7
]
=
DMACH_UART3_TX
,
[
8
]
=
DMACH_MAX
,
[
9
]
=
DMACH_MAX
,
[
10
]
=
DMACH_PCM0_TX
,
[
11
]
=
DMACH_PCM0_RX
,
[
12
]
=
DMACH_I2S0_TX
,
[
13
]
=
DMACH_I2S0_RX
,
[
14
]
=
DMACH_SPI0_TX
,
[
15
]
=
DMACH_SPI0_RX
,
[
16
]
=
DMACH_MAX
,
[
17
]
=
DMACH_MAX
,
[
18
]
=
DMACH_MAX
,
[
19
]
=
DMACH_MAX
,
[
20
]
=
DMACH_SPI1_TX
,
[
21
]
=
DMACH_SPI1_RX
,
[
22
]
=
DMACH_MAX
,
[
23
]
=
DMACH_MAX
,
[
24
]
=
DMACH_MAX
,
[
25
]
=
DMACH_MAX
,
[
26
]
=
DMACH_MAX
,
[
27
]
=
DMACH_MAX
,
[
28
]
=
DMACH_MAX
,
[
29
]
=
DMACH_PWM
,
[
30
]
=
DMACH_MAX
,
[
31
]
=
DMACH_MAX
,
},
struct
dma_pl330_platdata
s5p6440_pdma_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
s5p6440_pdma_peri
),
.
peri
=
s5p6440_pdma_peri
,
};
static
struct
s3c_pl330_platdata
s5p6450_pdma_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_UART0_RX
,
[
1
]
=
DMACH_UART0_TX
,
[
2
]
=
DMACH_UART1_RX
,
[
3
]
=
DMACH_UART1_TX
,
[
4
]
=
DMACH_UART2_RX
,
[
5
]
=
DMACH_UART2_TX
,
[
6
]
=
DMACH_UART3_RX
,
[
7
]
=
DMACH_UART3_TX
,
[
8
]
=
DMACH_UART4_RX
,
[
9
]
=
DMACH_UART4_TX
,
[
10
]
=
DMACH_PCM0_TX
,
[
11
]
=
DMACH_PCM0_RX
,
[
12
]
=
DMACH_I2S0_TX
,
[
13
]
=
DMACH_I2S0_RX
,
[
14
]
=
DMACH_SPI0_TX
,
[
15
]
=
DMACH_SPI0_RX
,
[
16
]
=
DMACH_PCM1_TX
,
[
17
]
=
DMACH_PCM1_RX
,
[
18
]
=
DMACH_PCM2_TX
,
[
19
]
=
DMACH_PCM2_RX
,
[
20
]
=
DMACH_SPI1_TX
,
[
21
]
=
DMACH_SPI1_RX
,
[
22
]
=
DMACH_USI_TX
,
[
23
]
=
DMACH_USI_RX
,
[
24
]
=
DMACH_MAX
,
[
25
]
=
DMACH_I2S1_TX
,
[
26
]
=
DMACH_I2S1_RX
,
[
27
]
=
DMACH_I2S2_TX
,
[
28
]
=
DMACH_I2S2_RX
,
[
29
]
=
DMACH_PWM
,
[
30
]
=
DMACH_UART5_RX
,
[
31
]
=
DMACH_UART5_TX
,
struct
dma_pl330_peri
s5p6450_pdma_peri
[
32
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART4_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART4_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_USI_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_USI_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PWM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART5_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART5_TX
,
.
rqtype
=
MEMTODEV
,
},
};
static
struct
platform_device
s5p64x0_device_pdma
=
{
.
name
=
"s3c-pl330"
,
.
id
=
-
1
,
.
num_resources
=
ARRAY_SIZE
(
s5p64x0_pdma_resource
),
.
resource
=
s5p64x0_pdma_resource
,
struct
dma_pl330_platdata
s5p6450_pdma_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
s5p6450_pdma_peri
),
.
peri
=
s5p6450_pdma_peri
,
};
struct
amba_device
s5p64x0_device_pdma
=
{
.
dev
=
{
.
init_name
=
"dma-pl330"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
},
.
res
=
{
.
start
=
S5P64X0_PA_PDMA
,
.
end
=
S5P64X0_PA_PDMA
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
.
irq
=
{
IRQ_DMA0
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
int
__init
s5p64x0_dma_init
(
void
)
{
unsigned
int
id
;
id
=
__raw_readl
(
S5P64X0_SYS_ID
)
&
0xFF000
;
unsigned
int
id
=
__raw_readl
(
S5P64X0_SYS_ID
)
&
0xFF000
;
if
(
id
==
0x50000
)
s5p64x0_device_pdma
.
dev
.
platform_data
=
&
s5p6450_pdma_pdata
;
else
s5p64x0_device_pdma
.
dev
.
platform_data
=
&
s5p6440_pdma_pdata
;
platform_device_register
(
&
s5p64x0_device_pdma
);
amba_device_register
(
&
s5p64x0_device_pdma
,
&
iomem_resource
);
return
0
;
}
...
...
arch/arm/mach-s5p64x0/include/mach/dma.h
View file @
59ca37f7
...
...
@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H
#define __MACH_DMA_H
/* This platform uses the common
S3C
DMA API driver for PL330 */
#include <plat/
s3c-
dma-pl330.h>
/* This platform uses the common
common
DMA API driver for PL330 */
#include <plat/dma-pl330.h>
#endif
/* __MACH_DMA_H */
arch/arm/mach-s5pc100/Kconfig
View file @
59ca37f7
...
...
@@ -10,7 +10,7 @@ if ARCH_S5PC100
config CPU_S5PC100
bool
select S5P_EXT_INT
select S
3C_PL330_DMA
select S
AMSUNG_DMADEV
help
Enable S5PC100 CPU support
...
...
arch/arm/mach-s5pc100/clock.c
View file @
59ca37f7
...
...
@@ -33,6 +33,11 @@ static struct clk s5p_clk_otgphy = {
.
name
=
"otg_phy"
,
};
static
struct
clk
dummy_apb_pclk
=
{
.
name
=
"apb_pclk"
,
.
id
=
-
1
,
};
static
struct
clk
*
clk_src_mout_href_list
[]
=
{
[
0
]
=
&
s5p_clk_27m
,
[
1
]
=
&
clk_fin_hpll
,
...
...
@@ -454,14 +459,14 @@ static struct clk init_clocks_off[] = {
.
enable
=
s5pc100_d1_0_ctrl
,
.
ctrlbit
=
(
1
<<
2
),
},
{
.
name
=
"
p
dma"
,
.
devname
=
"
s3c
-pl330.1"
,
.
name
=
"dma"
,
.
devname
=
"
dma
-pl330.1"
,
.
parent
=
&
clk_div_d1_bus
.
clk
,
.
enable
=
s5pc100_d1_0_ctrl
,
.
ctrlbit
=
(
1
<<
1
),
},
{
.
name
=
"
p
dma"
,
.
devname
=
"
s3c
-pl330.0"
,
.
name
=
"dma"
,
.
devname
=
"
dma
-pl330.0"
,
.
parent
=
&
clk_div_d1_bus
.
clk
,
.
enable
=
s5pc100_d1_0_ctrl
,
.
ctrlbit
=
(
1
<<
0
),
...
...
@@ -1276,5 +1281,7 @@ void __init s5pc100_register_clocks(void)
s3c_register_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c_disable_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c24xx_register_clock
(
&
dummy_apb_pclk
);
s3c_pwmclk_init
();
}
arch/arm/mach-s5pc100/dma.c
View file @
59ca37f7
/*
/* linux/arch/arm/mach-s5pc100/dma.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
...
...
@@ -17,150 +21,246 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
#include <asm/irq.h>
#include <plat/devs.h>
#include <plat/irqs.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <plat/s3c-pl330-pdata.h>
#include <mach/dma.h>
static
u64
dma_dmamask
=
DMA_BIT_MASK
(
32
);
static
struct
resource
s5pc100_pdma0_resource
[]
=
{
[
0
]
=
{
.
start
=
S5PC100_PA_PDMA0
,
.
end
=
S5PC100_PA_PDMA0
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_PDMA0
,
.
end
=
IRQ_PDMA0
,
.
flags
=
IORESOURCE_IRQ
,
struct
dma_pl330_peri
pdma0_peri
[
30
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
DMACH_IRDA
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0S_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_MICIN
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_PCMIN
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_PCMOUT
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_EXTERNAL
,
},
{
.
peri_id
=
(
u8
)
DMACH_PWM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPDIF
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_HSI_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_HSI_TX
,
.
rqtype
=
MEMTODEV
,
},
};
static
struct
s3c_pl330_platdata
s5pc100_pdma0_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_UART0_RX
,
[
1
]
=
DMACH_UART0_TX
,
[
2
]
=
DMACH_UART1_RX
,
[
3
]
=
DMACH_UART1_TX
,
[
4
]
=
DMACH_UART2_RX
,
[
5
]
=
DMACH_UART2_TX
,
[
6
]
=
DMACH_UART3_RX
,
[
7
]
=
DMACH_UART3_TX
,
[
8
]
=
DMACH_IRDA
,
[
9
]
=
DMACH_I2S0_RX
,
[
10
]
=
DMACH_I2S0_TX
,
[
11
]
=
DMACH_I2S0S_TX
,
[
12
]
=
DMACH_I2S1_RX
,
[
13
]
=
DMACH_I2S1_TX
,
[
14
]
=
DMACH_I2S2_RX
,
[
15
]
=
DMACH_I2S2_TX
,
[
16
]
=
DMACH_SPI0_RX
,
[
17
]
=
DMACH_SPI0_TX
,
[
18
]
=
DMACH_SPI1_RX
,
[
19
]
=
DMACH_SPI1_TX
,
[
20
]
=
DMACH_SPI2_RX
,
[
21
]
=
DMACH_SPI2_TX
,
[
22
]
=
DMACH_AC97_MICIN
,
[
23
]
=
DMACH_AC97_PCMIN
,
[
24
]
=
DMACH_AC97_PCMOUT
,
[
25
]
=
DMACH_EXTERNAL
,
[
26
]
=
DMACH_PWM
,
[
27
]
=
DMACH_SPDIF
,
[
28
]
=
DMACH_HSI_RX
,
[
29
]
=
DMACH_HSI_TX
,
[
30
]
=
DMACH_MAX
,
[
31
]
=
DMACH_MAX
,
},
struct
dma_pl330_platdata
s5pc100_pdma0_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
pdma0_peri
),
.
peri
=
pdma0_peri
,
};
static
struct
platform_device
s5pc100_device_pdma0
=
{
.
name
=
"s3c-pl330"
,
.
id
=
0
,
.
num_resources
=
ARRAY_SIZE
(
s5pc100_pdma0_resource
),
.
resource
=
s5pc100_pdma0_resource
,
struct
amba_device
s5pc100_device_pdma0
=
{
.
dev
=
{
.
init_name
=
"dma-pl330.0"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
.
platform_data
=
&
s5pc100_pdma0_pdata
,
},
};
static
struct
resource
s5pc100_pdma1_resource
[]
=
{
[
0
]
=
{
.
start
=
S5PC100_PA_PDMA1
,
.
end
=
S5PC100_PA_PDMA1
+
SZ_4K
,
.
res
=
{
.
start
=
S5PC100_PA_PDMA0
,
.
end
=
S5PC100_PA_PDMA0
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_PDMA1
,
.
end
=
IRQ_PDMA1
,
.
flags
=
IORESOURCE_IRQ
,
},
.
irq
=
{
IRQ_PDMA0
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
struct
s3c_pl330_platdata
s5pc100_pdma1_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_UART0_RX
,
[
1
]
=
DMACH_UART0_TX
,
[
2
]
=
DMACH_UART1_RX
,
[
3
]
=
DMACH_UART1_TX
,
[
4
]
=
DMACH_UART2_RX
,
[
5
]
=
DMACH_UART2_TX
,
[
6
]
=
DMACH_UART3_RX
,
[
7
]
=
DMACH_UART3_TX
,
[
8
]
=
DMACH_IRDA
,
[
9
]
=
DMACH_I2S0_RX
,
[
10
]
=
DMACH_I2S0_TX
,
[
11
]
=
DMACH_I2S0S_TX
,
[
12
]
=
DMACH_I2S1_RX
,
[
13
]
=
DMACH_I2S1_TX
,
[
14
]
=
DMACH_I2S2_RX
,
[
15
]
=
DMACH_I2S2_TX
,
[
16
]
=
DMACH_SPI0_RX
,
[
17
]
=
DMACH_SPI0_TX
,
[
18
]
=
DMACH_SPI1_RX
,
[
19
]
=
DMACH_SPI1_TX
,
[
20
]
=
DMACH_SPI2_RX
,
[
21
]
=
DMACH_SPI2_TX
,
[
22
]
=
DMACH_PCM0_RX
,
[
23
]
=
DMACH_PCM0_TX
,
[
24
]
=
DMACH_PCM1_RX
,
[
25
]
=
DMACH_PCM1_TX
,
[
26
]
=
DMACH_MSM_REQ0
,
[
27
]
=
DMACH_MSM_REQ1
,
[
28
]
=
DMACH_MSM_REQ2
,
[
29
]
=
DMACH_MSM_REQ3
,
[
30
]
=
DMACH_MAX
,
[
31
]
=
DMACH_MAX
,
struct
dma_pl330_peri
pdma1_peri
[
30
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
DMACH_IRDA
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0S_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ0
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ1
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ2
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ3
,
},
};
static
struct
platform_device
s5pc100_device_pdma1
=
{
.
name
=
"s3c-pl330"
,
.
id
=
1
,
.
num_resources
=
ARRAY_SIZE
(
s5pc100_pdma1_resource
),
.
resource
=
s5pc100_pdma1_resource
,
struct
dma_pl330_platdata
s5pc100_pdma1_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
pdma1_peri
),
.
peri
=
pdma1_peri
,
};
struct
amba_device
s5pc100_device_pdma1
=
{
.
dev
=
{
.
init_name
=
"dma-pl330.1"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
.
platform_data
=
&
s5pc100_pdma1_pdata
,
},
};
static
struct
platform_device
*
s5pc100_dmacs
[]
__initdata
=
{
&
s5pc100_device_pdma0
,
&
s5pc100_device_pdma1
,
.
res
=
{
.
start
=
S5PC100_PA_PDMA1
,
.
end
=
S5PC100_PA_PDMA1
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
.
irq
=
{
IRQ_PDMA1
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
int
__init
s5pc100_dma_init
(
void
)
{
platform_add_devices
(
s5pc100_dmacs
,
ARRAY_SIZE
(
s5pc100_dmacs
));
amba_device_register
(
&
s5pc100_device_pdma0
,
&
iomem_resource
);
amba_device_register
(
&
s5pc100_device_pdma1
,
&
iomem_resource
);
return
0
;
}
...
...
arch/arm/mach-s5pc100/include/mach/dma.h
View file @
59ca37f7
...
...
@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H
#define __MACH_DMA_H
/* This platform uses the common
S3C
DMA API driver for PL330 */
#include <plat/
s3c-
dma-pl330.h>
/* This platform uses the common DMA API driver for PL330 */
#include <plat/dma-pl330.h>
#endif
/* __MACH_DMA_H */
arch/arm/mach-s5pv210/Kconfig
View file @
59ca37f7
...
...
@@ -11,7 +11,7 @@ if ARCH_S5PV210
config CPU_S5PV210
bool
select S
3C_PL330_DMA
select S
AMSUNG_DMADEV
select S5P_EXT_INT
select S5P_HRT
select S5PV210_PM if PM
...
...
arch/arm/mach-s5pv210/clock.c
View file @
59ca37f7
...
...
@@ -203,6 +203,11 @@ static struct clk clk_pcmcdclk2 = {
.
name
=
"pcmcdclk"
,
};
static
struct
clk
dummy_apb_pclk
=
{
.
name
=
"apb_pclk"
,
.
id
=
-
1
,
};
static
struct
clk
*
clkset_vpllsrc_list
[]
=
{
[
0
]
=
&
clk_fin_vpll
,
[
1
]
=
&
clk_sclk_hdmi27m
,
...
...
@@ -289,14 +294,14 @@ static struct clk_ops clk_fout_apll_ops = {
static
struct
clk
init_clocks_off
[]
=
{
{
.
name
=
"
p
dma"
,
.
devname
=
"
s3c
-pl330.0"
,
.
name
=
"dma"
,
.
devname
=
"
dma
-pl330.0"
,
.
parent
=
&
clk_hclk_psys
.
clk
,
.
enable
=
s5pv210_clk_ip0_ctrl
,
.
ctrlbit
=
(
1
<<
3
),
},
{
.
name
=
"
p
dma"
,
.
devname
=
"
s3c
-pl330.1"
,
.
name
=
"dma"
,
.
devname
=
"
dma
-pl330.1"
,
.
parent
=
&
clk_hclk_psys
.
clk
,
.
enable
=
s5pv210_clk_ip0_ctrl
,
.
ctrlbit
=
(
1
<<
4
),
...
...
@@ -1159,5 +1164,6 @@ void __init s5pv210_register_clocks(void)
s3c_register_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c_disable_clocks
(
init_clocks_off
,
ARRAY_SIZE
(
init_clocks_off
));
s3c24xx_register_clock
(
&
dummy_apb_pclk
);
s3c_pwmclk_init
();
}
arch/arm/mach-s5pv210/dma.c
View file @
59ca37f7
/*
/* linux/arch/arm/mach-s5pv210/dma.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
...
...
@@ -17,151 +21,240 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
#include <asm/irq.h>
#include <plat/devs.h>
#include <plat/irqs.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <plat/s3c-pl330-pdata.h>
#include <mach/dma.h>
static
u64
dma_dmamask
=
DMA_BIT_MASK
(
32
);
static
struct
resource
s5pv210_pdma0_resource
[]
=
{
[
0
]
=
{
.
start
=
S5PV210_PA_PDMA0
,
.
end
=
S5PV210_PA_PDMA0
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_PDMA0
,
.
end
=
IRQ_PDMA0
,
.
flags
=
IORESOURCE_IRQ
,
struct
dma_pl330_peri
pdma0_peri
[
28
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0S_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_MICIN
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_PCMIN
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_AC97_PCMOUT
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_PWM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPDIF
,
.
rqtype
=
MEMTODEV
,
},
};
static
struct
s3c_pl330_platdata
s5pv210_pdma0_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_UART0_RX
,
[
1
]
=
DMACH_UART0_TX
,
[
2
]
=
DMACH_UART1_RX
,
[
3
]
=
DMACH_UART1_TX
,
[
4
]
=
DMACH_UART2_RX
,
[
5
]
=
DMACH_UART2_TX
,
[
6
]
=
DMACH_UART3_RX
,
[
7
]
=
DMACH_UART3_TX
,
[
8
]
=
DMACH_MAX
,
[
9
]
=
DMACH_I2S0_RX
,
[
10
]
=
DMACH_I2S0_TX
,
[
11
]
=
DMACH_I2S0S_TX
,
[
12
]
=
DMACH_I2S1_RX
,
[
13
]
=
DMACH_I2S1_TX
,
[
14
]
=
DMACH_MAX
,
[
15
]
=
DMACH_MAX
,
[
16
]
=
DMACH_SPI0_RX
,
[
17
]
=
DMACH_SPI0_TX
,
[
18
]
=
DMACH_SPI1_RX
,
[
19
]
=
DMACH_SPI1_TX
,
[
20
]
=
DMACH_MAX
,
[
21
]
=
DMACH_MAX
,
[
22
]
=
DMACH_AC97_MICIN
,
[
23
]
=
DMACH_AC97_PCMIN
,
[
24
]
=
DMACH_AC97_PCMOUT
,
[
25
]
=
DMACH_MAX
,
[
26
]
=
DMACH_PWM
,
[
27
]
=
DMACH_SPDIF
,
[
28
]
=
DMACH_MAX
,
[
29
]
=
DMACH_MAX
,
[
30
]
=
DMACH_MAX
,
[
31
]
=
DMACH_MAX
,
},
struct
dma_pl330_platdata
s5pv210_pdma0_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
pdma0_peri
),
.
peri
=
pdma0_peri
,
};
static
struct
platform_device
s5pv210_device_pdma0
=
{
.
name
=
"s3c-pl330"
,
.
id
=
0
,
.
num_resources
=
ARRAY_SIZE
(
s5pv210_pdma0_resource
),
.
resource
=
s5pv210_pdma0_resource
,
struct
amba_device
s5pv210_device_pdma0
=
{
.
dev
=
{
.
init_name
=
"dma-pl330.0"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
.
platform_data
=
&
s5pv210_pdma0_pdata
,
},
};
static
struct
resource
s5pv210_pdma1_resource
[]
=
{
[
0
]
=
{
.
start
=
S5PV210_PA_PDMA1
,
.
end
=
S5PV210_PA_PDMA1
+
SZ_4K
,
.
res
=
{
.
start
=
S5PV210_PA_PDMA0
,
.
end
=
S5PV210_PA_PDMA0
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
[
1
]
=
{
.
start
=
IRQ_PDMA1
,
.
end
=
IRQ_PDMA1
,
.
flags
=
IORESOURCE_IRQ
,
},
.
irq
=
{
IRQ_PDMA0
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
struct
s3c_pl330_platdata
s5pv210_pdma1_pdata
=
{
.
peri
=
{
[
0
]
=
DMACH_UART0_RX
,
[
1
]
=
DMACH_UART0_TX
,
[
2
]
=
DMACH_UART1_RX
,
[
3
]
=
DMACH_UART1_TX
,
[
4
]
=
DMACH_UART2_RX
,
[
5
]
=
DMACH_UART2_TX
,
[
6
]
=
DMACH_UART3_RX
,
[
7
]
=
DMACH_UART3_TX
,
[
8
]
=
DMACH_MAX
,
[
9
]
=
DMACH_I2S0_RX
,
[
10
]
=
DMACH_I2S0_TX
,
[
11
]
=
DMACH_I2S0S_TX
,
[
12
]
=
DMACH_I2S1_RX
,
[
13
]
=
DMACH_I2S1_TX
,
[
14
]
=
DMACH_I2S2_RX
,
[
15
]
=
DMACH_I2S2_TX
,
[
16
]
=
DMACH_SPI0_RX
,
[
17
]
=
DMACH_SPI0_TX
,
[
18
]
=
DMACH_SPI1_RX
,
[
19
]
=
DMACH_SPI1_TX
,
[
20
]
=
DMACH_MAX
,
[
21
]
=
DMACH_MAX
,
[
22
]
=
DMACH_PCM0_RX
,
[
23
]
=
DMACH_PCM0_TX
,
[
24
]
=
DMACH_PCM1_RX
,
[
25
]
=
DMACH_PCM1_TX
,
[
26
]
=
DMACH_MSM_REQ0
,
[
27
]
=
DMACH_MSM_REQ1
,
[
28
]
=
DMACH_MSM_REQ2
,
[
29
]
=
DMACH_MSM_REQ3
,
[
30
]
=
DMACH_PCM2_RX
,
[
31
]
=
DMACH_PCM2_TX
,
struct
dma_pl330_peri
pdma1_peri
[
32
]
=
{
{
.
peri_id
=
(
u8
)
DMACH_UART0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_UART3_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S0S_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_I2S2_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_SPI1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_MAX
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM0_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM1_TX
,
.
rqtype
=
MEMTODEV
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ0
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ1
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ2
,
},
{
.
peri_id
=
(
u8
)
DMACH_MSM_REQ3
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM2_RX
,
.
rqtype
=
DEVTOMEM
,
},
{
.
peri_id
=
(
u8
)
DMACH_PCM2_TX
,
.
rqtype
=
MEMTODEV
,
},
};
static
struct
platform_device
s5pv210_device_pdma1
=
{
.
name
=
"s3c-pl330"
,
.
id
=
1
,
.
num_resources
=
ARRAY_SIZE
(
s5pv210_pdma1_resource
),
.
resource
=
s5pv210_pdma1_resource
,
struct
dma_pl330_platdata
s5pv210_pdma1_pdata
=
{
.
nr_valid_peri
=
ARRAY_SIZE
(
pdma1_peri
),
.
peri
=
pdma1_peri
,
};
struct
amba_device
s5pv210_device_pdma1
=
{
.
dev
=
{
.
init_name
=
"dma-pl330.1"
,
.
dma_mask
=
&
dma_dmamask
,
.
coherent_dma_mask
=
DMA_BIT_MASK
(
32
),
.
platform_data
=
&
s5pv210_pdma1_pdata
,
},
};
static
struct
platform_device
*
s5pv210_dmacs
[]
__initdata
=
{
&
s5pv210_device_pdma0
,
&
s5pv210_device_pdma1
,
.
res
=
{
.
start
=
S5PV210_PA_PDMA1
,
.
end
=
S5PV210_PA_PDMA1
+
SZ_4K
,
.
flags
=
IORESOURCE_MEM
,
},
.
irq
=
{
IRQ_PDMA1
,
NO_IRQ
},
.
periphid
=
0x00041330
,
};
static
int
__init
s5pv210_dma_init
(
void
)
{
platform_add_devices
(
s5pv210_dmacs
,
ARRAY_SIZE
(
s5pv210_dmacs
));
amba_device_register
(
&
s5pv210_device_pdma0
,
&
iomem_resource
);
amba_device_register
(
&
s5pv210_device_pdma1
,
&
iomem_resource
);
return
0
;
}
...
...
arch/arm/mach-s5pv210/include/mach/dma.h
View file @
59ca37f7
...
...
@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H
#define __MACH_DMA_H
/* This platform uses the common
S3C
DMA API driver for PL330 */
#include <plat/
s3c-
dma-pl330.h>
/* This platform uses the common DMA API driver for PL330 */
#include <plat/dma-pl330.h>
#endif
/* __MACH_DMA_H */
arch/arm/plat-s3c24xx/dma.c
View file @
59ca37f7
...
...
@@ -1094,14 +1094,14 @@ EXPORT_SYMBOL(s3c2410_dma_config);
*
* configure the dma source/destination hardware type and address
*
* source:
S3C2410_DMASRC_HW
: source is hardware
*
S3C2410_DMASRC_MEM
: source is memory
* source:
DMA_FROM_DEVICE
: source is hardware
*
DMA_TO_DEVICE
: source is memory
*
* devaddr: physical address of the source
*/
int
s3c2410_dma_devconfig
(
enum
dma_ch
channel
,
enum
s3c2410_dmasrc
source
,
enum
dma_data_direction
source
,
unsigned
long
devaddr
)
{
struct
s3c2410_dma_chan
*
chan
=
s3c_dma_lookup_channel
(
channel
);
...
...
@@ -1131,7 +1131,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel,
hwcfg
|=
S3C2410_DISRCC_INC
;
switch
(
source
)
{
case
S3C2410_DMASRC_HW
:
case
DMA_FROM_DEVICE
:
/* source is hardware */
pr_debug
(
"%s: hw source, devaddr=%08lx, hwcfg=%d
\n
"
,
__func__
,
devaddr
,
hwcfg
);
...
...
@@ -1142,7 +1142,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel,
chan
->
addr_reg
=
dma_regaddr
(
chan
,
S3C2410_DMA_DIDST
);
break
;
case
S3C2410_DMASRC_MEM
:
case
DMA_TO_DEVICE
:
/* source is memory */
pr_debug
(
"%s: mem source, devaddr=%08lx, hwcfg=%d
\n
"
,
__func__
,
devaddr
,
hwcfg
);
...
...
arch/arm/plat-samsung/Kconfig
View file @
59ca37f7
...
...
@@ -300,11 +300,14 @@ config S3C_DMA
help
Internal configuration for S3C DMA core
config S
3C_PL330_DMA
config S
AMSUNG_DMADEV
bool
select PL330
select DMADEVICES
select PL330_DMA if (CPU_EXYNOS4210 || CPU_S5PV210 || CPU_S5PC100 || \
CPU_S5P6450 || CPU_S5P6440)
select ARM_AMBA
help
S3C DMA API Driver
for PL330 DMAC.
Use DMA device engine
for PL330 DMAC.
comment "Power management"
...
...
arch/arm/plat-samsung/Makefile
View file @
59ca37f7
...
...
@@ -63,9 +63,9 @@ obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT) += dev-backlight.o
# DMA support
obj-$(CONFIG_S3C_DMA)
+=
dma.o
obj-$(CONFIG_S3C_DMA)
+=
dma.o
s3c-dma-ops.o
obj-$(CONFIG_S
3C_PL330_DMA)
+=
s3c-pl330
.o
obj-$(CONFIG_S
AMSUNG_DMADEV)
+=
dma-ops
.o
# PM support
...
...
arch/arm/plat-samsung/dma-ops.c
0 → 100644
View file @
59ca37f7
/* linux/arch/arm/plat-samsung/dma-ops.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung DMA Operations
*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/amba/pl330.h>
#include <linux/scatterlist.h>
#include <mach/dma.h>
static
inline
bool
pl330_filter
(
struct
dma_chan
*
chan
,
void
*
param
)
{
struct
dma_pl330_peri
*
peri
=
chan
->
private
;
return
peri
->
peri_id
==
(
unsigned
)
param
;
}
static
unsigned
samsung_dmadev_request
(
enum
dma_ch
dma_ch
,
struct
samsung_dma_info
*
info
)
{
struct
dma_chan
*
chan
;
dma_cap_mask_t
mask
;
struct
dma_slave_config
slave_config
;
dma_cap_zero
(
mask
);
dma_cap_set
(
info
->
cap
,
mask
);
chan
=
dma_request_channel
(
mask
,
pl330_filter
,
(
void
*
)
dma_ch
);
if
(
info
->
direction
==
DMA_FROM_DEVICE
)
{
memset
(
&
slave_config
,
0
,
sizeof
(
struct
dma_slave_config
));
slave_config
.
direction
=
info
->
direction
;
slave_config
.
src_addr
=
info
->
fifo
;
slave_config
.
src_addr_width
=
info
->
width
;
slave_config
.
src_maxburst
=
1
;
dmaengine_slave_config
(
chan
,
&
slave_config
);
}
else
if
(
info
->
direction
==
DMA_TO_DEVICE
)
{
memset
(
&
slave_config
,
0
,
sizeof
(
struct
dma_slave_config
));
slave_config
.
direction
=
info
->
direction
;
slave_config
.
dst_addr
=
info
->
fifo
;
slave_config
.
dst_addr_width
=
info
->
width
;
slave_config
.
dst_maxburst
=
1
;
dmaengine_slave_config
(
chan
,
&
slave_config
);
}
return
(
unsigned
)
chan
;
}
static
int
samsung_dmadev_release
(
unsigned
ch
,
struct
s3c2410_dma_client
*
client
)
{
dma_release_channel
((
struct
dma_chan
*
)
ch
);
return
0
;
}
static
int
samsung_dmadev_prepare
(
unsigned
ch
,
struct
samsung_dma_prep_info
*
info
)
{
struct
scatterlist
sg
;
struct
dma_chan
*
chan
=
(
struct
dma_chan
*
)
ch
;
struct
dma_async_tx_descriptor
*
desc
;
switch
(
info
->
cap
)
{
case
DMA_SLAVE
:
sg_init_table
(
&
sg
,
1
);
sg_dma_len
(
&
sg
)
=
info
->
len
;
sg_set_page
(
&
sg
,
pfn_to_page
(
PFN_DOWN
(
info
->
buf
)),
info
->
len
,
offset_in_page
(
info
->
buf
));
sg_dma_address
(
&
sg
)
=
info
->
buf
;
desc
=
chan
->
device
->
device_prep_slave_sg
(
chan
,
&
sg
,
1
,
info
->
direction
,
DMA_PREP_INTERRUPT
);
break
;
case
DMA_CYCLIC
:
desc
=
chan
->
device
->
device_prep_dma_cyclic
(
chan
,
info
->
buf
,
info
->
len
,
info
->
period
,
info
->
direction
);
break
;
default:
dev_err
(
&
chan
->
dev
->
device
,
"unsupported format
\n
"
);
return
-
EFAULT
;
}
if
(
!
desc
)
{
dev_err
(
&
chan
->
dev
->
device
,
"cannot prepare cyclic dma
\n
"
);
return
-
EFAULT
;
}
desc
->
callback
=
info
->
fp
;
desc
->
callback_param
=
info
->
fp_param
;
dmaengine_submit
((
struct
dma_async_tx_descriptor
*
)
desc
);
return
0
;
}
static
inline
int
samsung_dmadev_trigger
(
unsigned
ch
)
{
dma_async_issue_pending
((
struct
dma_chan
*
)
ch
);
return
0
;
}
static
inline
int
samsung_dmadev_flush
(
unsigned
ch
)
{
return
dmaengine_terminate_all
((
struct
dma_chan
*
)
ch
);
}
struct
samsung_dma_ops
dmadev_ops
=
{
.
request
=
samsung_dmadev_request
,
.
release
=
samsung_dmadev_release
,
.
prepare
=
samsung_dmadev_prepare
,
.
trigger
=
samsung_dmadev_trigger
,
.
started
=
NULL
,
.
flush
=
samsung_dmadev_flush
,
.
stop
=
samsung_dmadev_flush
,
};
void
*
samsung_dmadev_get_ops
(
void
)
{
return
&
dmadev_ops
;
}
EXPORT_SYMBOL
(
samsung_dmadev_get_ops
);
arch/arm/plat-samsung/include/plat/dma-ops.h
0 → 100644
View file @
59ca37f7
/* arch/arm/plat-samsung/include/plat/dma-ops.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung DMA support
*
* 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 __SAMSUNG_DMA_OPS_H_
#define __SAMSUNG_DMA_OPS_H_ __FILE__
#include <linux/dmaengine.h>
struct
samsung_dma_prep_info
{
enum
dma_transaction_type
cap
;
enum
dma_data_direction
direction
;
dma_addr_t
buf
;
unsigned
long
period
;
unsigned
long
len
;
void
(
*
fp
)(
void
*
data
);
void
*
fp_param
;
};
struct
samsung_dma_info
{
enum
dma_transaction_type
cap
;
enum
dma_data_direction
direction
;
enum
dma_slave_buswidth
width
;
dma_addr_t
fifo
;
struct
s3c2410_dma_client
*
client
;
};
struct
samsung_dma_ops
{
unsigned
(
*
request
)(
enum
dma_ch
ch
,
struct
samsung_dma_info
*
info
);
int
(
*
release
)(
unsigned
ch
,
struct
s3c2410_dma_client
*
client
);
int
(
*
prepare
)(
unsigned
ch
,
struct
samsung_dma_prep_info
*
info
);
int
(
*
trigger
)(
unsigned
ch
);
int
(
*
started
)(
unsigned
ch
);
int
(
*
flush
)(
unsigned
ch
);
int
(
*
stop
)(
unsigned
ch
);
};
extern
void
*
samsung_dmadev_get_ops
(
void
);
extern
void
*
s3c_dma_get_ops
(
void
);
static
inline
void
*
__samsung_dma_get_ops
(
void
)
{
if
(
samsung_dma_is_dmadev
())
return
samsung_dmadev_get_ops
();
else
return
s3c_dma_get_ops
();
}
/*
* samsung_dma_get_ops
* get the set of samsung dma operations
*/
#define samsung_dma_get_ops() __samsung_dma_get_ops()
#endif
/* __SAMSUNG_DMA_OPS_H_ */
arch/arm/plat-samsung/include/plat/
s3c-
dma-pl330.h
→
arch/arm/plat-samsung/include/plat/dma-pl330.h
View file @
59ca37f7
...
...
@@ -8,11 +8,8 @@
* (at your option) any later version.
*/
#ifndef __S3C_DMA_PL330_H_
#define __S3C_DMA_PL330_H_
#define S3C2410_DMAF_AUTOSTART (1 << 0)
#define S3C2410_DMAF_CIRCULAR (1 << 1)
#ifndef __DMA_PL330_H_
#define __DMA_PL330_H_ __FILE__
/*
* PL330 can assign any channel to communicate with
...
...
@@ -20,7 +17,7 @@
* For the sake of consistency across client drivers,
* We keep the channel names unchanged and only add
* missing peripherals are added.
* Order is not important since
S3C
PL330 API driver
* Order is not important since
DMA
PL330 API driver
* use these just as IDs.
*/
enum
dma_ch
{
...
...
@@ -88,11 +85,20 @@ enum dma_ch {
DMACH_MAX
,
};
static
inline
bool
s3c_dma_has_circular
(
void
)
struct
s3c2410_dma_client
{
char
*
name
;
};
static
inline
bool
samsung_dma_has_circular
(
void
)
{
return
true
;
}
static
inline
bool
samsung_dma_is_dmadev
(
void
)
{
return
true
;
}
#include <plat/dma.h>
#include <plat/dma
-ops
.h>
#endif
/* __
S3C_
DMA_PL330_H_ */
#endif
/* __DMA_PL330_H_ */
arch/arm/plat-samsung/include/plat/dma-s3c24xx.h
View file @
59ca37f7
...
...
@@ -47,7 +47,7 @@ struct s3c24xx_dma_selection {
void
(
*
direction
)(
struct
s3c2410_dma_chan
*
chan
,
struct
s3c24xx_dma_map
*
map
,
enum
s3c2410_dmasrc
dir
);
enum
dma_data_direction
dir
);
};
extern
int
s3c24xx_dma_init_map
(
struct
s3c24xx_dma_selection
*
sel
);
...
...
arch/arm/plat-samsung/include/plat/dma.h
View file @
59ca37f7
...
...
@@ -10,17 +10,14 @@
* published by the Free Software Foundation.
*/
#include <linux/dma-mapping.h>
enum
s3c2410_dma_buffresult
{
S3C2410_RES_OK
,
S3C2410_RES_ERR
,
S3C2410_RES_ABORT
};
enum
s3c2410_dmasrc
{
S3C2410_DMASRC_HW
,
/* source is memory */
S3C2410_DMASRC_MEM
/* source is hardware */
};
/* enum s3c2410_chan_op
*
* operation codes passed to the DMA code by the user, and also used
...
...
@@ -112,7 +109,7 @@ extern int s3c2410_dma_config(enum dma_ch channel, int xferunit);
*/
extern
int
s3c2410_dma_devconfig
(
enum
dma_ch
channel
,
enum
s3c2410_dmasrc
source
,
unsigned
long
devaddr
);
enum
dma_data_direction
source
,
unsigned
long
devaddr
);
/* s3c2410_dma_getposition
*
...
...
@@ -126,3 +123,4 @@ extern int s3c2410_dma_set_opfn(enum dma_ch, s3c2410_dma_opfn_t rtn);
extern
int
s3c2410_dma_set_buffdone_fn
(
enum
dma_ch
,
s3c2410_dma_cbfn_t
rtn
);
#include <plat/dma-ops.h>
arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
deleted
100644 → 0
View file @
7d84b3e9
/* linux/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
*
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
* 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.
*/
#ifndef __S3C_PL330_PDATA_H
#define __S3C_PL330_PDATA_H
#include <plat/s3c-dma-pl330.h>
/*
* Every PL330 DMAC has max 32 peripheral interfaces,
* of which some may be not be really used in your
* DMAC's configuration.
* Populate this array of 32 peri i/fs with relevant
* channel IDs for used peri i/f and DMACH_MAX for
* those unused.
*
* The platforms just need to provide this info
* to the S3C DMA API driver for PL330.
*/
struct
s3c_pl330_platdata
{
enum
dma_ch
peri
[
32
];
};
#endif
/* __S3C_PL330_PDATA_H */
arch/arm/plat-samsung/s3c-dma-ops.c
0 → 100644
View file @
59ca37f7
/* linux/arch/arm/plat-samsung/s3c-dma-ops.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung S3C-DMA Operations
*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <mach/dma.h>
struct
cb_data
{
void
(
*
fp
)
(
void
*
);
void
*
fp_param
;
unsigned
ch
;
struct
list_head
node
;
};
static
LIST_HEAD
(
dma_list
);
static
void
s3c_dma_cb
(
struct
s3c2410_dma_chan
*
channel
,
void
*
param
,
int
size
,
enum
s3c2410_dma_buffresult
res
)
{
struct
cb_data
*
data
=
param
;
data
->
fp
(
data
->
fp_param
);
}
static
unsigned
s3c_dma_request
(
enum
dma_ch
dma_ch
,
struct
samsung_dma_info
*
info
)
{
struct
cb_data
*
data
;
if
(
s3c2410_dma_request
(
dma_ch
,
info
->
client
,
NULL
)
<
0
)
{
s3c2410_dma_free
(
dma_ch
,
info
->
client
);
return
0
;
}
data
=
kzalloc
(
sizeof
(
struct
cb_data
),
GFP_KERNEL
);
data
->
ch
=
dma_ch
;
list_add_tail
(
&
data
->
node
,
&
dma_list
);
s3c2410_dma_devconfig
(
dma_ch
,
info
->
direction
,
info
->
fifo
);
if
(
info
->
cap
==
DMA_CYCLIC
)
s3c2410_dma_setflags
(
dma_ch
,
S3C2410_DMAF_CIRCULAR
);
s3c2410_dma_config
(
dma_ch
,
info
->
width
);
return
(
unsigned
)
dma_ch
;
}
static
int
s3c_dma_release
(
unsigned
ch
,
struct
s3c2410_dma_client
*
client
)
{
struct
cb_data
*
data
;
list_for_each_entry
(
data
,
&
dma_list
,
node
)
if
(
data
->
ch
==
ch
)
break
;
list_del
(
&
data
->
node
);
s3c2410_dma_free
(
ch
,
client
);
kfree
(
data
);
return
0
;
}
static
int
s3c_dma_prepare
(
unsigned
ch
,
struct
samsung_dma_prep_info
*
info
)
{
struct
cb_data
*
data
;
int
len
=
(
info
->
cap
==
DMA_CYCLIC
)
?
info
->
period
:
info
->
len
;
list_for_each_entry
(
data
,
&
dma_list
,
node
)
if
(
data
->
ch
==
ch
)
break
;
if
(
!
data
->
fp
)
{
s3c2410_dma_set_buffdone_fn
(
ch
,
s3c_dma_cb
);
data
->
fp
=
info
->
fp
;
data
->
fp_param
=
info
->
fp_param
;
}
s3c2410_dma_enqueue
(
ch
,
(
void
*
)
data
,
info
->
buf
,
len
);
return
0
;
}
static
inline
int
s3c_dma_trigger
(
unsigned
ch
)
{
return
s3c2410_dma_ctrl
(
ch
,
S3C2410_DMAOP_START
);
}
static
inline
int
s3c_dma_started
(
unsigned
ch
)
{
return
s3c2410_dma_ctrl
(
ch
,
S3C2410_DMAOP_STARTED
);
}
static
inline
int
s3c_dma_flush
(
unsigned
ch
)
{
return
s3c2410_dma_ctrl
(
ch
,
S3C2410_DMAOP_FLUSH
);
}
static
inline
int
s3c_dma_stop
(
unsigned
ch
)
{
return
s3c2410_dma_ctrl
(
ch
,
S3C2410_DMAOP_STOP
);
}
static
struct
samsung_dma_ops
s3c_dma_ops
=
{
.
request
=
s3c_dma_request
,
.
release
=
s3c_dma_release
,
.
prepare
=
s3c_dma_prepare
,
.
trigger
=
s3c_dma_trigger
,
.
started
=
s3c_dma_started
,
.
flush
=
s3c_dma_flush
,
.
stop
=
s3c_dma_stop
,
};
void
*
s3c_dma_get_ops
(
void
)
{
return
&
s3c_dma_ops
;
}
EXPORT_SYMBOL
(
s3c_dma_get_ops
);
arch/arm/plat-samsung/s3c-pl330.c
deleted
100644 → 0
View file @
7d84b3e9
/* linux/arch/arm/plat-samsung/s3c-pl330.c
*
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm/hardware/pl330.h>
#include <plat/s3c-pl330-pdata.h>
/**
* struct s3c_pl330_dmac - Logical representation of a PL330 DMAC.
* @busy_chan: Number of channels currently busy.
* @peri: List of IDs of peripherals this DMAC can work with.
* @node: To attach to the global list of DMACs.
* @pi: PL330 configuration info for the DMAC.
* @kmcache: Pool to quickly allocate xfers for all channels in the dmac.
* @clk: Pointer of DMAC operation clock.
*/
struct
s3c_pl330_dmac
{
unsigned
busy_chan
;
enum
dma_ch
*
peri
;
struct
list_head
node
;
struct
pl330_info
*
pi
;
struct
kmem_cache
*
kmcache
;
struct
clk
*
clk
;
};
/**
* struct s3c_pl330_xfer - A request submitted by S3C DMA clients.
* @token: Xfer ID provided by the client.
* @node: To attach to the list of xfers on a channel.
* @px: Xfer for PL330 core.
* @chan: Owner channel of this xfer.
*/
struct
s3c_pl330_xfer
{
void
*
token
;
struct
list_head
node
;
struct
pl330_xfer
px
;
struct
s3c_pl330_chan
*
chan
;
};
/**
* struct s3c_pl330_chan - Logical channel to communicate with
* a Physical peripheral.
* @pl330_chan_id: Token of a hardware channel thread of PL330 DMAC.
* NULL if the channel is available to be acquired.
* @id: ID of the peripheral that this channel can communicate with.
* @options: Options specified by the client.
* @sdaddr: Address provided via s3c2410_dma_devconfig.
* @node: To attach to the global list of channels.
* @lrq: Pointer to the last submitted pl330_req to PL330 core.
* @xfer_list: To manage list of xfers enqueued.
* @req: Two requests to communicate with the PL330 engine.
* @callback_fn: Callback function to the client.
* @rqcfg: Channel configuration for the xfers.
* @xfer_head: Pointer to the xfer to be next executed.
* @dmac: Pointer to the DMAC that manages this channel, NULL if the
* channel is available to be acquired.
* @client: Client of this channel. NULL if the
* channel is available to be acquired.
*/
struct
s3c_pl330_chan
{
void
*
pl330_chan_id
;
enum
dma_ch
id
;
unsigned
int
options
;
unsigned
long
sdaddr
;
struct
list_head
node
;
struct
pl330_req
*
lrq
;
struct
list_head
xfer_list
;
struct
pl330_req
req
[
2
];
s3c2410_dma_cbfn_t
callback_fn
;
struct
pl330_reqcfg
rqcfg
;
struct
s3c_pl330_xfer
*
xfer_head
;
struct
s3c_pl330_dmac
*
dmac
;
struct
s3c2410_dma_client
*
client
;
};
/* All DMACs in the platform */
static
LIST_HEAD
(
dmac_list
);
/* All channels to peripherals in the platform */
static
LIST_HEAD
(
chan_list
);
/*
* Since we add resources(DMACs and Channels) to the global pool,
* we need to guard access to the resources using a global lock
*/
static
DEFINE_SPINLOCK
(
res_lock
);
/* Returns the channel with ID 'id' in the chan_list */
static
struct
s3c_pl330_chan
*
id_to_chan
(
const
enum
dma_ch
id
)
{
struct
s3c_pl330_chan
*
ch
;
list_for_each_entry
(
ch
,
&
chan_list
,
node
)
if
(
ch
->
id
==
id
)
return
ch
;
return
NULL
;
}
/* Allocate a new channel with ID 'id' and add to chan_list */
static
void
chan_add
(
const
enum
dma_ch
id
)
{
struct
s3c_pl330_chan
*
ch
=
id_to_chan
(
id
);
/* Return if the channel already exists */
if
(
ch
)
return
;
ch
=
kmalloc
(
sizeof
(
*
ch
),
GFP_KERNEL
);
/* Return silently to work with other channels */
if
(
!
ch
)
return
;
ch
->
id
=
id
;
ch
->
dmac
=
NULL
;
list_add_tail
(
&
ch
->
node
,
&
chan_list
);
}
/* If the channel is not yet acquired by any client */
static
bool
chan_free
(
struct
s3c_pl330_chan
*
ch
)
{
if
(
!
ch
)
return
false
;
/* Channel points to some DMAC only when it's acquired */
return
ch
->
dmac
?
false
:
true
;
}
/*
* Returns 0 is peripheral i/f is invalid or not present on the dmac.
* Index + 1, otherwise.
*/
static
unsigned
iface_of_dmac
(
struct
s3c_pl330_dmac
*
dmac
,
enum
dma_ch
ch_id
)
{
enum
dma_ch
*
id
=
dmac
->
peri
;
int
i
;
/* Discount invalid markers */
if
(
ch_id
==
DMACH_MAX
)
return
0
;
for
(
i
=
0
;
i
<
PL330_MAX_PERI
;
i
++
)
if
(
id
[
i
]
==
ch_id
)
return
i
+
1
;
return
0
;
}
/* If all channel threads of the DMAC are busy */
static
inline
bool
dmac_busy
(
struct
s3c_pl330_dmac
*
dmac
)
{
struct
pl330_info
*
pi
=
dmac
->
pi
;
return
(
dmac
->
busy_chan
<
pi
->
pcfg
.
num_chan
)
?
false
:
true
;
}
/*
* Returns the number of free channels that
* can be handled by this dmac only.
*/
static
unsigned
ch_onlyby_dmac
(
struct
s3c_pl330_dmac
*
dmac
)
{
enum
dma_ch
*
id
=
dmac
->
peri
;
struct
s3c_pl330_dmac
*
d
;
struct
s3c_pl330_chan
*
ch
;
unsigned
found
,
count
=
0
;
enum
dma_ch
p
;
int
i
;
for
(
i
=
0
;
i
<
PL330_MAX_PERI
;
i
++
)
{
p
=
id
[
i
];
ch
=
id_to_chan
(
p
);
if
(
p
==
DMACH_MAX
||
!
chan_free
(
ch
))
continue
;
found
=
0
;
list_for_each_entry
(
d
,
&
dmac_list
,
node
)
{
if
(
d
!=
dmac
&&
iface_of_dmac
(
d
,
ch
->
id
))
{
found
=
1
;
break
;
}
}
if
(
!
found
)
count
++
;
}
return
count
;
}
/*
* Measure of suitability of 'dmac' handling 'ch'
*
* 0 indicates 'dmac' can not handle 'ch' either
* because it is not supported by the hardware or
* because all dmac channels are currently busy.
*
* >0 vlaue indicates 'dmac' has the capability.
* The bigger the value the more suitable the dmac.
*/
#define MAX_SUIT UINT_MAX
#define MIN_SUIT 0
static
unsigned
suitablility
(
struct
s3c_pl330_dmac
*
dmac
,
struct
s3c_pl330_chan
*
ch
)
{
struct
pl330_info
*
pi
=
dmac
->
pi
;
enum
dma_ch
*
id
=
dmac
->
peri
;
struct
s3c_pl330_dmac
*
d
;
unsigned
s
;
int
i
;
s
=
MIN_SUIT
;
/* If all the DMAC channel threads are busy */
if
(
dmac_busy
(
dmac
))
return
s
;
for
(
i
=
0
;
i
<
PL330_MAX_PERI
;
i
++
)
if
(
id
[
i
]
==
ch
->
id
)
break
;
/* If the 'dmac' can't talk to 'ch' */
if
(
i
==
PL330_MAX_PERI
)
return
s
;
s
=
MAX_SUIT
;
list_for_each_entry
(
d
,
&
dmac_list
,
node
)
{
/*
* If some other dmac can talk to this
* peri and has some channel free.
*/
if
(
d
!=
dmac
&&
iface_of_dmac
(
d
,
ch
->
id
)
&&
!
dmac_busy
(
d
))
{
s
=
0
;
break
;
}
}
if
(
s
)
return
s
;
s
=
100
;
/* Good if free chans are more, bad otherwise */
s
+=
(
pi
->
pcfg
.
num_chan
-
dmac
->
busy_chan
)
-
ch_onlyby_dmac
(
dmac
);
return
s
;
}
/* More than one DMAC may have capability to transfer data with the
* peripheral. This function assigns most suitable DMAC to manage the
* channel and hence communicate with the peripheral.
*/
static
struct
s3c_pl330_dmac
*
map_chan_to_dmac
(
struct
s3c_pl330_chan
*
ch
)
{
struct
s3c_pl330_dmac
*
d
,
*
dmac
=
NULL
;
unsigned
sn
,
sl
=
MIN_SUIT
;
list_for_each_entry
(
d
,
&
dmac_list
,
node
)
{
sn
=
suitablility
(
d
,
ch
);
if
(
sn
==
MAX_SUIT
)
return
d
;
if
(
sn
>
sl
)
dmac
=
d
;
}
return
dmac
;
}
/* Acquire the channel for peripheral 'id' */
static
struct
s3c_pl330_chan
*
chan_acquire
(
const
enum
dma_ch
id
)
{
struct
s3c_pl330_chan
*
ch
=
id_to_chan
(
id
);
struct
s3c_pl330_dmac
*
dmac
;
/* If the channel doesn't exist or is already acquired */
if
(
!
ch
||
!
chan_free
(
ch
))
{
ch
=
NULL
;
goto
acq_exit
;
}
dmac
=
map_chan_to_dmac
(
ch
);
/* If couldn't map */
if
(
!
dmac
)
{
ch
=
NULL
;
goto
acq_exit
;
}
dmac
->
busy_chan
++
;
ch
->
dmac
=
dmac
;
acq_exit:
return
ch
;
}
/* Delete xfer from the queue */
static
inline
void
del_from_queue
(
struct
s3c_pl330_xfer
*
xfer
)
{
struct
s3c_pl330_xfer
*
t
;
struct
s3c_pl330_chan
*
ch
;
int
found
;
if
(
!
xfer
)
return
;
ch
=
xfer
->
chan
;
/* Make sure xfer is in the queue */
found
=
0
;
list_for_each_entry
(
t
,
&
ch
->
xfer_list
,
node
)
if
(
t
==
xfer
)
{
found
=
1
;
break
;
}
if
(
!
found
)
return
;
/* If xfer is last entry in the queue */
if
(
xfer
->
node
.
next
==
&
ch
->
xfer_list
)
t
=
list_entry
(
ch
->
xfer_list
.
next
,
struct
s3c_pl330_xfer
,
node
);
else
t
=
list_entry
(
xfer
->
node
.
next
,
struct
s3c_pl330_xfer
,
node
);
/* If there was only one node left */
if
(
t
==
xfer
)
ch
->
xfer_head
=
NULL
;
else
if
(
ch
->
xfer_head
==
xfer
)
ch
->
xfer_head
=
t
;
list_del
(
&
xfer
->
node
);
}
/* Provides pointer to the next xfer in the queue.
* If CIRCULAR option is set, the list is left intact,
* otherwise the xfer is removed from the list.
* Forced delete 'pluck' can be set to override the CIRCULAR option.
*/
static
struct
s3c_pl330_xfer
*
get_from_queue
(
struct
s3c_pl330_chan
*
ch
,
int
pluck
)
{
struct
s3c_pl330_xfer
*
xfer
=
ch
->
xfer_head
;
if
(
!
xfer
)
return
NULL
;
/* If xfer is last entry in the queue */
if
(
xfer
->
node
.
next
==
&
ch
->
xfer_list
)
ch
->
xfer_head
=
list_entry
(
ch
->
xfer_list
.
next
,
struct
s3c_pl330_xfer
,
node
);
else
ch
->
xfer_head
=
list_entry
(
xfer
->
node
.
next
,
struct
s3c_pl330_xfer
,
node
);
if
(
pluck
||
!
(
ch
->
options
&
S3C2410_DMAF_CIRCULAR
))
del_from_queue
(
xfer
);
return
xfer
;
}
static
inline
void
add_to_queue
(
struct
s3c_pl330_chan
*
ch
,
struct
s3c_pl330_xfer
*
xfer
,
int
front
)
{
struct
pl330_xfer
*
xt
;
/* If queue empty */
if
(
ch
->
xfer_head
==
NULL
)
ch
->
xfer_head
=
xfer
;
xt
=
&
ch
->
xfer_head
->
px
;
/* If the head already submitted (CIRCULAR head) */
if
(
ch
->
options
&
S3C2410_DMAF_CIRCULAR
&&
(
xt
==
ch
->
req
[
0
].
x
||
xt
==
ch
->
req
[
1
].
x
))
ch
->
xfer_head
=
xfer
;
/* If this is a resubmission, it should go at the head */
if
(
front
)
{
ch
->
xfer_head
=
xfer
;
list_add
(
&
xfer
->
node
,
&
ch
->
xfer_list
);
}
else
{
list_add_tail
(
&
xfer
->
node
,
&
ch
->
xfer_list
);
}
}
static
inline
void
_finish_off
(
struct
s3c_pl330_xfer
*
xfer
,
enum
s3c2410_dma_buffresult
res
,
int
ffree
)
{
struct
s3c_pl330_chan
*
ch
;
if
(
!
xfer
)
return
;
ch
=
xfer
->
chan
;
/* Do callback */
if
(
ch
->
callback_fn
)
ch
->
callback_fn
(
NULL
,
xfer
->
token
,
xfer
->
px
.
bytes
,
res
);
/* Force Free or if buffer is not needed anymore */
if
(
ffree
||
!
(
ch
->
options
&
S3C2410_DMAF_CIRCULAR
))
kmem_cache_free
(
ch
->
dmac
->
kmcache
,
xfer
);
}
static
inline
int
s3c_pl330_submit
(
struct
s3c_pl330_chan
*
ch
,
struct
pl330_req
*
r
)
{
struct
s3c_pl330_xfer
*
xfer
;
int
ret
=
0
;
/* If already submitted */
if
(
r
->
x
)
return
0
;
xfer
=
get_from_queue
(
ch
,
0
);
if
(
xfer
)
{
r
->
x
=
&
xfer
->
px
;
/* Use max bandwidth for M<->M xfers */
if
(
r
->
rqtype
==
MEMTOMEM
)
{
struct
pl330_info
*
pi
=
xfer
->
chan
->
dmac
->
pi
;
int
burst
=
1
<<
ch
->
rqcfg
.
brst_size
;
u32
bytes
=
r
->
x
->
bytes
;
int
bl
;
bl
=
pi
->
pcfg
.
data_bus_width
/
8
;
bl
*=
pi
->
pcfg
.
data_buf_dep
;
bl
/=
burst
;
/* src/dst_burst_len can't be more than 16 */
if
(
bl
>
16
)
bl
=
16
;
while
(
bl
>
1
)
{
if
(
!
(
bytes
%
(
bl
*
burst
)))
break
;
bl
--
;
}
ch
->
rqcfg
.
brst_len
=
bl
;
}
else
{
ch
->
rqcfg
.
brst_len
=
1
;
}
ret
=
pl330_submit_req
(
ch
->
pl330_chan_id
,
r
);
/* If submission was successful */
if
(
!
ret
)
{
ch
->
lrq
=
r
;
/* latest submitted req */
return
0
;
}
r
->
x
=
NULL
;
/* If both of the PL330 ping-pong buffers filled */
if
(
ret
==
-
EAGAIN
)
{
dev_err
(
ch
->
dmac
->
pi
->
dev
,
"%s:%d!
\n
"
,
__func__
,
__LINE__
);
/* Queue back again */
add_to_queue
(
ch
,
xfer
,
1
);
ret
=
0
;
}
else
{
dev_err
(
ch
->
dmac
->
pi
->
dev
,
"%s:%d!
\n
"
,
__func__
,
__LINE__
);
_finish_off
(
xfer
,
S3C2410_RES_ERR
,
0
);
}
}
return
ret
;
}
static
void
s3c_pl330_rq
(
struct
s3c_pl330_chan
*
ch
,
struct
pl330_req
*
r
,
enum
pl330_op_err
err
)
{
unsigned
long
flags
;
struct
s3c_pl330_xfer
*
xfer
;
struct
pl330_xfer
*
xl
=
r
->
x
;
enum
s3c2410_dma_buffresult
res
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
r
->
x
=
NULL
;
s3c_pl330_submit
(
ch
,
r
);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
/* Map result to S3C DMA API */
if
(
err
==
PL330_ERR_NONE
)
res
=
S3C2410_RES_OK
;
else
if
(
err
==
PL330_ERR_ABORT
)
res
=
S3C2410_RES_ABORT
;
else
res
=
S3C2410_RES_ERR
;
/* If last request had some xfer */
if
(
xl
)
{
xfer
=
container_of
(
xl
,
struct
s3c_pl330_xfer
,
px
);
_finish_off
(
xfer
,
res
,
0
);
}
else
{
dev_info
(
ch
->
dmac
->
pi
->
dev
,
"%s:%d No Xfer?!
\n
"
,
__func__
,
__LINE__
);
}
}
static
void
s3c_pl330_rq0
(
void
*
token
,
enum
pl330_op_err
err
)
{
struct
pl330_req
*
r
=
token
;
struct
s3c_pl330_chan
*
ch
=
container_of
(
r
,
struct
s3c_pl330_chan
,
req
[
0
]);
s3c_pl330_rq
(
ch
,
r
,
err
);
}
static
void
s3c_pl330_rq1
(
void
*
token
,
enum
pl330_op_err
err
)
{
struct
pl330_req
*
r
=
token
;
struct
s3c_pl330_chan
*
ch
=
container_of
(
r
,
struct
s3c_pl330_chan
,
req
[
1
]);
s3c_pl330_rq
(
ch
,
r
,
err
);
}
/* Release an acquired channel */
static
void
chan_release
(
struct
s3c_pl330_chan
*
ch
)
{
struct
s3c_pl330_dmac
*
dmac
;
if
(
chan_free
(
ch
))
return
;
dmac
=
ch
->
dmac
;
ch
->
dmac
=
NULL
;
dmac
->
busy_chan
--
;
}
int
s3c2410_dma_ctrl
(
enum
dma_ch
id
,
enum
s3c2410_chan_op
op
)
{
struct
s3c_pl330_xfer
*
xfer
;
enum
pl330_chan_op
pl330op
;
struct
s3c_pl330_chan
*
ch
;
unsigned
long
flags
;
int
idx
,
ret
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
if
(
!
ch
||
chan_free
(
ch
))
{
ret
=
-
EINVAL
;
goto
ctrl_exit
;
}
switch
(
op
)
{
case
S3C2410_DMAOP_START
:
/* Make sure both reqs are enqueued */
idx
=
(
ch
->
lrq
==
&
ch
->
req
[
0
])
?
1
:
0
;
s3c_pl330_submit
(
ch
,
&
ch
->
req
[
idx
]);
s3c_pl330_submit
(
ch
,
&
ch
->
req
[
1
-
idx
]);
pl330op
=
PL330_OP_START
;
break
;
case
S3C2410_DMAOP_STOP
:
pl330op
=
PL330_OP_ABORT
;
break
;
case
S3C2410_DMAOP_FLUSH
:
pl330op
=
PL330_OP_FLUSH
;
break
;
case
S3C2410_DMAOP_PAUSE
:
case
S3C2410_DMAOP_RESUME
:
case
S3C2410_DMAOP_TIMEOUT
:
case
S3C2410_DMAOP_STARTED
:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
0
;
default:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
-
EINVAL
;
}
ret
=
pl330_chan_ctrl
(
ch
->
pl330_chan_id
,
pl330op
);
if
(
pl330op
==
PL330_OP_START
)
{
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
idx
=
(
ch
->
lrq
==
&
ch
->
req
[
0
])
?
1
:
0
;
/* Abort the current xfer */
if
(
ch
->
req
[
idx
].
x
)
{
xfer
=
container_of
(
ch
->
req
[
idx
].
x
,
struct
s3c_pl330_xfer
,
px
);
/* Drop xfer during FLUSH */
if
(
pl330op
==
PL330_OP_FLUSH
)
del_from_queue
(
xfer
);
ch
->
req
[
idx
].
x
=
NULL
;
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
_finish_off
(
xfer
,
S3C2410_RES_ABORT
,
pl330op
==
PL330_OP_FLUSH
?
1
:
0
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
}
/* Flush the whole queue */
if
(
pl330op
==
PL330_OP_FLUSH
)
{
if
(
ch
->
req
[
1
-
idx
].
x
)
{
xfer
=
container_of
(
ch
->
req
[
1
-
idx
].
x
,
struct
s3c_pl330_xfer
,
px
);
del_from_queue
(
xfer
);
ch
->
req
[
1
-
idx
].
x
=
NULL
;
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
_finish_off
(
xfer
,
S3C2410_RES_ABORT
,
1
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
}
/* Finish off the remaining in the queue */
xfer
=
ch
->
xfer_head
;
while
(
xfer
)
{
del_from_queue
(
xfer
);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
_finish_off
(
xfer
,
S3C2410_RES_ABORT
,
1
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
xfer
=
ch
->
xfer_head
;
}
}
ctrl_exit:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_ctrl
);
int
s3c2410_dma_enqueue
(
enum
dma_ch
id
,
void
*
token
,
dma_addr_t
addr
,
int
size
)
{
struct
s3c_pl330_chan
*
ch
;
struct
s3c_pl330_xfer
*
xfer
;
unsigned
long
flags
;
int
idx
,
ret
=
0
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
/* Error if invalid or free channel */
if
(
!
ch
||
chan_free
(
ch
))
{
ret
=
-
EINVAL
;
goto
enq_exit
;
}
/* Error if size is unaligned */
if
(
ch
->
rqcfg
.
brst_size
&&
size
%
(
1
<<
ch
->
rqcfg
.
brst_size
))
{
ret
=
-
EINVAL
;
goto
enq_exit
;
}
xfer
=
kmem_cache_alloc
(
ch
->
dmac
->
kmcache
,
GFP_ATOMIC
);
if
(
!
xfer
)
{
ret
=
-
ENOMEM
;
goto
enq_exit
;
}
xfer
->
token
=
token
;
xfer
->
chan
=
ch
;
xfer
->
px
.
bytes
=
size
;
xfer
->
px
.
next
=
NULL
;
/* Single request */
/* For S3C DMA API, direction is always fixed for all xfers */
if
(
ch
->
req
[
0
].
rqtype
==
MEMTODEV
)
{
xfer
->
px
.
src_addr
=
addr
;
xfer
->
px
.
dst_addr
=
ch
->
sdaddr
;
}
else
{
xfer
->
px
.
src_addr
=
ch
->
sdaddr
;
xfer
->
px
.
dst_addr
=
addr
;
}
add_to_queue
(
ch
,
xfer
,
0
);
/* Try submitting on either request */
idx
=
(
ch
->
lrq
==
&
ch
->
req
[
0
])
?
1
:
0
;
if
(
!
ch
->
req
[
idx
].
x
)
s3c_pl330_submit
(
ch
,
&
ch
->
req
[
idx
]);
else
s3c_pl330_submit
(
ch
,
&
ch
->
req
[
1
-
idx
]);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
if
(
ch
->
options
&
S3C2410_DMAF_AUTOSTART
)
s3c2410_dma_ctrl
(
id
,
S3C2410_DMAOP_START
);
return
0
;
enq_exit:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_enqueue
);
int
s3c2410_dma_request
(
enum
dma_ch
id
,
struct
s3c2410_dma_client
*
client
,
void
*
dev
)
{
struct
s3c_pl330_dmac
*
dmac
;
struct
s3c_pl330_chan
*
ch
;
unsigned
long
flags
;
int
ret
=
0
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
chan_acquire
(
id
);
if
(
!
ch
)
{
ret
=
-
EBUSY
;
goto
req_exit
;
}
dmac
=
ch
->
dmac
;
ch
->
pl330_chan_id
=
pl330_request_channel
(
dmac
->
pi
);
if
(
!
ch
->
pl330_chan_id
)
{
chan_release
(
ch
);
ret
=
-
EBUSY
;
goto
req_exit
;
}
ch
->
client
=
client
;
ch
->
options
=
0
;
/* Clear any option */
ch
->
callback_fn
=
NULL
;
/* Clear any callback */
ch
->
lrq
=
NULL
;
ch
->
rqcfg
.
brst_size
=
2
;
/* Default word size */
ch
->
rqcfg
.
swap
=
SWAP_NO
;
ch
->
rqcfg
.
scctl
=
SCCTRL0
;
/* Noncacheable and nonbufferable */
ch
->
rqcfg
.
dcctl
=
DCCTRL0
;
/* Noncacheable and nonbufferable */
ch
->
rqcfg
.
privileged
=
0
;
ch
->
rqcfg
.
insnaccess
=
0
;
/* Set invalid direction */
ch
->
req
[
0
].
rqtype
=
DEVTODEV
;
ch
->
req
[
1
].
rqtype
=
ch
->
req
[
0
].
rqtype
;
ch
->
req
[
0
].
cfg
=
&
ch
->
rqcfg
;
ch
->
req
[
1
].
cfg
=
ch
->
req
[
0
].
cfg
;
ch
->
req
[
0
].
peri
=
iface_of_dmac
(
dmac
,
id
)
-
1
;
/* Original index */
ch
->
req
[
1
].
peri
=
ch
->
req
[
0
].
peri
;
ch
->
req
[
0
].
token
=
&
ch
->
req
[
0
];
ch
->
req
[
0
].
xfer_cb
=
s3c_pl330_rq0
;
ch
->
req
[
1
].
token
=
&
ch
->
req
[
1
];
ch
->
req
[
1
].
xfer_cb
=
s3c_pl330_rq1
;
ch
->
req
[
0
].
x
=
NULL
;
ch
->
req
[
1
].
x
=
NULL
;
/* Reset xfer list */
INIT_LIST_HEAD
(
&
ch
->
xfer_list
);
ch
->
xfer_head
=
NULL
;
req_exit:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_request
);
int
s3c2410_dma_free
(
enum
dma_ch
id
,
struct
s3c2410_dma_client
*
client
)
{
struct
s3c_pl330_chan
*
ch
;
struct
s3c_pl330_xfer
*
xfer
;
unsigned
long
flags
;
int
ret
=
0
;
unsigned
idx
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
if
(
!
ch
||
chan_free
(
ch
))
goto
free_exit
;
/* Refuse if someone else wanted to free the channel */
if
(
ch
->
client
!=
client
)
{
ret
=
-
EBUSY
;
goto
free_exit
;
}
/* Stop any active xfer, Flushe the queue and do callbacks */
pl330_chan_ctrl
(
ch
->
pl330_chan_id
,
PL330_OP_FLUSH
);
/* Abort the submitted requests */
idx
=
(
ch
->
lrq
==
&
ch
->
req
[
0
])
?
1
:
0
;
if
(
ch
->
req
[
idx
].
x
)
{
xfer
=
container_of
(
ch
->
req
[
idx
].
x
,
struct
s3c_pl330_xfer
,
px
);
ch
->
req
[
idx
].
x
=
NULL
;
del_from_queue
(
xfer
);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
_finish_off
(
xfer
,
S3C2410_RES_ABORT
,
1
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
}
if
(
ch
->
req
[
1
-
idx
].
x
)
{
xfer
=
container_of
(
ch
->
req
[
1
-
idx
].
x
,
struct
s3c_pl330_xfer
,
px
);
ch
->
req
[
1
-
idx
].
x
=
NULL
;
del_from_queue
(
xfer
);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
_finish_off
(
xfer
,
S3C2410_RES_ABORT
,
1
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
}
/* Pluck and Abort the queued requests in order */
do
{
xfer
=
get_from_queue
(
ch
,
1
);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
_finish_off
(
xfer
,
S3C2410_RES_ABORT
,
1
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
}
while
(
xfer
);
ch
->
client
=
NULL
;
pl330_release_channel
(
ch
->
pl330_chan_id
);
ch
->
pl330_chan_id
=
NULL
;
chan_release
(
ch
);
free_exit:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_free
);
int
s3c2410_dma_config
(
enum
dma_ch
id
,
int
xferunit
)
{
struct
s3c_pl330_chan
*
ch
;
struct
pl330_info
*
pi
;
unsigned
long
flags
;
int
i
,
dbwidth
,
ret
=
0
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
if
(
!
ch
||
chan_free
(
ch
))
{
ret
=
-
EINVAL
;
goto
cfg_exit
;
}
pi
=
ch
->
dmac
->
pi
;
dbwidth
=
pi
->
pcfg
.
data_bus_width
/
8
;
/* Max size of xfer can be pcfg.data_bus_width */
if
(
xferunit
>
dbwidth
)
{
ret
=
-
EINVAL
;
goto
cfg_exit
;
}
i
=
0
;
while
(
xferunit
!=
(
1
<<
i
))
i
++
;
/* If valid value */
if
(
xferunit
==
(
1
<<
i
))
ch
->
rqcfg
.
brst_size
=
i
;
else
ret
=
-
EINVAL
;
cfg_exit:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_config
);
/* Options that are supported by this driver */
#define S3C_PL330_FLAGS (S3C2410_DMAF_CIRCULAR | S3C2410_DMAF_AUTOSTART)
int
s3c2410_dma_setflags
(
enum
dma_ch
id
,
unsigned
int
options
)
{
struct
s3c_pl330_chan
*
ch
;
unsigned
long
flags
;
int
ret
=
0
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
if
(
!
ch
||
chan_free
(
ch
)
||
options
&
~
(
S3C_PL330_FLAGS
))
ret
=
-
EINVAL
;
else
ch
->
options
=
options
;
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
0
;
}
EXPORT_SYMBOL
(
s3c2410_dma_setflags
);
int
s3c2410_dma_set_buffdone_fn
(
enum
dma_ch
id
,
s3c2410_dma_cbfn_t
rtn
)
{
struct
s3c_pl330_chan
*
ch
;
unsigned
long
flags
;
int
ret
=
0
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
if
(
!
ch
||
chan_free
(
ch
))
ret
=
-
EINVAL
;
else
ch
->
callback_fn
=
rtn
;
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_set_buffdone_fn
);
int
s3c2410_dma_devconfig
(
enum
dma_ch
id
,
enum
s3c2410_dmasrc
source
,
unsigned
long
address
)
{
struct
s3c_pl330_chan
*
ch
;
unsigned
long
flags
;
int
ret
=
0
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
ch
=
id_to_chan
(
id
);
if
(
!
ch
||
chan_free
(
ch
))
{
ret
=
-
EINVAL
;
goto
devcfg_exit
;
}
switch
(
source
)
{
case
S3C2410_DMASRC_HW
:
/* P->M */
ch
->
req
[
0
].
rqtype
=
DEVTOMEM
;
ch
->
req
[
1
].
rqtype
=
DEVTOMEM
;
ch
->
rqcfg
.
src_inc
=
0
;
ch
->
rqcfg
.
dst_inc
=
1
;
break
;
case
S3C2410_DMASRC_MEM
:
/* M->P */
ch
->
req
[
0
].
rqtype
=
MEMTODEV
;
ch
->
req
[
1
].
rqtype
=
MEMTODEV
;
ch
->
rqcfg
.
src_inc
=
1
;
ch
->
rqcfg
.
dst_inc
=
0
;
break
;
default:
ret
=
-
EINVAL
;
goto
devcfg_exit
;
}
ch
->
sdaddr
=
address
;
devcfg_exit:
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL
(
s3c2410_dma_devconfig
);
int
s3c2410_dma_getposition
(
enum
dma_ch
id
,
dma_addr_t
*
src
,
dma_addr_t
*
dst
)
{
struct
s3c_pl330_chan
*
ch
=
id_to_chan
(
id
);
struct
pl330_chanstatus
status
;
int
ret
;
if
(
!
ch
||
chan_free
(
ch
))
return
-
EINVAL
;
ret
=
pl330_chan_status
(
ch
->
pl330_chan_id
,
&
status
);
if
(
ret
<
0
)
return
ret
;
*
src
=
status
.
src_addr
;
*
dst
=
status
.
dst_addr
;
return
0
;
}
EXPORT_SYMBOL
(
s3c2410_dma_getposition
);
static
irqreturn_t
pl330_irq_handler
(
int
irq
,
void
*
data
)
{
if
(
pl330_update
(
data
))
return
IRQ_HANDLED
;
else
return
IRQ_NONE
;
}
static
int
pl330_probe
(
struct
platform_device
*
pdev
)
{
struct
s3c_pl330_dmac
*
s3c_pl330_dmac
;
struct
s3c_pl330_platdata
*
pl330pd
;
struct
pl330_info
*
pl330_info
;
struct
resource
*
res
;
int
i
,
ret
,
irq
;
pl330pd
=
pdev
->
dev
.
platform_data
;
/* Can't do without the list of _32_ peripherals */
if
(
!
pl330pd
||
!
pl330pd
->
peri
)
{
dev_err
(
&
pdev
->
dev
,
"platform data missing!
\n
"
);
return
-
ENODEV
;
}
pl330_info
=
kzalloc
(
sizeof
(
*
pl330_info
),
GFP_KERNEL
);
if
(
!
pl330_info
)
return
-
ENOMEM
;
pl330_info
->
pl330_data
=
NULL
;
pl330_info
->
dev
=
&
pdev
->
dev
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
res
)
{
ret
=
-
ENODEV
;
goto
probe_err1
;
}
request_mem_region
(
res
->
start
,
resource_size
(
res
),
pdev
->
name
);
pl330_info
->
base
=
ioremap
(
res
->
start
,
resource_size
(
res
));
if
(
!
pl330_info
->
base
)
{
ret
=
-
ENXIO
;
goto
probe_err2
;
}
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
<
0
)
{
ret
=
irq
;
goto
probe_err3
;
}
ret
=
request_irq
(
irq
,
pl330_irq_handler
,
0
,
dev_name
(
&
pdev
->
dev
),
pl330_info
);
if
(
ret
)
goto
probe_err4
;
/* Allocate a new DMAC */
s3c_pl330_dmac
=
kmalloc
(
sizeof
(
*
s3c_pl330_dmac
),
GFP_KERNEL
);
if
(
!
s3c_pl330_dmac
)
{
ret
=
-
ENOMEM
;
goto
probe_err5
;
}
/* Get operation clock and enable it */
s3c_pl330_dmac
->
clk
=
clk_get
(
&
pdev
->
dev
,
"pdma"
);
if
(
IS_ERR
(
s3c_pl330_dmac
->
clk
))
{
dev_err
(
&
pdev
->
dev
,
"Cannot get operation clock.
\n
"
);
ret
=
-
EINVAL
;
goto
probe_err6
;
}
clk_enable
(
s3c_pl330_dmac
->
clk
);
ret
=
pl330_add
(
pl330_info
);
if
(
ret
)
goto
probe_err7
;
/* Hook the info */
s3c_pl330_dmac
->
pi
=
pl330_info
;
/* No busy channels */
s3c_pl330_dmac
->
busy_chan
=
0
;
s3c_pl330_dmac
->
kmcache
=
kmem_cache_create
(
dev_name
(
&
pdev
->
dev
),
sizeof
(
struct
s3c_pl330_xfer
),
0
,
0
,
NULL
);
if
(
!
s3c_pl330_dmac
->
kmcache
)
{
ret
=
-
ENOMEM
;
goto
probe_err8
;
}
/* Get the list of peripherals */
s3c_pl330_dmac
->
peri
=
pl330pd
->
peri
;
/* Attach to the list of DMACs */
list_add_tail
(
&
s3c_pl330_dmac
->
node
,
&
dmac_list
);
/* Create a channel for each peripheral in the DMAC
* that is, if it doesn't already exist
*/
for
(
i
=
0
;
i
<
PL330_MAX_PERI
;
i
++
)
if
(
s3c_pl330_dmac
->
peri
[
i
]
!=
DMACH_MAX
)
chan_add
(
s3c_pl330_dmac
->
peri
[
i
]);
printk
(
KERN_INFO
"Loaded driver for PL330 DMAC-%d %s
\n
"
,
pdev
->
id
,
pdev
->
name
);
printk
(
KERN_INFO
"
\t
DBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u
\n
"
,
pl330_info
->
pcfg
.
data_buf_dep
,
pl330_info
->
pcfg
.
data_bus_width
/
8
,
pl330_info
->
pcfg
.
num_chan
,
pl330_info
->
pcfg
.
num_peri
,
pl330_info
->
pcfg
.
num_events
);
return
0
;
probe_err8:
pl330_del
(
pl330_info
);
probe_err7:
clk_disable
(
s3c_pl330_dmac
->
clk
);
clk_put
(
s3c_pl330_dmac
->
clk
);
probe_err6:
kfree
(
s3c_pl330_dmac
);
probe_err5:
free_irq
(
irq
,
pl330_info
);
probe_err4:
probe_err3:
iounmap
(
pl330_info
->
base
);
probe_err2:
release_mem_region
(
res
->
start
,
resource_size
(
res
));
probe_err1:
kfree
(
pl330_info
);
return
ret
;
}
static
int
pl330_remove
(
struct
platform_device
*
pdev
)
{
struct
s3c_pl330_dmac
*
dmac
,
*
d
;
struct
s3c_pl330_chan
*
ch
;
unsigned
long
flags
;
int
del
,
found
;
if
(
!
pdev
->
dev
.
platform_data
)
return
-
EINVAL
;
spin_lock_irqsave
(
&
res_lock
,
flags
);
found
=
0
;
list_for_each_entry
(
d
,
&
dmac_list
,
node
)
if
(
d
->
pi
->
dev
==
&
pdev
->
dev
)
{
found
=
1
;
break
;
}
if
(
!
found
)
{
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
0
;
}
dmac
=
d
;
/* Remove all Channels that are managed only by this DMAC */
list_for_each_entry
(
ch
,
&
chan_list
,
node
)
{
/* Only channels that are handled by this DMAC */
if
(
iface_of_dmac
(
dmac
,
ch
->
id
))
del
=
1
;
else
continue
;
/* Don't remove if some other DMAC has it too */
list_for_each_entry
(
d
,
&
dmac_list
,
node
)
if
(
d
!=
dmac
&&
iface_of_dmac
(
d
,
ch
->
id
))
{
del
=
0
;
break
;
}
if
(
del
)
{
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
s3c2410_dma_free
(
ch
->
id
,
ch
->
client
);
spin_lock_irqsave
(
&
res_lock
,
flags
);
list_del
(
&
ch
->
node
);
kfree
(
ch
);
}
}
/* Disable operation clock */
clk_disable
(
dmac
->
clk
);
clk_put
(
dmac
->
clk
);
/* Remove the DMAC */
list_del
(
&
dmac
->
node
);
kfree
(
dmac
);
spin_unlock_irqrestore
(
&
res_lock
,
flags
);
return
0
;
}
static
struct
platform_driver
pl330_driver
=
{
.
driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"s3c-pl330"
,
},
.
probe
=
pl330_probe
,
.
remove
=
pl330_remove
,
};
static
int
__init
pl330_init
(
void
)
{
return
platform_driver_register
(
&
pl330_driver
);
}
module_init
(
pl330_init
);
static
void
__exit
pl330_exit
(
void
)
{
platform_driver_unregister
(
&
pl330_driver
);
return
;
}
module_exit
(
pl330_exit
);
MODULE_AUTHOR
(
"Jaswinder Singh <jassi.brar@samsung.com>"
);
MODULE_DESCRIPTION
(
"Driver for PL330 DMA Controller"
);
MODULE_LICENSE
(
"GPL"
);
drivers/dma/Kconfig
View file @
59ca37f7
...
...
@@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
config PL330_DMA
tristate "DMA API Driver for PL330"
select DMA_ENGINE
depends on PL330
depends on ARM_AMBA
select PL330
help
Select if your platform has one or more PL330 DMACs.
You need to provide platform specific settings via
...
...
drivers/dma/amba-pl08x.c
View file @
59ca37f7
...
...
@@ -66,32 +66,29 @@
* after the final transfer signalled by LBREQ or LSREQ. The DMAC
* will then move to the next LLI entry.
*
* Only the former works sanely with scatter lists, so we only implement
* the DMAC flow control method. However, peripherals which use the LBREQ
* and LSREQ signals (eg, MMCI) are unable to use this mode, which through
* these hardware restrictions prevents them from using scatter DMA.
*
* Global TODO:
* - Break out common code from arch/arm/mach-s3c64xx and share
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/dmaengine.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl08x.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <asm/hardware/pl080.h>
#define DRIVER_NAME "pl08xdmac"
static
struct
amba_driver
pl08x_amba_driver
;
/**
* struct vendor_data - vendor-specific config parameters for PL08x derivatives
* @channels: the number of channels available in this variant
...
...
@@ -126,7 +123,8 @@ struct pl08x_lli {
* @phy_chans: array of data for the physical channels
* @pool: a pool for the LLI descriptors
* @pool_ctr: counter of LLIs in the pool
* @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI fetches
* @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
* fetches
* @mem_buses: set to indicate memory transfers on AHB2.
* @lock: a spinlock for this struct
*/
...
...
@@ -149,14 +147,6 @@ struct pl08x_driver_data {
* PL08X specific defines
*/
/*
* Memory boundaries: the manual for PL08x says that the controller
* cannot read past a 1KiB boundary, so these defines are used to
* create transfer LLIs that do not cross such boundaries.
*/
#define PL08X_BOUNDARY_SHIFT (10)
/* 1KB 0x400 */
#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT)
/* Size (bytes) of each LLI buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000
...
...
@@ -272,7 +262,6 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
writel
(
val
,
ch
->
base
+
PL080_CH_CONFIG
);
}
/*
* pl08x_terminate_phy_chan() stops the channel, clears the FIFO and
* clears any pending interrupt status. This should not be used for
...
...
@@ -407,6 +396,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
return
NULL
;
}
pm_runtime_get_sync
(
&
pl08x
->
adev
->
dev
);
return
ch
;
}
...
...
@@ -420,6 +410,8 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
/* Stop the channel and clear its interrupts */
pl08x_terminate_phy_chan
(
pl08x
,
ch
);
pm_runtime_put
(
&
pl08x
->
adev
->
dev
);
/* Mark it as free */
ch
->
serving
=
NULL
;
spin_unlock_irqrestore
(
&
ch
->
lock
,
flags
);
...
...
@@ -499,36 +491,30 @@ struct pl08x_lli_build_data {
};
/*
* Autoselect a master bus to use for the transfer this prefers the
* destination bus if both available if fixed address on one bus the
* other will be chosen
* Autoselect a master bus to use for the transfer. Slave will be the chosen as
* victim in case src & dest are not similarly aligned. i.e. If after aligning
* masters address with width requirements of transfer (by sending few byte by
* byte data), slave is still not aligned, then its width will be reduced to
* BYTE.
* - prefers the destination bus if both available
* - prefers bus with fixed address (i.e. peripheral)
*/
static
void
pl08x_choose_master_bus
(
struct
pl08x_lli_build_data
*
bd
,
struct
pl08x_bus_data
**
mbus
,
struct
pl08x_bus_data
**
sbus
,
u32
cctl
)
{
if
(
!
(
cctl
&
PL080_CONTROL_DST_INCR
))
{
*
mbus
=
&
bd
->
srcbus
;
*
sbus
=
&
bd
->
dstbus
;
}
else
if
(
!
(
cctl
&
PL080_CONTROL_SRC_INCR
))
{
*
mbus
=
&
bd
->
dstbus
;
*
sbus
=
&
bd
->
srcbus
;
}
else
{
if
(
bd
->
dstbus
.
buswidth
==
4
)
{
*
mbus
=
&
bd
->
dstbus
;
*
sbus
=
&
bd
->
srcbus
;
}
else
if
(
bd
->
srcbus
.
buswidth
==
4
)
{
}
else
if
(
!
(
cctl
&
PL080_CONTROL_SRC_INCR
)
)
{
*
mbus
=
&
bd
->
srcbus
;
*
sbus
=
&
bd
->
dstbus
;
}
else
if
(
bd
->
dstbus
.
buswidth
==
2
)
{
}
else
{
if
(
bd
->
dstbus
.
buswidth
>=
bd
->
srcbus
.
buswidth
)
{
*
mbus
=
&
bd
->
dstbus
;
*
sbus
=
&
bd
->
srcbus
;
}
else
if
(
bd
->
srcbus
.
buswidth
==
2
)
{
}
else
{
*
mbus
=
&
bd
->
srcbus
;
*
sbus
=
&
bd
->
dstbus
;
}
else
{
/* bd->srcbus.buswidth == 1 */
*
mbus
=
&
bd
->
dstbus
;
*
sbus
=
&
bd
->
srcbus
;
}
}
}
...
...
@@ -547,7 +533,8 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
llis_va
[
num_llis
].
cctl
=
cctl
;
llis_va
[
num_llis
].
src
=
bd
->
srcbus
.
addr
;
llis_va
[
num_llis
].
dst
=
bd
->
dstbus
.
addr
;
llis_va
[
num_llis
].
lli
=
llis_bus
+
(
num_llis
+
1
)
*
sizeof
(
struct
pl08x_lli
);
llis_va
[
num_llis
].
lli
=
llis_bus
+
(
num_llis
+
1
)
*
sizeof
(
struct
pl08x_lli
);
llis_va
[
num_llis
].
lli
|=
bd
->
lli_bus
;
if
(
cctl
&
PL080_CONTROL_SRC_INCR
)
...
...
@@ -560,16 +547,12 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
bd
->
remainder
-=
len
;
}
/*
* Return number of bytes to fill to boundary, or len.
* This calculation works for any value of addr.
*/
static
inline
size_t
pl08x_pre_boundary
(
u32
addr
,
size_t
len
)
static
inline
void
prep_byte_width_lli
(
struct
pl08x_lli_build_data
*
bd
,
u32
*
cctl
,
u32
len
,
int
num_llis
,
size_t
*
total_bytes
)
{
size_t
boundary_len
=
PL08X_BOUNDARY_SIZE
-
(
addr
&
(
PL08X_BOUNDARY_SIZE
-
1
));
return
min
(
boundary_len
,
len
);
*
cctl
=
pl08x_cctl_bits
(
*
cctl
,
1
,
1
,
len
);
pl08x_fill_lli_for_desc
(
bd
,
num_llis
,
len
,
*
cctl
);
(
*
total_bytes
)
+=
len
;
}
/*
...
...
@@ -583,13 +566,11 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
struct
pl08x_bus_data
*
mbus
,
*
sbus
;
struct
pl08x_lli_build_data
bd
;
int
num_llis
=
0
;
u32
cctl
;
size_t
max_bytes_per_lli
;
size_t
total_bytes
=
0
;
u32
cctl
,
early_bytes
=
0
;
size_t
max_bytes_per_lli
,
total_bytes
=
0
;
struct
pl08x_lli
*
llis_va
;
txd
->
llis_va
=
dma_pool_alloc
(
pl08x
->
pool
,
GFP_NOWAIT
,
&
txd
->
llis_bus
);
txd
->
llis_va
=
dma_pool_alloc
(
pl08x
->
pool
,
GFP_NOWAIT
,
&
txd
->
llis_bus
);
if
(
!
txd
->
llis_va
)
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s no memory for llis
\n
"
,
__func__
);
return
0
;
...
...
@@ -619,55 +600,85 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
bd
.
srcbus
.
buswidth
=
bd
.
srcbus
.
maxwidth
;
bd
.
dstbus
.
buswidth
=
bd
.
dstbus
.
maxwidth
;
/*
* Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
*/
max_bytes_per_lli
=
min
(
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
)
*
PL080_CONTROL_TRANSFER_SIZE_MASK
;
/* We need to count this down to zero */
bd
.
remainder
=
txd
->
len
;
/*
* Choose bus to align to
* - prefers destination bus if both available
* - if fixed address on one bus chooses other
*/
pl08x_choose_master_bus
(
&
bd
,
&
mbus
,
&
sbus
,
cctl
);
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu
llimax=%zu
\n
"
,
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu
\n
"
,
bd
.
srcbus
.
addr
,
cctl
&
PL080_CONTROL_SRC_INCR
?
"+"
:
""
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
addr
,
cctl
&
PL080_CONTROL_DST_INCR
?
"+"
:
""
,
bd
.
dstbus
.
buswidth
,
bd
.
remainder
,
max_bytes_per_lli
);
bd
.
remainder
);
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"mbus=%s sbus=%s
\n
"
,
mbus
==
&
bd
.
srcbus
?
"src"
:
"dst"
,
sbus
==
&
bd
.
srcbus
?
"src"
:
"dst"
);
if
(
txd
->
len
<
mbus
->
buswidth
)
{
/* Less than a bus width available - send as single bytes */
while
(
bd
.
remainder
)
{
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s single byte LLIs for a transfer of "
"less than a bus width (remain 0x%08x)
\n
"
,
__func__
,
bd
.
remainder
);
cctl
=
pl08x_cctl_bits
(
cctl
,
1
,
1
,
1
);
pl08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
1
,
cctl
);
total_bytes
++
;
/*
* Zero length is only allowed if all these requirements are met:
* - flow controller is peripheral.
* - src.addr is aligned to src.width
* - dst.addr is aligned to dst.width
*
* sg_len == 1 should be true, as there can be two cases here:
* - Memory addresses are contiguous and are not scattered. Here, Only
* one sg will be passed by user driver, with memory address and zero
* length. We pass this to controller and after the transfer it will
* receive the last burst request from peripheral and so transfer
* finishes.
*
* - Memory addresses are scattered and are not contiguous. Here,
* Obviously as DMA controller doesn't know when a lli's transfer gets
* over, it can't load next lli. So in this case, there has to be an
* assumption that only one lli is supported. Thus, we can't have
* scattered addresses.
*/
if
(
!
bd
.
remainder
)
{
u32
fc
=
(
txd
->
ccfg
&
PL080_CONFIG_FLOW_CONTROL_MASK
)
>>
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
if
(
!
((
fc
>=
PL080_FLOW_SRC2DST_DST
)
&&
(
fc
<=
PL080_FLOW_SRC2DST_SRC
)))
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s sg len can't be zero"
,
__func__
);
return
0
;
}
}
else
{
/* Make one byte LLIs until master bus is aligned */
while
((
mbus
->
addr
)
%
(
mbus
->
buswidth
))
{
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s adjustment lli for less than bus width "
"(remain 0x%08x)
\n
"
,
__func__
,
bd
.
remainder
);
cctl
=
pl08x_cctl_bits
(
cctl
,
1
,
1
,
1
);
pl08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
1
,
cctl
);
total_bytes
++
;
if
((
bd
.
srcbus
.
addr
%
bd
.
srcbus
.
buswidth
)
||
(
bd
.
srcbus
.
addr
%
bd
.
srcbus
.
buswidth
))
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s src & dst address must be aligned to src"
" & dst width if peripheral is flow controller"
,
__func__
);
return
0
;
}
cctl
=
pl08x_cctl_bits
(
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
0
);
pl08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
0
,
cctl
);
}
/*
* Send byte by byte for following cases
* - Less than a bus width available
* - until master bus is aligned
*/
if
(
bd
.
remainder
<
mbus
->
buswidth
)
early_bytes
=
bd
.
remainder
;
else
if
((
mbus
->
addr
)
%
(
mbus
->
buswidth
))
{
early_bytes
=
mbus
->
buswidth
-
(
mbus
->
addr
)
%
(
mbus
->
buswidth
);
if
((
bd
.
remainder
-
early_bytes
)
<
mbus
->
buswidth
)
early_bytes
=
bd
.
remainder
;
}
if
(
early_bytes
)
{
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s byte width LLIs "
"(remain 0x%08x)
\n
"
,
__func__
,
bd
.
remainder
);
prep_byte_width_lli
(
&
bd
,
&
cctl
,
early_bytes
,
num_llis
++
,
&
total_bytes
);
}
if
(
bd
.
remainder
)
{
/*
* Master now aligned
* - if slave is not then we must set its width down
...
...
@@ -680,138 +691,55 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
sbus
->
buswidth
=
1
;
}
/* Bytes transferred = tsize * src width, not MIN(buswidths) */
max_bytes_per_lli
=
bd
.
srcbus
.
buswidth
*
PL080_CONTROL_TRANSFER_SIZE_MASK
;
/*
* Make largest possible LLIs until less than one bus
* width left
*/
while
(
bd
.
remainder
>
(
mbus
->
buswidth
-
1
))
{
size_t
lli_len
,
t
arget_len
,
tsize
,
odd_bytes
;
size_t
lli_len
,
t
size
,
width
;
/*
* If enough left try to send max possible,
* otherwise try to send the remainder
*/
target_len
=
min
(
bd
.
remainder
,
max_bytes_per_lli
);
/*
* Set bus lengths for incrementing buses to the
* number of bytes which fill to next memory boundary,
* limiting on the target length calculated above.
*/
if
(
cctl
&
PL080_CONTROL_SRC_INCR
)
bd
.
srcbus
.
fill_bytes
=
pl08x_pre_boundary
(
bd
.
srcbus
.
addr
,
target_len
);
else
bd
.
srcbus
.
fill_bytes
=
target_len
;
if
(
cctl
&
PL080_CONTROL_DST_INCR
)
bd
.
dstbus
.
fill_bytes
=
pl08x_pre_boundary
(
bd
.
dstbus
.
addr
,
target_len
);
else
bd
.
dstbus
.
fill_bytes
=
target_len
;
/* Find the nearest */
lli_len
=
min
(
bd
.
srcbus
.
fill_bytes
,
bd
.
dstbus
.
fill_bytes
);
BUG_ON
(
lli_len
>
bd
.
remainder
);
if
(
lli_len
<=
0
)
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s lli_len is %zu, <= 0
\n
"
,
__func__
,
lli_len
);
return
0
;
}
if
(
lli_len
==
target_len
)
{
/*
* Can send what we wanted.
* Maintain alignment
*/
lli_len
=
(
lli_len
/
mbus
->
buswidth
)
*
mbus
->
buswidth
;
odd_bytes
=
0
;
}
else
{
/*
* So now we know how many bytes to transfer
* to get to the nearest boundary. The next
* LLI will past the boundary. However, we
* may be working to a boundary on the slave
* bus. We need to ensure the master stays
* aligned, and that we are working in
* multiples of the bus widths.
*/
odd_bytes
=
lli_len
%
mbus
->
buswidth
;
lli_len
-=
odd_bytes
;
}
lli_len
=
min
(
bd
.
remainder
,
max_bytes_per_lli
);
if
(
lli_len
)
{
/*
* Check against minimum bus alignment:
* Calculate actual transfer size in relation
* to bus width an get a maximum remainder of
* the smallest bus width - 1
* Check against maximum bus alignment: Calculate actual
* transfer size in relation to bus width and get a
* maximum remainder of the highest bus width - 1
*/
/* FIXME: use round_down()? */
tsize
=
lli_len
/
min
(
mbus
->
buswidth
,
sbus
->
buswidth
);
lli_len
=
tsize
*
min
(
mbus
->
buswidth
,
sbus
->
buswidth
);
if
(
target_len
!=
lli_len
)
{
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s can't send what we want. Desired 0x%08zx, lli of 0x%08zx bytes in txd of 0x%08zx
\n
"
,
__func__
,
target_len
,
lli_len
,
txd
->
len
);
}
cctl
=
pl08x_cctl_bits
(
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
tsize
);
width
=
max
(
mbus
->
buswidth
,
sbus
->
buswidth
);
lli_len
=
(
lli_len
/
width
)
*
width
;
tsize
=
lli_len
/
bd
.
srcbus
.
buswidth
;
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)
\n
"
,
"%s fill lli with single lli chunk of "
"size 0x%08zx (remainder 0x%08zx)
\n
"
,
__func__
,
lli_len
,
bd
.
remainder
);
pl08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
lli_len
,
cctl
);
total_bytes
+=
lli_len
;
}
if
(
odd_bytes
)
{
/*
* Creep past the boundary, maintaining
* master alignment
*/
int
j
;
for
(
j
=
0
;
(
j
<
mbus
->
buswidth
)
&&
(
bd
.
remainder
);
j
++
)
{
cctl
=
pl08x_cctl_bits
(
cctl
,
1
,
1
,
1
);
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s align with boundary, single byte (remain 0x%08zx)
\n
"
,
__func__
,
bd
.
remainder
);
pl08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
1
,
cctl
);
total_bytes
++
;
}
}
cctl
=
pl08x_cctl_bits
(
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
tsize
);
pl08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
lli_len
,
cctl
);
total_bytes
+=
lli_len
;
}
/*
* Send any odd bytes
*/
while
(
bd
.
remainder
)
{
cctl
=
pl08x_cctl_bits
(
cctl
,
1
,
1
,
1
);
if
(
bd
.
remainder
)
{
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"%s align with boundary, s
ingle odd byte
(remain %zu)
\n
"
,
"%s align with boundary, s
end odd bytes
(remain %zu)
\n
"
,
__func__
,
bd
.
remainder
);
p
l08x_fill_lli_for_desc
(
&
bd
,
num_llis
++
,
1
,
cctl
);
total_bytes
++
;
p
rep_byte_width_lli
(
&
bd
,
&
cctl
,
bd
.
remainder
,
num_llis
++
,
&
total_bytes
)
;
}
}
if
(
total_bytes
!=
txd
->
len
)
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx
\n
"
,
...
...
@@ -917,9 +845,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
* need, but for slaves the physical signals may be muxed!
* Can the platform allow us to use this channel?
*/
if
(
plchan
->
slave
&&
ch
->
signal
<
0
&&
pl08x
->
pd
->
get_signal
)
{
if
(
plchan
->
slave
&&
pl08x
->
pd
->
get_signal
)
{
ret
=
pl08x
->
pd
->
get_signal
(
plchan
);
if
(
ret
<
0
)
{
dev_dbg
(
&
pl08x
->
adev
->
dev
,
...
...
@@ -1008,10 +934,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
* If slaves are relying on interrupts to signal completion this function
* must not be called with interrupts disabled.
*/
static
enum
dma_status
pl08x_dma_tx_status
(
struct
dma_chan
*
chan
,
dma_cookie_t
cookie
,
struct
dma_tx_state
*
txstate
)
static
enum
dma_status
pl08x_dma_tx_status
(
struct
dma_chan
*
chan
,
dma_cookie_t
cookie
,
struct
dma_tx_state
*
txstate
)
{
struct
pl08x_dma_chan
*
plchan
=
to_pl08x_chan
(
chan
);
dma_cookie_t
last_used
;
...
...
@@ -1253,7 +1177,9 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
num_llis
=
pl08x_fill_llis_for_desc
(
pl08x
,
txd
);
if
(
!
num_llis
)
{
kfree
(
txd
);
spin_lock_irqsave
(
&
plchan
->
lock
,
flags
);
pl08x_free_txd
(
pl08x
,
txd
);
spin_unlock_irqrestore
(
&
plchan
->
lock
,
flags
);
return
-
EINVAL
;
}
...
...
@@ -1301,7 +1227,7 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
static
struct
pl08x_txd
*
pl08x_get_txd
(
struct
pl08x_dma_chan
*
plchan
,
unsigned
long
flags
)
{
struct
pl08x_txd
*
txd
=
kzalloc
(
sizeof
(
struct
pl08x_
txd
),
GFP_NOWAIT
);
struct
pl08x_txd
*
txd
=
kzalloc
(
sizeof
(
*
txd
),
GFP_NOWAIT
);
if
(
txd
)
{
dma_async_tx_descriptor_init
(
&
txd
->
tx
,
&
plchan
->
chan
);
...
...
@@ -1367,7 +1293,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct
pl08x_dma_chan
*
plchan
=
to_pl08x_chan
(
chan
);
struct
pl08x_driver_data
*
pl08x
=
plchan
->
host
;
struct
pl08x_txd
*
txd
;
int
ret
;
int
ret
,
tmp
;
/*
* Current implementation ASSUMES only one sg
...
...
@@ -1401,12 +1327,10 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
txd
->
len
=
sgl
->
length
;
if
(
direction
==
DMA_TO_DEVICE
)
{
txd
->
ccfg
|=
PL080_FLOW_MEM2PER
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
txd
->
cctl
=
plchan
->
dst_cctl
;
txd
->
src_addr
=
sgl
->
dma_address
;
txd
->
dst_addr
=
plchan
->
dst_addr
;
}
else
if
(
direction
==
DMA_FROM_DEVICE
)
{
txd
->
ccfg
|=
PL080_FLOW_PER2MEM
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
txd
->
cctl
=
plchan
->
src_cctl
;
txd
->
src_addr
=
plchan
->
src_addr
;
txd
->
dst_addr
=
sgl
->
dma_address
;
...
...
@@ -1416,6 +1340,15 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
return
NULL
;
}
if
(
plchan
->
cd
->
device_fc
)
tmp
=
(
direction
==
DMA_TO_DEVICE
)
?
PL080_FLOW_MEM2PER_PER
:
PL080_FLOW_PER2MEM_PER
;
else
tmp
=
(
direction
==
DMA_TO_DEVICE
)
?
PL080_FLOW_MEM2PER
:
PL080_FLOW_PER2MEM
;
txd
->
ccfg
|=
tmp
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
ret
=
pl08x_prep_channel_resources
(
plchan
,
txd
);
if
(
ret
)
return
NULL
;
...
...
@@ -1489,9 +1422,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
bool
pl08x_filter_id
(
struct
dma_chan
*
chan
,
void
*
chan_id
)
{
struct
pl08x_dma_chan
*
plchan
=
to_pl08x_chan
(
chan
)
;
struct
pl08x_dma_chan
*
plchan
;
char
*
name
=
chan_id
;
/* Reject channels for devices not bound to this driver */
if
(
chan
->
device
->
dev
->
driver
!=
&
pl08x_amba_driver
.
drv
)
return
false
;
plchan
=
to_pl08x_chan
(
chan
);
/* Check that the channel is not taken! */
if
(
!
strcmp
(
plchan
->
name
,
name
))
return
true
;
...
...
@@ -1507,13 +1446,7 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
*/
static
void
pl08x_ensure_on
(
struct
pl08x_driver_data
*
pl08x
)
{
u32
val
;
val
=
readl
(
pl08x
->
base
+
PL080_CONFIG
);
val
&=
~
(
PL080_CONFIG_M2_BE
|
PL080_CONFIG_M1_BE
|
PL080_CONFIG_ENABLE
);
/* We implicitly clear bit 1 and that means little-endian mode */
val
|=
PL080_CONFIG_ENABLE
;
writel
(
val
,
pl08x
->
base
+
PL080_CONFIG
);
writel
(
PL080_CONFIG_ENABLE
,
pl08x
->
base
+
PL080_CONFIG
);
}
static
void
pl08x_unmap_buffers
(
struct
pl08x_txd
*
txd
)
...
...
@@ -1630,38 +1563,40 @@ static void pl08x_tasklet(unsigned long data)
static
irqreturn_t
pl08x_irq
(
int
irq
,
void
*
dev
)
{
struct
pl08x_driver_data
*
pl08x
=
dev
;
u32
mask
=
0
;
u32
val
;
int
i
;
u32
mask
=
0
,
err
,
tc
,
i
;
val
=
readl
(
pl08x
->
base
+
PL080_ERR_STATUS
);
if
(
val
)
{
/* An error interrupt (on one or more channels) */
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s error interrupt, register value 0x%08x
\n
"
,
__func__
,
val
);
/*
* Simply clear ALL PL08X error interrupts,
* regardless of channel and cause
* FIXME: should be 0x00000003 on PL081 really.
*/
writel
(
0x000000FF
,
pl08x
->
base
+
PL080_ERR_CLEAR
);
/* check & clear - ERR & TC interrupts */
err
=
readl
(
pl08x
->
base
+
PL080_ERR_STATUS
);
if
(
err
)
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s error interrupt, register value 0x%08x
\n
"
,
__func__
,
err
);
writel
(
err
,
pl08x
->
base
+
PL080_ERR_CLEAR
);
}
val
=
readl
(
pl08x
->
base
+
PL080_INT_STATUS
);
tc
=
readl
(
pl08x
->
base
+
PL080_INT_STATUS
);
if
(
tc
)
writel
(
tc
,
pl08x
->
base
+
PL080_TC_CLEAR
);
if
(
!
err
&&
!
tc
)
return
IRQ_NONE
;
for
(
i
=
0
;
i
<
pl08x
->
vd
->
channels
;
i
++
)
{
if
((
1
<<
i
)
&
val
)
{
if
((
(
1
<<
i
)
&
err
)
||
((
1
<<
i
)
&
tc
)
)
{
/* Locate physical channel */
struct
pl08x_phy_chan
*
phychan
=
&
pl08x
->
phy_chans
[
i
];
struct
pl08x_dma_chan
*
plchan
=
phychan
->
serving
;
if
(
!
plchan
)
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s Error TC interrupt on unused channel: 0x%08x
\n
"
,
__func__
,
i
);
continue
;
}
/* Schedule tasklet on this channel */
tasklet_schedule
(
&
plchan
->
tasklet
);
mask
|=
(
1
<<
i
);
}
}
/* Clear only the terminal interrupts on channels we processed */
writel
(
mask
,
pl08x
->
base
+
PL080_TC_CLEAR
);
return
mask
?
IRQ_HANDLED
:
IRQ_NONE
;
}
...
...
@@ -1685,9 +1620,7 @@ static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
* Make a local wrapper to hold required data
*/
static
int
pl08x_dma_init_virtual_channels
(
struct
pl08x_driver_data
*
pl08x
,
struct
dma_device
*
dmadev
,
unsigned
int
channels
,
bool
slave
)
struct
dma_device
*
dmadev
,
unsigned
int
channels
,
bool
slave
)
{
struct
pl08x_dma_chan
*
chan
;
int
i
;
...
...
@@ -1700,7 +1633,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
* to cope with that situation.
*/
for
(
i
=
0
;
i
<
channels
;
i
++
)
{
chan
=
kzalloc
(
sizeof
(
struct
pl08x_dma_
chan
),
GFP_KERNEL
);
chan
=
kzalloc
(
sizeof
(
*
chan
),
GFP_KERNEL
);
if
(
!
chan
)
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s no memory for channel
\n
"
,
__func__
);
...
...
@@ -1728,7 +1661,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
kfree
(
chan
);
continue
;
}
dev_
info
(
&
pl08x
->
adev
->
dev
,
dev_
dbg
(
&
pl08x
->
adev
->
dev
,
"initialize virtual channel
\"
%s
\"\n
"
,
chan
->
name
);
...
...
@@ -1837,8 +1770,8 @@ static const struct file_operations pl08x_debugfs_operations = {
static
void
init_pl08x_debugfs
(
struct
pl08x_driver_data
*
pl08x
)
{
/* Expose a simple debugfs interface to view all clocks */
(
void
)
debugfs_create_file
(
dev_name
(
&
pl08x
->
adev
->
dev
),
S_IFREG
|
S_IRUGO
,
NULL
,
pl08x
,
(
void
)
debugfs_create_file
(
dev_name
(
&
pl08x
->
adev
->
dev
),
S_IFREG
|
S_IRUGO
,
NULL
,
pl08x
,
&
pl08x_debugfs_operations
);
}
...
...
@@ -1860,12 +1793,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
return
ret
;
/* Create the driver state holder */
pl08x
=
kzalloc
(
sizeof
(
struct
pl08x_driver_data
),
GFP_KERNEL
);
pl08x
=
kzalloc
(
sizeof
(
*
pl08x
),
GFP_KERNEL
);
if
(
!
pl08x
)
{
ret
=
-
ENOMEM
;
goto
out_no_pl08x
;
}
pm_runtime_set_active
(
&
adev
->
dev
);
pm_runtime_enable
(
&
adev
->
dev
);
/* Initialize memcpy engine */
dma_cap_set
(
DMA_MEMCPY
,
pl08x
->
memcpy
.
cap_mask
);
pl08x
->
memcpy
.
dev
=
&
adev
->
dev
;
...
...
@@ -1939,7 +1875,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
}
/* Initialize physical channels */
pl08x
->
phy_chans
=
kmalloc
((
vd
->
channels
*
sizeof
(
struct
pl08x_phy_chan
)),
pl08x
->
phy_chans
=
kmalloc
((
vd
->
channels
*
sizeof
(
*
pl08x
->
phy_chans
)),
GFP_KERNEL
);
if
(
!
pl08x
->
phy_chans
)
{
dev_err
(
&
adev
->
dev
,
"%s failed to allocate "
...
...
@@ -1956,9 +1892,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init
(
&
ch
->
lock
);
ch
->
serving
=
NULL
;
ch
->
signal
=
-
1
;
dev_info
(
&
adev
->
dev
,
"physical channel %d is %s
\n
"
,
i
,
pl08x_phy_channel_busy
(
ch
)
?
"BUSY"
:
"FREE"
);
dev_dbg
(
&
adev
->
dev
,
"physical channel %d is %s
\n
"
,
i
,
pl08x_phy_channel_busy
(
ch
)
?
"BUSY"
:
"FREE"
);
}
/* Register as many memcpy channels as there are physical channels */
...
...
@@ -1974,8 +1909,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
/* Register slave channels */
ret
=
pl08x_dma_init_virtual_channels
(
pl08x
,
&
pl08x
->
slave
,
pl08x
->
pd
->
num_slave_channels
,
true
);
pl08x
->
pd
->
num_slave_channels
,
true
);
if
(
ret
<=
0
)
{
dev_warn
(
&
pl08x
->
adev
->
dev
,
"%s failed to enumerate slave channels - %d
\n
"
,
...
...
@@ -2005,6 +1939,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
dev_info
(
&
pl08x
->
adev
->
dev
,
"DMA: PL%03x rev%u at 0x%08llx irq %d
\n
"
,
amba_part
(
adev
),
amba_rev
(
adev
),
(
unsigned
long
long
)
adev
->
res
.
start
,
adev
->
irq
[
0
]);
pm_runtime_put
(
&
adev
->
dev
);
return
0
;
out_no_slave_reg:
...
...
@@ -2023,6 +1959,9 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
dma_pool_destroy
(
pl08x
->
pool
);
out_no_lli_pool:
out_no_platdata:
pm_runtime_put
(
&
adev
->
dev
);
pm_runtime_disable
(
&
adev
->
dev
);
kfree
(
pl08x
);
out_no_pl08x:
amba_release_regions
(
adev
);
...
...
drivers/dma/at_hdmac.c
View file @
59ca37f7
...
...
@@ -107,10 +107,11 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
{
struct
at_desc
*
desc
,
*
_desc
;
struct
at_desc
*
ret
=
NULL
;
unsigned
long
flags
;
unsigned
int
i
=
0
;
LIST_HEAD
(
tmp_list
);
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
list_for_each_entry_safe
(
desc
,
_desc
,
&
atchan
->
free_list
,
desc_node
)
{
i
++
;
if
(
async_tx_test_ack
(
&
desc
->
txd
))
{
...
...
@@ -121,7 +122,7 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
dev_dbg
(
chan2dev
(
&
atchan
->
chan_common
),
"desc %p not ACKed
\n
"
,
desc
);
}
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
dev_vdbg
(
chan2dev
(
&
atchan
->
chan_common
),
"scanned %u descriptors on freelist
\n
"
,
i
);
...
...
@@ -129,9 +130,9 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
if
(
!
ret
)
{
ret
=
atc_alloc_descriptor
(
&
atchan
->
chan_common
,
GFP_ATOMIC
);
if
(
ret
)
{
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
atchan
->
descs_allocated
++
;
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
}
else
{
dev_err
(
chan2dev
(
&
atchan
->
chan_common
),
"not enough descriptors available
\n
"
);
...
...
@@ -150,8 +151,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
{
if
(
desc
)
{
struct
at_desc
*
child
;
unsigned
long
flags
;
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
list_for_each_entry
(
child
,
&
desc
->
tx_list
,
desc_node
)
dev_vdbg
(
chan2dev
(
&
atchan
->
chan_common
),
"moving child desc %p to freelist
\n
"
,
...
...
@@ -160,7 +162,7 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
dev_vdbg
(
chan2dev
(
&
atchan
->
chan_common
),
"moving desc %p to freelist
\n
"
,
desc
);
list_add
(
&
desc
->
desc_node
,
&
atchan
->
free_list
);
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
}
}
...
...
@@ -299,7 +301,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
/* for cyclic transfers,
* no need to replay callback function while stopping */
if
(
!
test_bit
(
ATC_IS_CYCLIC
,
&
atchan
->
status
))
{
if
(
!
atc_chan_is_cyclic
(
atchan
))
{
dma_async_tx_callback
callback
=
txd
->
callback
;
void
*
param
=
txd
->
callback_param
;
...
...
@@ -471,16 +473,17 @@ static void atc_handle_cyclic(struct at_dma_chan *atchan)
static
void
atc_tasklet
(
unsigned
long
data
)
{
struct
at_dma_chan
*
atchan
=
(
struct
at_dma_chan
*
)
data
;
unsigned
long
flags
;
spin_lock
(
&
atchan
->
lock
);
spin_lock
_irqsave
(
&
atchan
->
lock
,
flags
);
if
(
test_and_clear_bit
(
ATC_IS_ERROR
,
&
atchan
->
status
))
atc_handle_error
(
atchan
);
else
if
(
test_bit
(
ATC_IS_CYCLIC
,
&
atchan
->
status
))
else
if
(
atc_chan_is_cyclic
(
atchan
))
atc_handle_cyclic
(
atchan
);
else
atc_advance_work
(
atchan
);
spin_unlock
(
&
atchan
->
lock
);
spin_unlock
_irqrestore
(
&
atchan
->
lock
,
flags
);
}
static
irqreturn_t
at_dma_interrupt
(
int
irq
,
void
*
dev_id
)
...
...
@@ -539,8 +542,9 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
struct
at_desc
*
desc
=
txd_to_at_desc
(
tx
);
struct
at_dma_chan
*
atchan
=
to_at_dma_chan
(
tx
->
chan
);
dma_cookie_t
cookie
;
unsigned
long
flags
;
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
cookie
=
atc_assign_cookie
(
atchan
,
desc
);
if
(
list_empty
(
&
atchan
->
active_list
))
{
...
...
@@ -554,7 +558,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
list_add_tail
(
&
desc
->
desc_node
,
&
atchan
->
queue
);
}
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
return
cookie
;
}
...
...
@@ -927,28 +931,29 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
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
;
unsigned
long
flags
;
LIST_HEAD
(
list
);
dev_vdbg
(
chan2dev
(
chan
),
"atc_control (%d)
\n
"
,
cmd
);
if
(
cmd
==
DMA_PAUSE
)
{
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
dma_writel
(
atdma
,
CHER
,
AT_DMA_SUSP
(
chan_id
));
set_bit
(
ATC_IS_PAUSED
,
&
atchan
->
status
);
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
}
else
if
(
cmd
==
DMA_RESUME
)
{
if
(
!
test_bit
(
ATC_IS_PAUSED
,
&
atchan
->
status
))
if
(
!
atc_chan_is_paused
(
atchan
))
return
0
;
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
dma_writel
(
atdma
,
CHDR
,
AT_DMA_RES
(
chan_id
));
clear_bit
(
ATC_IS_PAUSED
,
&
atchan
->
status
);
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
}
else
if
(
cmd
==
DMA_TERMINATE_ALL
)
{
struct
at_desc
*
desc
,
*
_desc
;
/*
...
...
@@ -957,7 +962,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
* channel. We still have to poll the channel enable bit due
* to AHB/HSB limitations.
*/
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
/* disabling channel: must also remove suspend state */
dma_writel
(
atdma
,
CHDR
,
AT_DMA_RES
(
chan_id
)
|
atchan
->
mask
);
...
...
@@ -978,7 +983,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* if channel dedicated to cyclic operations, free it */
clear_bit
(
ATC_IS_CYCLIC
,
&
atchan
->
status
);
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
}
else
{
return
-
ENXIO
;
}
...
...
@@ -1004,9 +1009,10 @@ atc_tx_status(struct 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
;
enum
dma_status
ret
;
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
last_complete
=
atchan
->
completed_cookie
;
last_used
=
chan
->
cookie
;
...
...
@@ -1021,7 +1027,7 @@ atc_tx_status(struct dma_chan *chan,
ret
=
dma_async_is_complete
(
cookie
,
last_complete
,
last_used
);
}
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
if
(
ret
!=
DMA_SUCCESS
)
dma_set_tx_state
(
txstate
,
last_complete
,
last_used
,
...
...
@@ -1029,7 +1035,7 @@ atc_tx_status(struct dma_chan *chan,
else
dma_set_tx_state
(
txstate
,
last_complete
,
last_used
,
0
);
if
(
test_bit
(
ATC_IS_PAUSED
,
&
atchan
->
status
))
if
(
atc_chan_is_paused
(
atchan
))
ret
=
DMA_PAUSED
;
dev_vdbg
(
chan2dev
(
chan
),
"tx_status %d: cookie = %d (d%d, u%d)
\n
"
,
...
...
@@ -1046,18 +1052,19 @@ atc_tx_status(struct dma_chan *chan,
static
void
atc_issue_pending
(
struct
dma_chan
*
chan
)
{
struct
at_dma_chan
*
atchan
=
to_at_dma_chan
(
chan
);
unsigned
long
flags
;
dev_vdbg
(
chan2dev
(
chan
),
"issue_pending
\n
"
);
/* Not needed for cyclic transfers */
if
(
test_bit
(
ATC_IS_CYCLIC
,
&
atchan
->
status
))
if
(
atc_chan_is_cyclic
(
atchan
))
return
;
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
if
(
!
atc_chan_is_enabled
(
atchan
))
{
atc_advance_work
(
atchan
);
}
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
}
/**
...
...
@@ -1073,6 +1080,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
struct
at_dma
*
atdma
=
to_at_dma
(
chan
->
device
);
struct
at_desc
*
desc
;
struct
at_dma_slave
*
atslave
;
unsigned
long
flags
;
int
i
;
u32
cfg
;
LIST_HEAD
(
tmp_list
);
...
...
@@ -1116,11 +1124,11 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
list_add_tail
(
&
desc
->
desc_node
,
&
tmp_list
);
}
spin_lock_
bh
(
&
atchan
->
lock
);
spin_lock_
irqsave
(
&
atchan
->
lock
,
flags
);
atchan
->
descs_allocated
=
i
;
list_splice
(
&
tmp_list
,
&
atchan
->
free_list
);
atchan
->
completed_cookie
=
chan
->
cookie
=
1
;
spin_unlock_
bh
(
&
atchan
->
lock
);
spin_unlock_
irqrestore
(
&
atchan
->
lock
,
flags
);
/* channel parameters */
channel_writel
(
atchan
,
CFG
,
cfg
);
...
...
@@ -1293,15 +1301,13 @@ static int __init at_dma_probe(struct platform_device *pdev)
if
(
dma_has_cap
(
DMA_MEMCPY
,
atdma
->
dma_common
.
cap_mask
))
atdma
->
dma_common
.
device_prep_dma_memcpy
=
atc_prep_dma_memcpy
;
if
(
dma_has_cap
(
DMA_SLAVE
,
atdma
->
dma_common
.
cap_mask
))
if
(
dma_has_cap
(
DMA_SLAVE
,
atdma
->
dma_common
.
cap_mask
))
{
atdma
->
dma_common
.
device_prep_slave_sg
=
atc_prep_slave_sg
;
if
(
dma_has_cap
(
DMA_CYCLIC
,
atdma
->
dma_common
.
cap_mask
))
/* controller can do slave DMA: can trigger cyclic transfers */
dma_cap_set
(
DMA_CYCLIC
,
atdma
->
dma_common
.
cap_mask
);
atdma
->
dma_common
.
device_prep_dma_cyclic
=
atc_prep_dma_cyclic
;
if
(
dma_has_cap
(
DMA_SLAVE
,
atdma
->
dma_common
.
cap_mask
)
||
dma_has_cap
(
DMA_CYCLIC
,
atdma
->
dma_common
.
cap_mask
))
atdma
->
dma_common
.
device_control
=
atc_control
;
}
dma_writel
(
atdma
,
EN
,
AT_DMA_ENABLE
);
...
...
@@ -1377,27 +1383,112 @@ static void at_dma_shutdown(struct platform_device *pdev)
clk_disable
(
atdma
->
clk
);
}
static
int
at_dma_prepare
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
at_dma
*
atdma
=
platform_get_drvdata
(
pdev
);
struct
dma_chan
*
chan
,
*
_chan
;
list_for_each_entry_safe
(
chan
,
_chan
,
&
atdma
->
dma_common
.
channels
,
device_node
)
{
struct
at_dma_chan
*
atchan
=
to_at_dma_chan
(
chan
);
/* wait for transaction completion (except in cyclic case) */
if
(
atc_chan_is_enabled
(
atchan
)
&&
!
atc_chan_is_cyclic
(
atchan
))
return
-
EAGAIN
;
}
return
0
;
}
static
void
atc_suspend_cyclic
(
struct
at_dma_chan
*
atchan
)
{
struct
dma_chan
*
chan
=
&
atchan
->
chan_common
;
/* Channel should be paused by user
* do it anyway even if it is not done already */
if
(
!
atc_chan_is_paused
(
atchan
))
{
dev_warn
(
chan2dev
(
chan
),
"cyclic channel not paused, should be done by channel user
\n
"
);
atc_control
(
chan
,
DMA_PAUSE
,
0
);
}
/* now preserve additional data for cyclic operations */
/* next descriptor address in the cyclic list */
atchan
->
save_dscr
=
channel_readl
(
atchan
,
DSCR
);
vdbg_dump_regs
(
atchan
);
}
static
int
at_dma_suspend_noirq
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
at_dma
*
atdma
=
platform_get_drvdata
(
pdev
);
struct
dma_chan
*
chan
,
*
_chan
;
at_dma_off
(
platform_get_drvdata
(
pdev
));
/* preserve data */
list_for_each_entry_safe
(
chan
,
_chan
,
&
atdma
->
dma_common
.
channels
,
device_node
)
{
struct
at_dma_chan
*
atchan
=
to_at_dma_chan
(
chan
);
if
(
atc_chan_is_cyclic
(
atchan
))
atc_suspend_cyclic
(
atchan
);
atchan
->
save_cfg
=
channel_readl
(
atchan
,
CFG
);
}
atdma
->
save_imr
=
dma_readl
(
atdma
,
EBCIMR
);
/* disable DMA controller */
at_dma_off
(
atdma
);
clk_disable
(
atdma
->
clk
);
return
0
;
}
static
void
atc_resume_cyclic
(
struct
at_dma_chan
*
atchan
)
{
struct
at_dma
*
atdma
=
to_at_dma
(
atchan
->
chan_common
.
device
);
/* restore channel status for cyclic descriptors list:
* next descriptor in the cyclic list at the time of suspend */
channel_writel
(
atchan
,
SADDR
,
0
);
channel_writel
(
atchan
,
DADDR
,
0
);
channel_writel
(
atchan
,
CTRLA
,
0
);
channel_writel
(
atchan
,
CTRLB
,
0
);
channel_writel
(
atchan
,
DSCR
,
atchan
->
save_dscr
);
dma_writel
(
atdma
,
CHER
,
atchan
->
mask
);
/* channel pause status should be removed by channel user
* We cannot take the initiative to do it here */
vdbg_dump_regs
(
atchan
);
}
static
int
at_dma_resume_noirq
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
at_dma
*
atdma
=
platform_get_drvdata
(
pdev
);
struct
dma_chan
*
chan
,
*
_chan
;
/* bring back DMA controller */
clk_enable
(
atdma
->
clk
);
dma_writel
(
atdma
,
EN
,
AT_DMA_ENABLE
);
/* clear any pending interrupt */
while
(
dma_readl
(
atdma
,
EBCISR
))
cpu_relax
();
/* restore saved data */
dma_writel
(
atdma
,
EBCIER
,
atdma
->
save_imr
);
list_for_each_entry_safe
(
chan
,
_chan
,
&
atdma
->
dma_common
.
channels
,
device_node
)
{
struct
at_dma_chan
*
atchan
=
to_at_dma_chan
(
chan
);
channel_writel
(
atchan
,
CFG
,
atchan
->
save_cfg
);
if
(
atc_chan_is_cyclic
(
atchan
))
atc_resume_cyclic
(
atchan
);
}
return
0
;
}
static
const
struct
dev_pm_ops
at_dma_dev_pm_ops
=
{
.
prepare
=
at_dma_prepare
,
.
suspend_noirq
=
at_dma_suspend_noirq
,
.
resume_noirq
=
at_dma_resume_noirq
,
};
...
...
drivers/dma/at_hdmac_regs.h
View file @
59ca37f7
...
...
@@ -204,6 +204,9 @@ enum atc_status {
* @status: transmit status information from irq/prep* functions
* to tasklet (use atomic operations)
* @tasklet: bottom half to finish transaction work
* @save_cfg: configuration register that is saved on suspend/resume cycle
* @save_dscr: for cyclic operations, preserve next descriptor address in
* the cyclic list on suspend/resume cycle
* @lock: serializes enqueue/dequeue operations to descriptors lists
* @completed_cookie: identifier for the most recently completed operation
* @active_list: list of descriptors dmaengine is being running on
...
...
@@ -218,6 +221,8 @@ struct at_dma_chan {
u8
mask
;
unsigned
long
status
;
struct
tasklet_struct
tasklet
;
u32
save_cfg
;
u32
save_dscr
;
spinlock_t
lock
;
...
...
@@ -248,6 +253,7 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan)
* @chan_common: common dmaengine dma_device object members
* @ch_regs: memory mapped register base
* @clk: dma controller clock
* @save_imr: interrupt mask register that is saved on suspend/resume cycle
* @all_chan_mask: all channels availlable in a mask
* @dma_desc_pool: base of DMA descriptor region (DMA address)
* @chan: channels table to store at_dma_chan structures
...
...
@@ -256,6 +262,7 @@ struct at_dma {
struct
dma_device
dma_common
;
void
__iomem
*
regs
;
struct
clk
*
clk
;
u32
save_imr
;
u8
all_chan_mask
;
...
...
@@ -355,6 +362,23 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan)
return
!!
(
dma_readl
(
atdma
,
CHSR
)
&
atchan
->
mask
);
}
/**
* atc_chan_is_paused - test channel pause/resume status
* @atchan: channel we want to test status
*/
static
inline
int
atc_chan_is_paused
(
struct
at_dma_chan
*
atchan
)
{
return
test_bit
(
ATC_IS_PAUSED
,
&
atchan
->
status
);
}
/**
* atc_chan_is_cyclic - test if given channel has cyclic property set
* @atchan: channel we want to test status
*/
static
inline
int
atc_chan_is_cyclic
(
struct
at_dma_chan
*
atchan
)
{
return
test_bit
(
ATC_IS_CYCLIC
,
&
atchan
->
status
);
}
/**
* set_desc_eol - set end-of-link to descriptor so it will end transfer
...
...
drivers/dma/dmatest.c
View file @
59ca37f7
...
...
@@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/freezer.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
...
...
@@ -251,6 +252,7 @@ static int dmatest_func(void *data)
int
i
;
thread_name
=
current
->
comm
;
set_freezable_with_signal
();
ret
=
-
ENOMEM
;
...
...
@@ -305,7 +307,8 @@ static int dmatest_func(void *data)
dma_addr_t
dma_srcs
[
src_cnt
];
dma_addr_t
dma_dsts
[
dst_cnt
];
struct
completion
cmp
;
unsigned
long
tmo
=
msecs_to_jiffies
(
timeout
);
unsigned
long
start
,
tmo
,
end
=
0
/* compiler... */
;
bool
reload
=
true
;
u8
align
=
0
;
total_tests
++
;
...
...
@@ -404,7 +407,17 @@ static int dmatest_func(void *data)
}
dma_async_issue_pending
(
chan
);
tmo
=
wait_for_completion_timeout
(
&
cmp
,
tmo
);
do
{
start
=
jiffies
;
if
(
reload
)
end
=
start
+
msecs_to_jiffies
(
timeout
);
else
if
(
end
<=
start
)
end
=
start
+
1
;
tmo
=
wait_for_completion_interruptible_timeout
(
&
cmp
,
end
-
start
);
reload
=
try_to_freeze
();
}
while
(
tmo
==
-
ERESTARTSYS
);
status
=
dma_async_is_tx_complete
(
chan
,
cookie
,
NULL
,
NULL
);
if
(
tmo
==
0
)
{
...
...
@@ -477,6 +490,8 @@ static int dmatest_func(void *data)
pr_notice
(
"%s: terminating after %u tests, %u failures (status %d)
\n
"
,
thread_name
,
total_tests
,
failed_tests
,
ret
);
/* terminate all transfers on specified channels */
chan
->
device
->
device_control
(
chan
,
DMA_TERMINATE_ALL
,
0
);
if
(
iterations
>
0
)
while
(
!
kthread_should_stop
())
{
DECLARE_WAIT_QUEUE_HEAD_ONSTACK
(
wait_dmatest_exit
);
...
...
@@ -499,6 +514,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
list_del
(
&
thread
->
node
);
kfree
(
thread
);
}
/* terminate all transfers on specified channels */
dtc
->
chan
->
device
->
device_control
(
dtc
->
chan
,
DMA_TERMINATE_ALL
,
0
);
kfree
(
dtc
);
}
...
...
drivers/dma/imx-sdma.c
View file @
59ca37f7
...
...
@@ -318,6 +318,7 @@ struct sdma_engine {
dma_addr_t
context_phys
;
struct
dma_device
dma_device
;
struct
clk
*
clk
;
struct
mutex
channel_0_lock
;
struct
sdma_script_start_addrs
*
script_addrs
;
};
...
...
@@ -415,11 +416,15 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
dma_addr_t
buf_phys
;
int
ret
;
mutex_lock
(
&
sdma
->
channel_0_lock
);
buf_virt
=
dma_alloc_coherent
(
NULL
,
size
,
&
buf_phys
,
GFP_KERNEL
);
if
(
!
buf_virt
)
return
-
ENOMEM
;
if
(
!
buf_virt
)
{
ret
=
-
ENOMEM
;
goto
err_out
;
}
bd0
->
mode
.
command
=
C0_SETPM
;
bd0
->
mode
.
status
=
BD_DONE
|
BD_INTR
|
BD_WRAP
|
BD_EXTD
;
...
...
@@ -433,6 +438,9 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
dma_free_coherent
(
NULL
,
size
,
buf_virt
,
buf_phys
);
err_out:
mutex_unlock
(
&
sdma
->
channel_0_lock
);
return
ret
;
}
...
...
@@ -656,6 +664,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
dev_dbg
(
sdma
->
dev
,
"event_mask0 = 0x%08x
\n
"
,
sdmac
->
event_mask0
);
dev_dbg
(
sdma
->
dev
,
"event_mask1 = 0x%08x
\n
"
,
sdmac
->
event_mask1
);
mutex_lock
(
&
sdma
->
channel_0_lock
);
memset
(
context
,
0
,
sizeof
(
*
context
));
context
->
channel_state
.
pc
=
load_address
;
...
...
@@ -676,6 +686,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
ret
=
sdma_run_channel
(
&
sdma
->
channel
[
0
]);
mutex_unlock
(
&
sdma
->
channel_0_lock
);
return
ret
;
}
...
...
@@ -1131,18 +1143,17 @@ static void sdma_add_scripts(struct sdma_engine *sdma,
saddr_arr
[
i
]
=
addr_arr
[
i
];
}
static
int
__init
sdma_get_firmware
(
struct
sdma_engine
*
sdma
,
const
char
*
fw_name
)
static
void
sdma_load_firmware
(
const
struct
firmware
*
fw
,
void
*
context
)
{
const
struct
firmware
*
fw
;
struct
sdma_engine
*
sdma
=
context
;
const
struct
sdma_firmware_header
*
header
;
int
ret
;
const
struct
sdma_script_start_addrs
*
addr
;
unsigned
short
*
ram_code
;
ret
=
request_firmware
(
&
fw
,
fw_name
,
sdma
->
dev
);
if
(
ret
)
return
ret
;
if
(
!
fw
)
{
dev_err
(
sdma
->
dev
,
"firmware not found
\n
"
);
return
;
}
if
(
fw
->
size
<
sizeof
(
*
header
))
goto
err_firmware
;
...
...
@@ -1172,6 +1183,16 @@ static int __init sdma_get_firmware(struct sdma_engine *sdma,
err_firmware:
release_firmware
(
fw
);
}
static
int
__init
sdma_get_firmware
(
struct
sdma_engine
*
sdma
,
const
char
*
fw_name
)
{
int
ret
;
ret
=
request_firmware_nowait
(
THIS_MODULE
,
FW_ACTION_HOTPLUG
,
fw_name
,
sdma
->
dev
,
GFP_KERNEL
,
sdma
,
sdma_load_firmware
);
return
ret
;
}
...
...
@@ -1269,11 +1290,14 @@ static int __init sdma_probe(struct platform_device *pdev)
struct
sdma_platform_data
*
pdata
=
pdev
->
dev
.
platform_data
;
int
i
;
struct
sdma_engine
*
sdma
;
s32
*
saddr_arr
;
sdma
=
kzalloc
(
sizeof
(
*
sdma
),
GFP_KERNEL
);
if
(
!
sdma
)
return
-
ENOMEM
;
mutex_init
(
&
sdma
->
channel_0_lock
);
sdma
->
dev
=
&
pdev
->
dev
;
iores
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
...
...
@@ -1310,6 +1334,11 @@ static int __init sdma_probe(struct platform_device *pdev)
goto
err_alloc
;
}
/* initially no scripts available */
saddr_arr
=
(
s32
*
)
sdma
->
script_addrs
;
for
(
i
=
0
;
i
<
SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1
;
i
++
)
saddr_arr
[
i
]
=
-
EINVAL
;
if
(
of_id
)
pdev
->
id_entry
=
of_id
->
data
;
sdma
->
devtype
=
pdev
->
id_entry
->
driver_data
;
...
...
drivers/dma/mxs-dma.c
View file @
59ca37f7
...
...
@@ -130,6 +130,23 @@ struct mxs_dma_engine {
struct
mxs_dma_chan
mxs_chans
[
MXS_DMA_CHANNELS
];
};
static
inline
void
mxs_dma_clkgate
(
struct
mxs_dma_chan
*
mxs_chan
,
int
enable
)
{
struct
mxs_dma_engine
*
mxs_dma
=
mxs_chan
->
mxs_dma
;
int
chan_id
=
mxs_chan
->
chan
.
chan_id
;
int
set_clr
=
enable
?
MXS_CLR_ADDR
:
MXS_SET_ADDR
;
/* enable apbh channel clock */
if
(
dma_is_apbh
())
{
if
(
apbh_is_old
())
writel
(
1
<<
(
chan_id
+
BP_APBH_CTRL0_CLKGATE_CHANNEL
),
mxs_dma
->
base
+
HW_APBHX_CTRL0
+
set_clr
);
else
writel
(
1
<<
chan_id
,
mxs_dma
->
base
+
HW_APBHX_CTRL0
+
set_clr
);
}
}
static
void
mxs_dma_reset_chan
(
struct
mxs_dma_chan
*
mxs_chan
)
{
struct
mxs_dma_engine
*
mxs_dma
=
mxs_chan
->
mxs_dma
;
...
...
@@ -148,38 +165,21 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
struct
mxs_dma_engine
*
mxs_dma
=
mxs_chan
->
mxs_dma
;
int
chan_id
=
mxs_chan
->
chan
.
chan_id
;
/* clkgate needs to be enabled before writing other registers */
mxs_dma_clkgate
(
mxs_chan
,
1
);
/* set cmd_addr up */
writel
(
mxs_chan
->
ccw_phys
,
mxs_dma
->
base
+
HW_APBHX_CHn_NXTCMDAR
(
chan_id
));
/* enable apbh channel clock */
if
(
dma_is_apbh
())
{
if
(
apbh_is_old
())
writel
(
1
<<
(
chan_id
+
BP_APBH_CTRL0_CLKGATE_CHANNEL
),
mxs_dma
->
base
+
HW_APBHX_CTRL0
+
MXS_CLR_ADDR
);
else
writel
(
1
<<
chan_id
,
mxs_dma
->
base
+
HW_APBHX_CTRL0
+
MXS_CLR_ADDR
);
}
/* write 1 to SEMA to kick off the channel */
writel
(
1
,
mxs_dma
->
base
+
HW_APBHX_CHn_SEMA
(
chan_id
));
}
static
void
mxs_dma_disable_chan
(
struct
mxs_dma_chan
*
mxs_chan
)
{
struct
mxs_dma_engine
*
mxs_dma
=
mxs_chan
->
mxs_dma
;
int
chan_id
=
mxs_chan
->
chan
.
chan_id
;
/* disable apbh channel clock */
if
(
dma_is_apbh
())
{
if
(
apbh_is_old
())
writel
(
1
<<
(
chan_id
+
BP_APBH_CTRL0_CLKGATE_CHANNEL
),
mxs_dma
->
base
+
HW_APBHX_CTRL0
+
MXS_SET_ADDR
);
else
writel
(
1
<<
chan_id
,
mxs_dma
->
base
+
HW_APBHX_CTRL0
+
MXS_SET_ADDR
);
}
mxs_dma_clkgate
(
mxs_chan
,
0
);
mxs_chan
->
status
=
DMA_SUCCESS
;
}
...
...
@@ -338,7 +338,10 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
if
(
ret
)
goto
err_clk
;
/* clkgate needs to be enabled for reset to finish */
mxs_dma_clkgate
(
mxs_chan
,
1
);
mxs_dma_reset_chan
(
mxs_chan
);
mxs_dma_clkgate
(
mxs_chan
,
0
);
dma_async_tx_descriptor_init
(
&
mxs_chan
->
desc
,
chan
);
mxs_chan
->
desc
.
tx_submit
=
mxs_dma_tx_submit
;
...
...
drivers/dma/pl330.c
View file @
59ca37f7
...
...
@@ -17,6 +17,8 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#define NR_DEFAULT_DESC 16
...
...
@@ -68,6 +70,14 @@ struct dma_pl330_chan {
* NULL if the channel is available to be acquired.
*/
void
*
pl330_chid
;
/* For D-to-M and M-to-D channels */
int
burst_sz
;
/* the peripheral fifo width */
int
burst_len
;
/* the number of burst */
dma_addr_t
fifo_addr
;
/* for cyclic capability */
bool
cyclic
;
};
struct
dma_pl330_dmac
{
...
...
@@ -83,6 +93,8 @@ struct dma_pl330_dmac {
/* Peripheral channels connected to this DMAC */
struct
dma_pl330_chan
*
peripherals
;
/* keep at end */
struct
clk
*
clk
;
};
struct
dma_pl330_desc
{
...
...
@@ -152,6 +164,31 @@ static inline void free_desc_list(struct list_head *list)
spin_unlock_irqrestore
(
&
pdmac
->
pool_lock
,
flags
);
}
static
inline
void
handle_cyclic_desc_list
(
struct
list_head
*
list
)
{
struct
dma_pl330_desc
*
desc
;
struct
dma_pl330_chan
*
pch
;
unsigned
long
flags
;
if
(
list_empty
(
list
))
return
;
list_for_each_entry
(
desc
,
list
,
node
)
{
dma_async_tx_callback
callback
;
/* Change status to reload it */
desc
->
status
=
PREP
;
pch
=
desc
->
pchan
;
callback
=
desc
->
txd
.
callback
;
if
(
callback
)
callback
(
desc
->
txd
.
callback_param
);
}
spin_lock_irqsave
(
&
pch
->
lock
,
flags
);
list_splice_tail_init
(
list
,
&
pch
->
work_list
);
spin_unlock_irqrestore
(
&
pch
->
lock
,
flags
);
}
static
inline
void
fill_queue
(
struct
dma_pl330_chan
*
pch
)
{
struct
dma_pl330_desc
*
desc
;
...
...
@@ -205,6 +242,9 @@ static void pl330_tasklet(unsigned long data)
spin_unlock_irqrestore
(
&
pch
->
lock
,
flags
);
if
(
pch
->
cyclic
)
handle_cyclic_desc_list
(
&
list
);
else
free_desc_list
(
&
list
);
}
...
...
@@ -236,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irqsave
(
&
pch
->
lock
,
flags
);
pch
->
completed
=
chan
->
cookie
=
1
;
pch
->
cyclic
=
false
;
pch
->
pl330_chid
=
pl330_request_channel
(
&
pdmac
->
pif
);
if
(
!
pch
->
pl330_chid
)
{
...
...
@@ -253,25 +294,52 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
static
int
pl330_control
(
struct
dma_chan
*
chan
,
enum
dma_ctrl_cmd
cmd
,
unsigned
long
arg
)
{
struct
dma_pl330_chan
*
pch
=
to_pchan
(
chan
);
struct
dma_pl330_desc
*
desc
;
struct
dma_pl330_desc
*
desc
,
*
_dt
;
unsigned
long
flags
;
struct
dma_pl330_dmac
*
pdmac
=
pch
->
dmac
;
struct
dma_slave_config
*
slave_config
;
LIST_HEAD
(
list
);
/* Only supports DMA_TERMINATE_ALL */
if
(
cmd
!=
DMA_TERMINATE_ALL
)
return
-
ENXIO
;
switch
(
cmd
)
{
case
DMA_TERMINATE_ALL
:
spin_lock_irqsave
(
&
pch
->
lock
,
flags
);
/* FLUSH the PL330 Channel thread */
pl330_chan_ctrl
(
pch
->
pl330_chid
,
PL330_OP_FLUSH
);
/* Mark all desc done */
list_for_each_entry
(
desc
,
&
pch
->
work_list
,
node
)
list_for_each_entry_safe
(
desc
,
_dt
,
&
pch
->
work_list
,
node
)
{
desc
->
status
=
DONE
;
pch
->
completed
=
desc
->
txd
.
cookie
;
list_move_tail
(
&
desc
->
node
,
&
list
);
}
list_splice_tail_init
(
&
list
,
&
pdmac
->
desc_pool
);
spin_unlock_irqrestore
(
&
pch
->
lock
,
flags
);
pl330_tasklet
((
unsigned
long
)
pch
);
break
;
case
DMA_SLAVE_CONFIG
:
slave_config
=
(
struct
dma_slave_config
*
)
arg
;
if
(
slave_config
->
direction
==
DMA_TO_DEVICE
)
{
if
(
slave_config
->
dst_addr
)
pch
->
fifo_addr
=
slave_config
->
dst_addr
;
if
(
slave_config
->
dst_addr_width
)
pch
->
burst_sz
=
__ffs
(
slave_config
->
dst_addr_width
);
if
(
slave_config
->
dst_maxburst
)
pch
->
burst_len
=
slave_config
->
dst_maxburst
;
}
else
if
(
slave_config
->
direction
==
DMA_FROM_DEVICE
)
{
if
(
slave_config
->
src_addr
)
pch
->
fifo_addr
=
slave_config
->
src_addr
;
if
(
slave_config
->
src_addr_width
)
pch
->
burst_sz
=
__ffs
(
slave_config
->
src_addr_width
);
if
(
slave_config
->
src_maxburst
)
pch
->
burst_len
=
slave_config
->
src_maxburst
;
}
break
;
default:
dev_err
(
pch
->
dmac
->
pif
.
dev
,
"Not supported command.
\n
"
);
return
-
ENXIO
;
}
return
0
;
}
...
...
@@ -288,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
pl330_release_channel
(
pch
->
pl330_chid
);
pch
->
pl330_chid
=
NULL
;
if
(
pch
->
cyclic
)
list_splice_tail_init
(
&
pch
->
work_list
,
&
pch
->
dmac
->
desc_pool
);
spin_unlock_irqrestore
(
&
pch
->
lock
,
flags
);
}
...
...
@@ -453,7 +524,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
if
(
peri
)
{
desc
->
req
.
rqtype
=
peri
->
rqtype
;
desc
->
req
.
peri
=
p
eri
->
peri
_id
;
desc
->
req
.
peri
=
p
ch
->
chan
.
chan
_id
;
}
else
{
desc
->
req
.
rqtype
=
MEMTOMEM
;
desc
->
req
.
peri
=
0
;
...
...
@@ -524,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
return
burst_len
;
}
static
struct
dma_async_tx_descriptor
*
pl330_prep_dma_cyclic
(
struct
dma_chan
*
chan
,
dma_addr_t
dma_addr
,
size_t
len
,
size_t
period_len
,
enum
dma_data_direction
direction
)
{
struct
dma_pl330_desc
*
desc
;
struct
dma_pl330_chan
*
pch
=
to_pchan
(
chan
);
dma_addr_t
dst
;
dma_addr_t
src
;
desc
=
pl330_get_desc
(
pch
);
if
(
!
desc
)
{
dev_err
(
pch
->
dmac
->
pif
.
dev
,
"%s:%d Unable to fetch desc
\n
"
,
__func__
,
__LINE__
);
return
NULL
;
}
switch
(
direction
)
{
case
DMA_TO_DEVICE
:
desc
->
rqcfg
.
src_inc
=
1
;
desc
->
rqcfg
.
dst_inc
=
0
;
src
=
dma_addr
;
dst
=
pch
->
fifo_addr
;
break
;
case
DMA_FROM_DEVICE
:
desc
->
rqcfg
.
src_inc
=
0
;
desc
->
rqcfg
.
dst_inc
=
1
;
src
=
pch
->
fifo_addr
;
dst
=
dma_addr
;
break
;
default:
dev_err
(
pch
->
dmac
->
pif
.
dev
,
"%s:%d Invalid dma direction
\n
"
,
__func__
,
__LINE__
);
return
NULL
;
}
desc
->
rqcfg
.
brst_size
=
pch
->
burst_sz
;
desc
->
rqcfg
.
brst_len
=
1
;
pch
->
cyclic
=
true
;
fill_px
(
&
desc
->
px
,
dst
,
src
,
period_len
);
return
&
desc
->
txd
;
}
static
struct
dma_async_tx_descriptor
*
pl330_prep_dma_memcpy
(
struct
dma_chan
*
chan
,
dma_addr_t
dst
,
dma_addr_t
src
,
size_t
len
,
unsigned
long
flags
)
...
...
@@ -579,7 +695,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct
dma_pl330_peri
*
peri
=
chan
->
private
;
struct
scatterlist
*
sg
;
unsigned
long
flags
;
int
i
,
burst_size
;
int
i
;
dma_addr_t
addr
;
if
(
unlikely
(
!
pch
||
!
sgl
||
!
sg_len
||
!
peri
))
...
...
@@ -595,8 +711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return
NULL
;
}
addr
=
peri
->
fifo_addr
;
burst_size
=
peri
->
burst_sz
;
addr
=
pch
->
fifo_addr
;
first
=
NULL
;
...
...
@@ -644,7 +759,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
sg_dma_address
(
sg
),
addr
,
sg_dma_len
(
sg
));
}
desc
->
rqcfg
.
brst_size
=
burst_size
;
desc
->
rqcfg
.
brst_size
=
pch
->
burst_sz
;
desc
->
rqcfg
.
brst_len
=
1
;
}
...
...
@@ -696,6 +811,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto
probe_err1
;
}
pdmac
->
clk
=
clk_get
(
&
adev
->
dev
,
"dma"
);
if
(
IS_ERR
(
pdmac
->
clk
))
{
dev_err
(
&
adev
->
dev
,
"Cannot get operation clock.
\n
"
);
ret
=
-
EINVAL
;
goto
probe_err1
;
}
amba_set_drvdata
(
adev
,
pdmac
);
#ifdef CONFIG_PM_RUNTIME
/* to use the runtime PM helper functions */
pm_runtime_enable
(
&
adev
->
dev
);
/* enable the power domain */
if
(
pm_runtime_get_sync
(
&
adev
->
dev
))
{
dev_err
(
&
adev
->
dev
,
"failed to get runtime pm
\n
"
);
ret
=
-
ENODEV
;
goto
probe_err1
;
}
#else
/* enable dma clk */
clk_enable
(
pdmac
->
clk
);
#endif
irq
=
adev
->
irq
[
0
];
ret
=
request_irq
(
irq
,
pl330_irq_handler
,
0
,
dev_name
(
&
adev
->
dev
),
pi
);
...
...
@@ -732,6 +871,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
case
MEMTODEV
:
case
DEVTOMEM
:
dma_cap_set
(
DMA_SLAVE
,
pd
->
cap_mask
);
dma_cap_set
(
DMA_CYCLIC
,
pd
->
cap_mask
);
break
;
default:
dev_err
(
&
adev
->
dev
,
"DEVTODEV Not Supported
\n
"
);
...
...
@@ -760,6 +900,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pd
->
device_alloc_chan_resources
=
pl330_alloc_chan_resources
;
pd
->
device_free_chan_resources
=
pl330_free_chan_resources
;
pd
->
device_prep_dma_memcpy
=
pl330_prep_dma_memcpy
;
pd
->
device_prep_dma_cyclic
=
pl330_prep_dma_cyclic
;
pd
->
device_tx_status
=
pl330_tx_status
;
pd
->
device_prep_slave_sg
=
pl330_prep_slave_sg
;
pd
->
device_control
=
pl330_control
;
...
...
@@ -771,8 +912,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto
probe_err4
;
}
amba_set_drvdata
(
adev
,
pdmac
);
dev_info
(
&
adev
->
dev
,
"Loaded driver for PL330 DMAC-%d
\n
"
,
adev
->
periphid
);
dev_info
(
&
adev
->
dev
,
...
...
@@ -833,6 +972,13 @@ static int __devexit pl330_remove(struct amba_device *adev)
res
=
&
adev
->
res
;
release_mem_region
(
res
->
start
,
resource_size
(
res
));
#ifdef CONFIG_PM_RUNTIME
pm_runtime_put
(
&
adev
->
dev
);
pm_runtime_disable
(
&
adev
->
dev
);
#else
clk_disable
(
pdmac
->
clk
);
#endif
kfree
(
pdmac
);
return
0
;
...
...
@@ -846,10 +992,49 @@ static struct amba_id pl330_ids[] = {
{
0
,
0
},
};
#ifdef CONFIG_PM_RUNTIME
static
int
pl330_runtime_suspend
(
struct
device
*
dev
)
{
struct
dma_pl330_dmac
*
pdmac
=
dev_get_drvdata
(
dev
);
if
(
!
pdmac
)
{
dev_err
(
dev
,
"failed to get dmac
\n
"
);
return
-
ENODEV
;
}
clk_disable
(
pdmac
->
clk
);
return
0
;
}
static
int
pl330_runtime_resume
(
struct
device
*
dev
)
{
struct
dma_pl330_dmac
*
pdmac
=
dev_get_drvdata
(
dev
);
if
(
!
pdmac
)
{
dev_err
(
dev
,
"failed to get dmac
\n
"
);
return
-
ENODEV
;
}
clk_enable
(
pdmac
->
clk
);
return
0
;
}
#else
#define pl330_runtime_suspend NULL
#define pl330_runtime_resume NULL
#endif
/* CONFIG_PM_RUNTIME */
static
const
struct
dev_pm_ops
pl330_pm_ops
=
{
.
runtime_suspend
=
pl330_runtime_suspend
,
.
runtime_resume
=
pl330_runtime_resume
,
};
static
struct
amba_driver
pl330_driver
=
{
.
drv
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"dma-pl330"
,
.
pm
=
&
pl330_pm_ops
,
},
.
id_table
=
pl330_ids
,
.
probe
=
pl330_probe
,
...
...
drivers/mmc/host/s3cmci.c
View file @
59ca37f7
...
...
@@ -913,9 +913,9 @@ static void finalize_request(struct s3cmci_host *host)
}
static
void
s3cmci_dma_setup
(
struct
s3cmci_host
*
host
,
enum
s3c2410_dmasrc
source
)
enum
dma_data_direction
source
)
{
static
enum
s3c2410_dmasrc
last_source
=
-
1
;
static
enum
dma_data_direction
last_source
=
-
1
;
static
int
setup_ok
;
if
(
last_source
==
source
)
...
...
@@ -1087,7 +1087,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
BUG_ON
((
data
->
flags
&
BOTH_DIR
)
==
BOTH_DIR
);
s3cmci_dma_setup
(
host
,
rw
?
S3C2410_DMASRC_MEM
:
S3C2410_DMASRC_HW
);
s3cmci_dma_setup
(
host
,
rw
?
DMA_TO_DEVICE
:
DMA_FROM_DEVICE
);
s3c2410_dma_ctrl
(
host
->
dma
,
S3C2410_DMAOP_FLUSH
);
dma_len
=
dma_map_sg
(
mmc_dev
(
host
->
mmc
),
data
->
sg
,
data
->
sg_len
,
...
...
drivers/spi/spi-s3c64xx.c
View file @
59ca37f7
...
...
@@ -131,6 +131,12 @@
#define RXBUSY (1<<2)
#define TXBUSY (1<<3)
struct
s3c64xx_spi_dma_data
{
unsigned
ch
;
enum
dma_data_direction
direction
;
enum
dma_ch
dmach
;
};
/**
* struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
* @clk: Pointer to the spi clock.
...
...
@@ -164,13 +170,14 @@ struct s3c64xx_spi_driver_data {
struct
work_struct
work
;
struct
list_head
queue
;
spinlock_t
lock
;
enum
dma_ch
rx_dmach
;
enum
dma_ch
tx_dmach
;
unsigned
long
sfr_start
;
struct
completion
xfer_completion
;
unsigned
state
;
unsigned
cur_mode
,
cur_bpw
;
unsigned
cur_speed
;
struct
s3c64xx_spi_dma_data
rx_dma
;
struct
s3c64xx_spi_dma_data
tx_dma
;
struct
samsung_dma_ops
*
ops
;
};
static
struct
s3c2410_dma_client
s3c64xx_spi_dma_client
=
{
...
...
@@ -226,6 +233,78 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
writel
(
val
,
regs
+
S3C64XX_SPI_CH_CFG
);
}
static
void
s3c64xx_spi_dmacb
(
void
*
data
)
{
struct
s3c64xx_spi_driver_data
*
sdd
;
struct
s3c64xx_spi_dma_data
*
dma
=
data
;
unsigned
long
flags
;
if
(
dma
->
direction
==
DMA_FROM_DEVICE
)
sdd
=
container_of
(
data
,
struct
s3c64xx_spi_driver_data
,
rx_dma
);
else
sdd
=
container_of
(
data
,
struct
s3c64xx_spi_driver_data
,
tx_dma
);
spin_lock_irqsave
(
&
sdd
->
lock
,
flags
);
if
(
dma
->
direction
==
DMA_FROM_DEVICE
)
{
sdd
->
state
&=
~
RXBUSY
;
if
(
!
(
sdd
->
state
&
TXBUSY
))
complete
(
&
sdd
->
xfer_completion
);
}
else
{
sdd
->
state
&=
~
TXBUSY
;
if
(
!
(
sdd
->
state
&
RXBUSY
))
complete
(
&
sdd
->
xfer_completion
);
}
spin_unlock_irqrestore
(
&
sdd
->
lock
,
flags
);
}
static
void
prepare_dma
(
struct
s3c64xx_spi_dma_data
*
dma
,
unsigned
len
,
dma_addr_t
buf
)
{
struct
s3c64xx_spi_driver_data
*
sdd
;
struct
samsung_dma_prep_info
info
;
if
(
dma
->
direction
==
DMA_FROM_DEVICE
)
sdd
=
container_of
((
void
*
)
dma
,
struct
s3c64xx_spi_driver_data
,
rx_dma
);
else
sdd
=
container_of
((
void
*
)
dma
,
struct
s3c64xx_spi_driver_data
,
tx_dma
);
info
.
cap
=
DMA_SLAVE
;
info
.
len
=
len
;
info
.
fp
=
s3c64xx_spi_dmacb
;
info
.
fp_param
=
dma
;
info
.
direction
=
dma
->
direction
;
info
.
buf
=
buf
;
sdd
->
ops
->
prepare
(
dma
->
ch
,
&
info
);
sdd
->
ops
->
trigger
(
dma
->
ch
);
}
static
int
acquire_dma
(
struct
s3c64xx_spi_driver_data
*
sdd
)
{
struct
samsung_dma_info
info
;
sdd
->
ops
=
samsung_dma_get_ops
();
info
.
cap
=
DMA_SLAVE
;
info
.
client
=
&
s3c64xx_spi_dma_client
;
info
.
width
=
sdd
->
cur_bpw
/
8
;
info
.
direction
=
sdd
->
rx_dma
.
direction
;
info
.
fifo
=
sdd
->
sfr_start
+
S3C64XX_SPI_RX_DATA
;
sdd
->
rx_dma
.
ch
=
sdd
->
ops
->
request
(
sdd
->
rx_dma
.
dmach
,
&
info
);
info
.
direction
=
sdd
->
tx_dma
.
direction
;
info
.
fifo
=
sdd
->
sfr_start
+
S3C64XX_SPI_TX_DATA
;
sdd
->
tx_dma
.
ch
=
sdd
->
ops
->
request
(
sdd
->
tx_dma
.
dmach
,
&
info
);
return
1
;
}
static
void
enable_datapath
(
struct
s3c64xx_spi_driver_data
*
sdd
,
struct
spi_device
*
spi
,
struct
spi_transfer
*
xfer
,
int
dma_mode
)
...
...
@@ -258,10 +337,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
chcfg
|=
S3C64XX_SPI_CH_TXCH_ON
;
if
(
dma_mode
)
{
modecfg
|=
S3C64XX_SPI_MODE_TXDMA_ON
;
s3c2410_dma_config
(
sdd
->
tx_dmach
,
sdd
->
cur_bpw
/
8
);
s3c2410_dma_enqueue
(
sdd
->
tx_dmach
,
(
void
*
)
sdd
,
xfer
->
tx_dma
,
xfer
->
len
);
s3c2410_dma_ctrl
(
sdd
->
tx_dmach
,
S3C2410_DMAOP_START
);
prepare_dma
(
&
sdd
->
tx_dma
,
xfer
->
len
,
xfer
->
tx_dma
);
}
else
{
switch
(
sdd
->
cur_bpw
)
{
case
32
:
...
...
@@ -293,10 +369,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
writel
(((
xfer
->
len
*
8
/
sdd
->
cur_bpw
)
&
0xffff
)
|
S3C64XX_SPI_PACKET_CNT_EN
,
regs
+
S3C64XX_SPI_PACKET_CNT
);
s3c2410_dma_config
(
sdd
->
rx_dmach
,
sdd
->
cur_bpw
/
8
);
s3c2410_dma_enqueue
(
sdd
->
rx_dmach
,
(
void
*
)
sdd
,
xfer
->
rx_dma
,
xfer
->
len
);
s3c2410_dma_ctrl
(
sdd
->
rx_dmach
,
S3C2410_DMAOP_START
);
prepare_dma
(
&
sdd
->
rx_dma
,
xfer
->
len
,
xfer
->
rx_dma
);
}
}
...
...
@@ -482,46 +555,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
}
}
static
void
s3c64xx_spi_dma_rxcb
(
struct
s3c2410_dma_chan
*
chan
,
void
*
buf_id
,
int
size
,
enum
s3c2410_dma_buffresult
res
)
{
struct
s3c64xx_spi_driver_data
*
sdd
=
buf_id
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
sdd
->
lock
,
flags
);
if
(
res
==
S3C2410_RES_OK
)
sdd
->
state
&=
~
RXBUSY
;
else
dev_err
(
&
sdd
->
pdev
->
dev
,
"DmaAbrtRx-%d
\n
"
,
size
);
/* If the other done */
if
(
!
(
sdd
->
state
&
TXBUSY
))
complete
(
&
sdd
->
xfer_completion
);
spin_unlock_irqrestore
(
&
sdd
->
lock
,
flags
);
}
static
void
s3c64xx_spi_dma_txcb
(
struct
s3c2410_dma_chan
*
chan
,
void
*
buf_id
,
int
size
,
enum
s3c2410_dma_buffresult
res
)
{
struct
s3c64xx_spi_driver_data
*
sdd
=
buf_id
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
sdd
->
lock
,
flags
);
if
(
res
==
S3C2410_RES_OK
)
sdd
->
state
&=
~
TXBUSY
;
else
dev_err
(
&
sdd
->
pdev
->
dev
,
"DmaAbrtTx-%d
\n
"
,
size
);
/* If the other done */
if
(
!
(
sdd
->
state
&
RXBUSY
))
complete
(
&
sdd
->
xfer_completion
);
spin_unlock_irqrestore
(
&
sdd
->
lock
,
flags
);
}
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
static
int
s3c64xx_spi_map_mssg
(
struct
s3c64xx_spi_driver_data
*
sdd
,
...
...
@@ -696,12 +729,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
if
(
use_dma
)
{
if
(
xfer
->
tx_buf
!=
NULL
&&
(
sdd
->
state
&
TXBUSY
))
s3c2410_dma_ctrl
(
sdd
->
tx_dmach
,
S3C2410_DMAOP_FLUSH
);
sdd
->
ops
->
stop
(
sdd
->
tx_dma
.
ch
);
if
(
xfer
->
rx_buf
!=
NULL
&&
(
sdd
->
state
&
RXBUSY
))
s3c2410_dma_ctrl
(
sdd
->
rx_dmach
,
S3C2410_DMAOP_FLUSH
);
sdd
->
ops
->
stop
(
sdd
->
rx_dma
.
ch
);
}
goto
out
;
...
...
@@ -739,30 +770,6 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
msg
->
complete
(
msg
->
context
);
}
static
int
acquire_dma
(
struct
s3c64xx_spi_driver_data
*
sdd
)
{
if
(
s3c2410_dma_request
(
sdd
->
rx_dmach
,
&
s3c64xx_spi_dma_client
,
NULL
)
<
0
)
{
dev_err
(
&
sdd
->
pdev
->
dev
,
"cannot get RxDMA
\n
"
);
return
0
;
}
s3c2410_dma_set_buffdone_fn
(
sdd
->
rx_dmach
,
s3c64xx_spi_dma_rxcb
);
s3c2410_dma_devconfig
(
sdd
->
rx_dmach
,
S3C2410_DMASRC_HW
,
sdd
->
sfr_start
+
S3C64XX_SPI_RX_DATA
);
if
(
s3c2410_dma_request
(
sdd
->
tx_dmach
,
&
s3c64xx_spi_dma_client
,
NULL
)
<
0
)
{
dev_err
(
&
sdd
->
pdev
->
dev
,
"cannot get TxDMA
\n
"
);
s3c2410_dma_free
(
sdd
->
rx_dmach
,
&
s3c64xx_spi_dma_client
);
return
0
;
}
s3c2410_dma_set_buffdone_fn
(
sdd
->
tx_dmach
,
s3c64xx_spi_dma_txcb
);
s3c2410_dma_devconfig
(
sdd
->
tx_dmach
,
S3C2410_DMASRC_MEM
,
sdd
->
sfr_start
+
S3C64XX_SPI_TX_DATA
);
return
1
;
}
static
void
s3c64xx_spi_work
(
struct
work_struct
*
work
)
{
struct
s3c64xx_spi_driver_data
*
sdd
=
container_of
(
work
,
...
...
@@ -799,8 +806,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
spin_unlock_irqrestore
(
&
sdd
->
lock
,
flags
);
/* Free DMA channels */
s
3c2410_dma_free
(
sdd
->
tx_dma
ch
,
&
s3c64xx_spi_dma_client
);
s
3c2410_dma_free
(
sdd
->
rx_dma
ch
,
&
s3c64xx_spi_dma_client
);
s
dd
->
ops
->
release
(
sdd
->
rx_dma
.
ch
,
&
s3c64xx_spi_dma_client
);
s
dd
->
ops
->
release
(
sdd
->
tx_dma
.
ch
,
&
s3c64xx_spi_dma_client
);
}
static
int
s3c64xx_spi_transfer
(
struct
spi_device
*
spi
,
...
...
@@ -1017,8 +1024,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
sdd
->
cntrlr_info
=
sci
;
sdd
->
pdev
=
pdev
;
sdd
->
sfr_start
=
mem_res
->
start
;
sdd
->
tx_dmach
=
dmatx_res
->
start
;
sdd
->
rx_dmach
=
dmarx_res
->
start
;
sdd
->
tx_dma
.
dmach
=
dmatx_res
->
start
;
sdd
->
tx_dma
.
direction
=
DMA_TO_DEVICE
;
sdd
->
rx_dma
.
dmach
=
dmarx_res
->
start
;
sdd
->
rx_dma
.
direction
=
DMA_FROM_DEVICE
;
sdd
->
cur_bpw
=
8
;
...
...
@@ -1106,7 +1115,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
pdev
->
id
,
master
->
num_chipselect
);
dev_dbg
(
&
pdev
->
dev
,
"
\t
IOmem=[0x%x-0x%x]
\t
DMA=[Rx-%d, Tx-%d]
\n
"
,
mem_res
->
end
,
mem_res
->
start
,
sdd
->
rx_dma
ch
,
sdd
->
tx_
dmach
);
sdd
->
rx_dma
.
dmach
,
sdd
->
tx_dma
.
dmach
);
return
0
;
...
...
include/linux/amba/pl08x.h
View file @
59ca37f7
...
...
@@ -47,6 +47,9 @@ enum {
* @muxval: a number usually used to poke into some mux regiser to
* mux in the signal to this channel
* @cctl_opt: default options for the channel control register
* @device_fc: Flow Controller Settings for ccfg register. Only valid for slave
* channels. Fill with 'true' if peripheral should be flow controller. Direction
* will be selected at Runtime.
* @addr: source/target address in physical memory for this DMA channel,
* can be the address of a FIFO register for burst requests for example.
* This can be left undefined if the PrimeCell API is used for configuring
...
...
@@ -65,6 +68,7 @@ struct pl08x_channel_data {
int
max_signal
;
u32
muxval
;
u32
cctl
;
bool
device_fc
;
dma_addr_t
addr
;
bool
circular_buffer
;
bool
single
;
...
...
@@ -77,13 +81,11 @@ struct pl08x_channel_data {
* @addr: current address
* @maxwidth: the maximum width of a transfer on this bus
* @buswidth: the width of this bus in bytes: 1, 2 or 4
* @fill_bytes: bytes required to fill to the next bus memory boundary
*/
struct
pl08x_bus_data
{
dma_addr_t
addr
;
u8
maxwidth
;
u8
buswidth
;
size_t
fill_bytes
;
};
/**
...
...
@@ -105,8 +107,16 @@ struct pl08x_phy_chan {
/**
* struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
* @tx: async tx descriptor
* @node: node for txd list for channels
* @src_addr: src address of txd
* @dst_addr: dst address of txd
* @len: transfer len in bytes
* @direction: direction of transfer
* @llis_bus: DMA memory address (physical) start for the LLIs
* @llis_va: virtual memory address start for the LLIs
* @cctl: control reg values for current txd
* @ccfg: config reg values for current txd
*/
struct
pl08x_txd
{
struct
dma_async_tx_descriptor
tx
;
...
...
include/linux/amba/pl330.h
View file @
59ca37f7
...
...
@@ -19,12 +19,8 @@ struct dma_pl330_peri {
* Peri_Req i/f of the DMAC that is
* peripheral could be reached from.
*/
u8
peri_id
;
/*
{0, 31}
*/
u8
peri_id
;
/*
specific dma id
*/
enum
pl330_reqtype
rqtype
;
/* For M->D and D->M Channels */
int
burst_sz
;
/* in power of 2 */
dma_addr_t
fifo_addr
;
};
struct
dma_pl330_platdata
{
...
...
include/linux/dmaengine.h
View file @
59ca37f7
...
...
@@ -24,8 +24,7 @@
#include <linux/device.h>
#include <linux/uio.h>
#include <linux/dma-direction.h>
struct
scatterlist
;
#include <linux/scatterlist.h>
/**
* typedef dma_cookie_t - an opaque DMA cookie
...
...
@@ -519,6 +518,16 @@ static inline int dmaengine_slave_config(struct dma_chan *chan,
(
unsigned
long
)
config
);
}
static
inline
struct
dma_async_tx_descriptor
*
dmaengine_prep_slave_single
(
struct
dma_chan
*
chan
,
void
*
buf
,
size_t
len
,
enum
dma_data_direction
dir
,
unsigned
long
flags
)
{
struct
scatterlist
sg
;
sg_init_one
(
&
sg
,
buf
,
len
);
return
chan
->
device
->
device_prep_slave_sg
(
chan
,
&
sg
,
1
,
dir
,
flags
);
}
static
inline
int
dmaengine_terminate_all
(
struct
dma_chan
*
chan
)
{
return
dmaengine_device_control
(
chan
,
DMA_TERMINATE_ALL
,
0
);
...
...
sound/soc/samsung/ac97.c
View file @
59ca37f7
...
...
@@ -271,7 +271,10 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
writel
(
ac_glbctrl
,
s3c_ac97
.
regs
+
S3C_AC97_GLBCTRL
);
s3c2410_dma_ctrl
(
dma_data
->
channel
,
S3C2410_DMAOP_STARTED
);
if
(
!
dma_data
->
ops
)
dma_data
->
ops
=
samsung_dma_get_ops
();
dma_data
->
ops
->
started
(
dma_data
->
channel
);
return
0
;
}
...
...
@@ -317,7 +320,10 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
writel
(
ac_glbctrl
,
s3c_ac97
.
regs
+
S3C_AC97_GLBCTRL
);
s3c2410_dma_ctrl
(
dma_data
->
channel
,
S3C2410_DMAOP_STARTED
);
if
(
!
dma_data
->
ops
)
dma_data
->
ops
=
samsung_dma_get_ops
();
dma_data
->
ops
->
started
(
dma_data
->
channel
);
return
0
;
}
...
...
sound/soc/samsung/dma.c
View file @
59ca37f7
...
...
@@ -54,7 +54,6 @@ struct runtime_data {
spinlock_t
lock
;
int
state
;
unsigned
int
dma_loaded
;
unsigned
int
dma_limit
;
unsigned
int
dma_period
;
dma_addr_t
dma_start
;
dma_addr_t
dma_pos
;
...
...
@@ -62,77 +61,79 @@ struct runtime_data {
struct
s3c_dma_params
*
params
;
};
static
void
audio_buffdone
(
void
*
data
);
/* dma_enqueue
*
* place a dma buffer onto the queue for the dma system
* to handle.
*/
*/
static
void
dma_enqueue
(
struct
snd_pcm_substream
*
substream
)
{
struct
runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
dma_addr_t
pos
=
prtd
->
dma_pos
;
unsigned
int
limit
;
int
ret
;
struct
samsung_dma_prep_info
dma_info
;
pr_debug
(
"Entered %s
\n
"
,
__func__
);
if
(
s3c_dma_has_circular
())
limit
=
(
prtd
->
dma_end
-
prtd
->
dma_start
)
/
prtd
->
dma_period
;
else
limit
=
prtd
->
dma_limit
;
pr_debug
(
"%s: loaded %d, limit %d
\n
"
,
__func__
,
prtd
->
dma_loaded
,
limit
);
while
(
prtd
->
dma_loaded
<
limit
)
{
unsigned
long
len
=
prtd
->
dma_period
;
dma_info
.
cap
=
(
samsung_dma_has_circular
()
?
DMA_CYCLIC
:
DMA_SLAVE
);
dma_info
.
direction
=
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
?
DMA_TO_DEVICE
:
DMA_FROM_DEVICE
);
dma_info
.
fp
=
audio_buffdone
;
dma_info
.
fp_param
=
substream
;
dma_info
.
period
=
prtd
->
dma_period
;
dma_info
.
len
=
prtd
->
dma_period
*
limit
;
while
(
prtd
->
dma_loaded
<
limit
)
{
pr_debug
(
"dma_loaded: %d
\n
"
,
prtd
->
dma_loaded
);
if
((
pos
+
len
)
>
prtd
->
dma_end
)
{
len
=
prtd
->
dma_end
-
pos
;
pr_debug
(
"%s: corrected dma len %ld
\n
"
,
__func__
,
len
);
if
((
pos
+
dma_info
.
period
)
>
prtd
->
dma_end
)
{
dma_info
.
period
=
prtd
->
dma_end
-
pos
;
pr_debug
(
"%s: corrected dma len %ld
\n
"
,
__func__
,
dma_info
.
period
);
}
ret
=
s3c2410_dma_enqueue
(
prtd
->
params
->
channel
,
substream
,
pos
,
len
);
dma_info
.
buf
=
pos
;
prtd
->
params
->
ops
->
prepare
(
prtd
->
params
->
ch
,
&
dma_info
);
if
(
ret
==
0
)
{
prtd
->
dma_loaded
++
;
pos
+=
prtd
->
dma_period
;
if
(
pos
>=
prtd
->
dma_end
)
pos
=
prtd
->
dma_start
;
}
else
break
;
}
prtd
->
dma_pos
=
pos
;
}
static
void
audio_buffdone
(
struct
s3c2410_dma_chan
*
channel
,
void
*
dev_id
,
int
size
,
enum
s3c2410_dma_buffresult
result
)
static
void
audio_buffdone
(
void
*
data
)
{
struct
snd_pcm_substream
*
substream
=
d
ev_id
;
struct
runtime_data
*
prtd
;
struct
snd_pcm_substream
*
substream
=
d
ata
;
struct
runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
pr_debug
(
"Entered %s
\n
"
,
__func__
);
if
(
result
==
S3C2410_RES_ABORT
||
result
==
S3C2410_RES_ERR
)
return
;
prtd
=
substream
->
runtime
->
private_data
;
if
(
prtd
->
state
&
ST_RUNNING
)
{
prtd
->
dma_pos
+=
prtd
->
dma_period
;
if
(
prtd
->
dma_pos
>=
prtd
->
dma_end
)
prtd
->
dma_pos
=
prtd
->
dma_start
;
if
(
substream
)
snd_pcm_period_elapsed
(
substream
);
spin_lock
(
&
prtd
->
lock
);
if
(
prtd
->
state
&
ST_RUNNING
&&
!
s3c
_dma_has_circular
())
{
if
(
!
samsung
_dma_has_circular
())
{
prtd
->
dma_loaded
--
;
dma_enqueue
(
substream
);
}
spin_unlock
(
&
prtd
->
lock
);
}
}
static
int
dma_hw_params
(
struct
snd_pcm_substream
*
substream
,
...
...
@@ -144,8 +145,7 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
unsigned
long
totbytes
=
params_buffer_bytes
(
params
);
struct
s3c_dma_params
*
dma
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
int
ret
=
0
;
struct
samsung_dma_info
dma_info
;
pr_debug
(
"Entered %s
\n
"
,
__func__
);
...
...
@@ -163,30 +163,26 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
pr_debug
(
"params %p, client %p, channel %d
\n
"
,
prtd
->
params
,
prtd
->
params
->
client
,
prtd
->
params
->
channel
);
ret
=
s3c2410_dma_request
(
prtd
->
params
->
channel
,
prtd
->
params
->
client
,
NULL
);
if
(
ret
<
0
)
{
printk
(
KERN_ERR
"failed to get dma channel
\n
"
)
;
return
ret
;
}
/* use the circular buffering if we have it available. */
if
(
s3c_dma_has_circular
())
s3c2410_dma_setflags
(
prtd
->
params
->
channel
,
S3C2410_DMAF_CIRCULAR
);
prtd
->
params
->
ops
=
samsung_dma_get_ops
();
dma_info
.
cap
=
(
samsung_dma_has_circular
()
?
DMA_CYCLIC
:
DMA_SLAVE
);
dma_info
.
client
=
prtd
->
params
->
client
;
dma_info
.
direction
=
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
?
DMA_TO_DEVICE
:
DMA_FROM_DEVICE
);
dma_info
.
width
=
prtd
->
params
->
dma_size
;
dma_info
.
fifo
=
prtd
->
params
->
dma_addr
;
prtd
->
params
->
ch
=
prtd
->
params
->
ops
->
request
(
prtd
->
params
->
channel
,
&
dma_info
);
}
s3c2410_dma_set_buffdone_fn
(
prtd
->
params
->
channel
,
audio_buffdone
);
snd_pcm_set_runtime_buffer
(
substream
,
&
substream
->
dma_buffer
);
runtime
->
dma_bytes
=
totbytes
;
spin_lock_irq
(
&
prtd
->
lock
);
prtd
->
dma_loaded
=
0
;
prtd
->
dma_limit
=
runtime
->
hw
.
periods_min
;
prtd
->
dma_period
=
params_period_bytes
(
params
);
prtd
->
dma_start
=
runtime
->
dma_addr
;
prtd
->
dma_pos
=
prtd
->
dma_start
;
...
...
@@ -206,7 +202,8 @@ static int dma_hw_free(struct snd_pcm_substream *substream)
snd_pcm_set_runtime_buffer
(
substream
,
NULL
);
if
(
prtd
->
params
)
{
s3c2410_dma_free
(
prtd
->
params
->
channel
,
prtd
->
params
->
client
);
prtd
->
params
->
ops
->
release
(
prtd
->
params
->
ch
,
prtd
->
params
->
client
);
prtd
->
params
=
NULL
;
}
...
...
@@ -225,23 +222,9 @@ static int dma_prepare(struct snd_pcm_substream *substream)
if
(
!
prtd
->
params
)
return
0
;
/* channel needs configuring for mem=>device, increment memory addr,
* sync to pclk, half-word transfers to the IIS-FIFO. */
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
{
s3c2410_dma_devconfig
(
prtd
->
params
->
channel
,
S3C2410_DMASRC_MEM
,
prtd
->
params
->
dma_addr
);
}
else
{
s3c2410_dma_devconfig
(
prtd
->
params
->
channel
,
S3C2410_DMASRC_HW
,
prtd
->
params
->
dma_addr
);
}
s3c2410_dma_config
(
prtd
->
params
->
channel
,
prtd
->
params
->
dma_size
);
/* flush the DMA channel */
s3c2410_dma_ctrl
(
prtd
->
params
->
channel
,
S3C2410_DMAOP_FLUSH
);
prtd
->
params
->
ops
->
flush
(
prtd
->
params
->
ch
);
prtd
->
dma_loaded
=
0
;
prtd
->
dma_pos
=
prtd
->
dma_start
;
...
...
@@ -265,14 +248,14 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
case
SNDRV_PCM_TRIGGER_RESUME
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
prtd
->
state
|=
ST_RUNNING
;
s3c2410_dma_ctrl
(
prtd
->
params
->
channel
,
S3C2410_DMAOP_START
);
prtd
->
params
->
ops
->
trigger
(
prtd
->
params
->
ch
);
break
;
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
prtd
->
state
&=
~
ST_RUNNING
;
s3c2410_dma_ctrl
(
prtd
->
params
->
channel
,
S3C2410_DMAOP_STOP
);
prtd
->
params
->
ops
->
stop
(
prtd
->
params
->
ch
);
break
;
default:
...
...
@@ -291,21 +274,12 @@ dma_pointer(struct snd_pcm_substream *substream)
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
runtime_data
*
prtd
=
runtime
->
private_data
;
unsigned
long
res
;
dma_addr_t
src
,
dst
;
pr_debug
(
"Entered %s
\n
"
,
__func__
);
spin_lock
(
&
prtd
->
lock
);
s3c2410_dma_getposition
(
prtd
->
params
->
channel
,
&
src
,
&
dst
);
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_CAPTURE
)
res
=
dst
-
prtd
->
dma_start
;
else
res
=
src
-
prtd
->
dma_start
;
spin_unlock
(
&
prtd
->
lock
);
res
=
prtd
->
dma_pos
-
prtd
->
dma_start
;
pr_debug
(
"Pointer
%x %x
\n
"
,
src
,
dst
);
pr_debug
(
"Pointer
offset: %lu
\n
"
,
res
);
/* we seem to be getting the odd error from the pcm library due
* to out-of-bounds pointers. this is maybe due to the dma engine
...
...
sound/soc/samsung/dma.h
View file @
59ca37f7
...
...
@@ -6,7 +6,7 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* ALSA PCM interface for the Samsung S
3C24xx CPU
* ALSA PCM interface for the Samsung S
oC
*/
#ifndef _S3C_AUDIO_H
...
...
@@ -17,6 +17,8 @@ struct s3c_dma_params {
int
channel
;
/* Channel ID */
dma_addr_t
dma_addr
;
int
dma_size
;
/* Size of the DMA transfer */
unsigned
ch
;
struct
samsung_dma_ops
*
ops
;
};
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment