Commit 0745c9a5 authored by Vinod Koul's avatar Vinod Koul Committed by Vinod Koul

Merge branch 'samsung_dma' into next

parents f8de8f4c 51ddf31d
...@@ -11,7 +11,7 @@ if ARCH_EXYNOS4 ...@@ -11,7 +11,7 @@ if ARCH_EXYNOS4
config CPU_EXYNOS4210 config CPU_EXYNOS4210
bool bool
select S3C_PL330_DMA select SAMSUNG_DMADEV
help help
Enable EXYNOS4210 CPU support Enable EXYNOS4210 CPU support
......
...@@ -43,6 +43,11 @@ static struct clk clk_sclk_usbphy1 = { ...@@ -43,6 +43,11 @@ static struct clk clk_sclk_usbphy1 = {
.name = "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) static int exynos4_clksrc_mask_top_ctrl(struct clk *clk, int enable)
{ {
return s5p_gatectrl(S5P_CLKSRC_MASK_TOP, clk, enable); return s5p_gatectrl(S5P_CLKSRC_MASK_TOP, clk, enable);
...@@ -454,12 +459,12 @@ static struct clk init_clocks_off[] = { ...@@ -454,12 +459,12 @@ static struct clk init_clocks_off[] = {
.enable = exynos4_clk_ip_fsys_ctrl, .enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 10), .ctrlbit = (1 << 10),
}, { }, {
.name = "pdma", .name = "dma",
.devname = "s3c-pl330.0", .devname = "s3c-pl330.0",
.enable = exynos4_clk_ip_fsys_ctrl, .enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 0), .ctrlbit = (1 << 0),
}, { }, {
.name = "pdma", .name = "dma",
.devname = "s3c-pl330.1", .devname = "s3c-pl330.1",
.enable = exynos4_clk_ip_fsys_ctrl, .enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 1), .ctrlbit = (1 << 1),
...@@ -1210,5 +1215,7 @@ void __init exynos4_register_clocks(void) ...@@ -1210,5 +1215,7 @@ void __init exynos4_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_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(); s3c_pwmclk_init();
} }
...@@ -21,151 +21,228 @@ ...@@ -21,151 +21,228 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/platform_device.h>
#include <linux/dma-mapping.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/devs.h>
#include <plat/irqs.h> #include <plat/irqs.h>
#include <mach/map.h> #include <mach/map.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <mach/dma.h>
#include <plat/s3c-pl330-pdata.h>
static u64 dma_dmamask = DMA_BIT_MASK(32); static u64 dma_dmamask = DMA_BIT_MASK(32);
static struct resource exynos4_pdma0_resource[] = { struct dma_pl330_peri pdma0_peri[28] = {
[0] = { {
.start = EXYNOS4_PA_PDMA0, .peri_id = (u8)DMACH_PCM0_RX,
.end = EXYNOS4_PA_PDMA0 + SZ_4K, .rqtype = DEVTOMEM,
.flags = IORESOURCE_MEM, }, {
}, .peri_id = (u8)DMACH_PCM0_TX,
[1] = { .rqtype = MEMTODEV,
.start = IRQ_PDMA0, }, {
.end = IRQ_PDMA0, .peri_id = (u8)DMACH_PCM2_RX,
.flags = IORESOURCE_IRQ, .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 = { struct dma_pl330_platdata exynos4_pdma0_pdata = {
.peri = { .nr_valid_peri = ARRAY_SIZE(pdma0_peri),
[0] = DMACH_PCM0_RX, .peri = pdma0_peri,
[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,
},
}; };
static struct platform_device exynos4_device_pdma0 = { struct amba_device exynos4_device_pdma0 = {
.name = "s3c-pl330",
.id = 0,
.num_resources = ARRAY_SIZE(exynos4_pdma0_resource),
.resource = exynos4_pdma0_resource,
.dev = { .dev = {
.init_name = "dma-pl330.0",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos4_pdma0_pdata, .platform_data = &exynos4_pdma0_pdata,
}, },
}; .res = {
.start = EXYNOS4_PA_PDMA0,
static struct resource exynos4_pdma1_resource[] = { .end = EXYNOS4_PA_PDMA0 + SZ_4K,
[0] = {
.start = EXYNOS4_PA_PDMA1,
.end = EXYNOS4_PA_PDMA1 + SZ_4K,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { .irq = {IRQ_PDMA0, NO_IRQ},
.start = IRQ_PDMA1, .periphid = 0x00041330,
.end = IRQ_PDMA1,
.flags = IORESOURCE_IRQ,
},
}; };
static struct s3c_pl330_platdata exynos4_pdma1_pdata = { struct dma_pl330_peri pdma1_peri[25] = {
.peri = { {
[0] = DMACH_PCM0_RX, .peri_id = (u8)DMACH_PCM0_RX,
[1] = DMACH_PCM0_TX, .rqtype = DEVTOMEM,
[2] = DMACH_PCM1_RX, }, {
[3] = DMACH_PCM1_TX, .peri_id = (u8)DMACH_PCM0_TX,
[4] = DMACH_MSM_REQ1, .rqtype = MEMTODEV,
[5] = DMACH_MSM_REQ3, }, {
[6] = DMACH_SPI1_RX, .peri_id = (u8)DMACH_PCM1_RX,
[7] = DMACH_SPI1_TX, .rqtype = DEVTOMEM,
[8] = DMACH_I2S0S_TX, }, {
[9] = DMACH_I2S0_RX, .peri_id = (u8)DMACH_PCM1_TX,
[10] = DMACH_I2S0_TX, .rqtype = MEMTODEV,
[11] = DMACH_I2S1_RX, }, {
[12] = DMACH_I2S1_TX, .peri_id = (u8)DMACH_MSM_REQ1,
[13] = DMACH_UART0_RX, }, {
[14] = DMACH_UART0_TX, .peri_id = (u8)DMACH_MSM_REQ3,
[15] = DMACH_UART1_RX, }, {
[16] = DMACH_UART1_TX, .peri_id = (u8)DMACH_SPI1_RX,
[17] = DMACH_UART3_RX, .rqtype = DEVTOMEM,
[18] = DMACH_UART3_TX, }, {
[19] = DMACH_SLIMBUS1_RX, .peri_id = (u8)DMACH_SPI1_TX,
[20] = DMACH_SLIMBUS1_TX, .rqtype = MEMTODEV,
[21] = DMACH_SLIMBUS3_RX, }, {
[22] = DMACH_SLIMBUS3_TX, .peri_id = (u8)DMACH_I2S0S_TX,
[23] = DMACH_SLIMBUS5_RX, .rqtype = MEMTODEV,
[24] = DMACH_SLIMBUS5_TX, }, {
[25] = DMACH_SLIMBUS0AUX_RX, .peri_id = (u8)DMACH_I2S0_RX,
[26] = DMACH_SLIMBUS0AUX_TX, .rqtype = DEVTOMEM,
[27] = DMACH_SPDIF, }, {
[28] = DMACH_MAX, .peri_id = (u8)DMACH_I2S0_TX,
[29] = DMACH_MAX, .rqtype = MEMTODEV,
[30] = DMACH_MAX, }, {
[31] = DMACH_MAX, .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 = { struct dma_pl330_platdata exynos4_pdma1_pdata = {
.name = "s3c-pl330", .nr_valid_peri = ARRAY_SIZE(pdma1_peri),
.id = 1, .peri = pdma1_peri,
.num_resources = ARRAY_SIZE(exynos4_pdma1_resource), };
.resource = exynos4_pdma1_resource,
struct amba_device exynos4_device_pdma1 = {
.dev = { .dev = {
.init_name = "dma-pl330.1",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos4_pdma1_pdata, .platform_data = &exynos4_pdma1_pdata,
}, },
}; .res = {
.start = EXYNOS4_PA_PDMA1,
static struct platform_device *exynos4_dmacs[] __initdata = { .end = EXYNOS4_PA_PDMA1 + SZ_4K,
&exynos4_device_pdma0, .flags = IORESOURCE_MEM,
&exynos4_device_pdma1, },
.irq = {IRQ_PDMA1, NO_IRQ},
.periphid = 0x00041330,
}; };
static int __init exynos4_dma_init(void) static int __init exynos4_dma_init(void)
{ {
platform_add_devices(exynos4_dmacs, ARRAY_SIZE(exynos4_dmacs)); amba_device_register(&exynos4_device_pdma0, &iomem_resource);
return 0; return 0;
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H #ifndef __MACH_DMA_H
#define __MACH_DMA_H #define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */ /* This platform uses the common DMA API driver for PL330 */
#include <plat/s3c-dma-pl330.h> #include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */ #endif /* __MACH_DMA_H */
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#ifndef __ASM_ARCH_DMA_H #ifndef __ASM_ARCH_DMA_H
#define __ASM_ARCH_DMA_H __FILE__ #define __ASM_ARCH_DMA_H __FILE__
#include <plat/dma.h>
#include <linux/sysdev.h> #include <linux/sysdev.h>
#define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */ #define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */
...@@ -51,6 +50,18 @@ enum dma_ch { ...@@ -51,6 +50,18 @@ enum dma_ch {
DMACH_MAX, /* the end entry */ 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 */ #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */
/* we have 4 dma channels */ /* we have 4 dma channels */
...@@ -163,7 +174,7 @@ struct s3c2410_dma_chan { ...@@ -163,7 +174,7 @@ struct s3c2410_dma_chan {
struct s3c2410_dma_client *client; struct s3c2410_dma_client *client;
/* channel configuration */ /* channel configuration */
enum s3c2410_dmasrc source; enum dma_data_direction source;
enum dma_ch req_ch; enum dma_ch req_ch;
unsigned long dev_addr; unsigned long dev_addr;
unsigned long load_timeout; unsigned long load_timeout;
...@@ -196,9 +207,4 @@ struct s3c2410_dma_chan { ...@@ -196,9 +207,4 @@ struct s3c2410_dma_chan {
typedef unsigned long dma_device_t; typedef unsigned long dma_device_t;
static inline bool s3c_dma_has_circular(void)
{
return false;
}
#endif /* __ASM_ARCH_DMA_H */ #endif /* __ASM_ARCH_DMA_H */
...@@ -148,11 +148,11 @@ static struct s3c24xx_dma_map __initdata s3c2412_dma_mappings[] = { ...@@ -148,11 +148,11 @@ static struct s3c24xx_dma_map __initdata s3c2412_dma_mappings[] = {
static void s3c2412_dma_direction(struct s3c2410_dma_chan *chan, static void s3c2412_dma_direction(struct s3c2410_dma_chan *chan,
struct s3c24xx_dma_map *map, struct s3c24xx_dma_map *map,
enum s3c2410_dmasrc dir) enum dma_data_direction dir)
{ {
unsigned long chsel; unsigned long chsel;
if (dir == S3C2410_DMASRC_HW) if (dir == DMA_FROM_DEVICE)
chsel = map->channels_rx[0]; chsel = map->channels_rx[0];
else else
chsel = map->channels[0]; chsel = map->channels[0];
......
...@@ -147,14 +147,14 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, ...@@ -147,14 +147,14 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan,
u32 control0, control1; u32 control0, control1;
switch (chan->source) { switch (chan->source) {
case S3C2410_DMASRC_HW: case DMA_FROM_DEVICE:
src = chan->dev_addr; src = chan->dev_addr;
dst = data; dst = data;
control0 = PL080_CONTROL_SRC_AHB2; control0 = PL080_CONTROL_SRC_AHB2;
control0 |= PL080_CONTROL_DST_INCR; control0 |= PL080_CONTROL_DST_INCR;
break; break;
case S3C2410_DMASRC_MEM: case DMA_TO_DEVICE:
src = data; src = data;
dst = chan->dev_addr; dst = chan->dev_addr;
control0 = PL080_CONTROL_DST_AHB2; control0 = PL080_CONTROL_DST_AHB2;
...@@ -416,7 +416,7 @@ EXPORT_SYMBOL(s3c2410_dma_enqueue); ...@@ -416,7 +416,7 @@ EXPORT_SYMBOL(s3c2410_dma_enqueue);
int s3c2410_dma_devconfig(enum dma_ch channel, int s3c2410_dma_devconfig(enum dma_ch channel,
enum s3c2410_dmasrc source, enum dma_data_direction source,
unsigned long devaddr) unsigned long devaddr)
{ {
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
...@@ -437,11 +437,11 @@ int s3c2410_dma_devconfig(enum dma_ch channel, ...@@ -437,11 +437,11 @@ int s3c2410_dma_devconfig(enum dma_ch channel,
pr_debug("%s: peripheral %d\n", __func__, peripheral); pr_debug("%s: peripheral %d\n", __func__, peripheral);
switch (source) { switch (source) {
case S3C2410_DMASRC_HW: case DMA_FROM_DEVICE:
config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT; config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT; config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT;
break; break;
case S3C2410_DMASRC_MEM: case DMA_TO_DEVICE:
config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT; config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT; config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT;
break; break;
......
...@@ -58,11 +58,15 @@ enum dma_ch { ...@@ -58,11 +58,15 @@ enum dma_ch {
DMACH_MAX /* the end */ DMACH_MAX /* the end */
}; };
static __inline__ bool s3c_dma_has_circular(void) static inline bool samsung_dma_has_circular(void)
{ {
return true; return true;
} }
static inline bool samsung_dma_is_dmadev(void)
{
return false;
}
#define S3C2410_DMAF_CIRCULAR (1 << 0) #define S3C2410_DMAF_CIRCULAR (1 << 0)
#include <plat/dma.h> #include <plat/dma.h>
...@@ -95,7 +99,7 @@ struct s3c2410_dma_chan { ...@@ -95,7 +99,7 @@ struct s3c2410_dma_chan {
unsigned char peripheral; unsigned char peripheral;
unsigned int flags; unsigned int flags;
enum s3c2410_dmasrc source; enum dma_data_direction source;
dma_addr_t dev_addr; dma_addr_t dev_addr;
......
...@@ -9,14 +9,14 @@ if ARCH_S5P64X0 ...@@ -9,14 +9,14 @@ if ARCH_S5P64X0
config CPU_S5P6440 config CPU_S5P6440
bool bool
select S3C_PL330_DMA select SAMSUNG_DMADEV
select S5P_HRT select S5P_HRT
help help
Enable S5P6440 CPU support Enable S5P6440 CPU support
config CPU_S5P6450 config CPU_S5P6450
bool bool
select S3C_PL330_DMA select SAMSUNG_DMADEV
select S5P_HRT select S5P_HRT
help help
Enable S5P6450 CPU support Enable S5P6450 CPU support
......
...@@ -146,7 +146,7 @@ static struct clk init_clocks_off[] = { ...@@ -146,7 +146,7 @@ static struct clk init_clocks_off[] = {
.enable = s5p64x0_hclk0_ctrl, .enable = s5p64x0_hclk0_ctrl,
.ctrlbit = (1 << 8), .ctrlbit = (1 << 8),
}, { }, {
.name = "pdma", .name = "dma",
.parent = &clk_hclk_low.clk, .parent = &clk_hclk_low.clk,
.enable = s5p64x0_hclk0_ctrl, .enable = s5p64x0_hclk0_ctrl,
.ctrlbit = (1 << 12), .ctrlbit = (1 << 12),
...@@ -499,6 +499,11 @@ static struct clksrc_clk *sysclks[] = { ...@@ -499,6 +499,11 @@ static struct clksrc_clk *sysclks[] = {
&clk_pclk_low, &clk_pclk_low,
}; };
static struct clk dummy_apb_pclk = {
.name = "apb_pclk",
.id = -1,
};
void __init_or_cpufreq s5p6440_setup_clocks(void) void __init_or_cpufreq s5p6440_setup_clocks(void)
{ {
struct clk *xtal_clk; struct clk *xtal_clk;
...@@ -581,5 +586,7 @@ void __init s5p6440_register_clocks(void) ...@@ -581,5 +586,7 @@ void __init s5p6440_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_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(); s3c_pwmclk_init();
} }
...@@ -179,7 +179,7 @@ static struct clk init_clocks_off[] = { ...@@ -179,7 +179,7 @@ static struct clk init_clocks_off[] = {
.enable = s5p64x0_hclk0_ctrl, .enable = s5p64x0_hclk0_ctrl,
.ctrlbit = (1 << 3), .ctrlbit = (1 << 3),
}, { }, {
.name = "pdma", .name = "dma",
.parent = &clk_hclk_low.clk, .parent = &clk_hclk_low.clk,
.enable = s5p64x0_hclk0_ctrl, .enable = s5p64x0_hclk0_ctrl,
.ctrlbit = (1 << 12), .ctrlbit = (1 << 12),
...@@ -553,6 +553,11 @@ static struct clksrc_clk *sysclks[] = { ...@@ -553,6 +553,11 @@ static struct clksrc_clk *sysclks[] = {
&clk_sclk_audio0, &clk_sclk_audio0,
}; };
static struct clk dummy_apb_pclk = {
.name = "apb_pclk",
.id = -1,
};
void __init_or_cpufreq s5p6450_setup_clocks(void) void __init_or_cpufreq s5p6450_setup_clocks(void)
{ {
struct clk *xtal_clk; struct clk *xtal_clk;
...@@ -632,5 +637,7 @@ void __init s5p6450_register_clocks(void) ...@@ -632,5 +637,7 @@ void __init s5p6450_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_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(); s3c_pwmclk_init();
} }
...@@ -21,128 +21,219 @@ ...@@ -21,128 +21,219 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/platform_device.h>
#include <linux/dma-mapping.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/map.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <mach/regs-clock.h> #include <mach/regs-clock.h>
#include <mach/dma.h>
#include <plat/devs.h> #include <plat/devs.h>
#include <plat/s3c-pl330-pdata.h> #include <plat/irqs.h>
static u64 dma_dmamask = DMA_BIT_MASK(32); static u64 dma_dmamask = DMA_BIT_MASK(32);
static struct resource s5p64x0_pdma_resource[] = { struct dma_pl330_peri s5p6440_pdma_peri[22] = {
[0] = { {
.start = S5P64X0_PA_PDMA, .peri_id = (u8)DMACH_UART0_RX,
.end = S5P64X0_PA_PDMA + SZ_4K, .rqtype = DEVTOMEM,
.flags = IORESOURCE_MEM, }, {
}, .peri_id = (u8)DMACH_UART0_TX,
[1] = { .rqtype = MEMTODEV,
.start = IRQ_DMA0, }, {
.end = IRQ_DMA0, .peri_id = (u8)DMACH_UART1_RX,
.flags = IORESOURCE_IRQ, .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 = { struct dma_pl330_platdata s5p6440_pdma_pdata = {
.peri = { .nr_valid_peri = ARRAY_SIZE(s5p6440_pdma_peri),
[0] = DMACH_UART0_RX, .peri = s5p6440_pdma_peri,
[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,
},
}; };
static struct s3c_pl330_platdata s5p6450_pdma_pdata = { struct dma_pl330_peri s5p6450_pdma_peri[32] = {
.peri = { {
[0] = DMACH_UART0_RX, .peri_id = (u8)DMACH_UART0_RX,
[1] = DMACH_UART0_TX, .rqtype = DEVTOMEM,
[2] = DMACH_UART1_RX, }, {
[3] = DMACH_UART1_TX, .peri_id = (u8)DMACH_UART0_TX,
[4] = DMACH_UART2_RX, .rqtype = MEMTODEV,
[5] = DMACH_UART2_TX, }, {
[6] = DMACH_UART3_RX, .peri_id = (u8)DMACH_UART1_RX,
[7] = DMACH_UART3_TX, .rqtype = DEVTOMEM,
[8] = DMACH_UART4_RX, }, {
[9] = DMACH_UART4_TX, .peri_id = (u8)DMACH_UART1_TX,
[10] = DMACH_PCM0_TX, .rqtype = MEMTODEV,
[11] = DMACH_PCM0_RX, }, {
[12] = DMACH_I2S0_TX, .peri_id = (u8)DMACH_UART2_RX,
[13] = DMACH_I2S0_RX, .rqtype = DEVTOMEM,
[14] = DMACH_SPI0_TX, }, {
[15] = DMACH_SPI0_RX, .peri_id = (u8)DMACH_UART2_TX,
[16] = DMACH_PCM1_TX, .rqtype = MEMTODEV,
[17] = DMACH_PCM1_RX, }, {
[18] = DMACH_PCM2_TX, .peri_id = (u8)DMACH_UART3_RX,
[19] = DMACH_PCM2_RX, .rqtype = DEVTOMEM,
[20] = DMACH_SPI1_TX, }, {
[21] = DMACH_SPI1_RX, .peri_id = (u8)DMACH_UART3_TX,
[22] = DMACH_USI_TX, .rqtype = MEMTODEV,
[23] = DMACH_USI_RX, }, {
[24] = DMACH_MAX, .peri_id = (u8)DMACH_UART4_RX,
[25] = DMACH_I2S1_TX, .rqtype = DEVTOMEM,
[26] = DMACH_I2S1_RX, }, {
[27] = DMACH_I2S2_TX, .peri_id = (u8)DMACH_UART4_TX,
[28] = DMACH_I2S2_RX, .rqtype = MEMTODEV,
[29] = DMACH_PWM, }, {
[30] = DMACH_UART5_RX, .peri_id = (u8)DMACH_PCM0_TX,
[31] = DMACH_UART5_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 = { struct dma_pl330_platdata s5p6450_pdma_pdata = {
.name = "s3c-pl330", .nr_valid_peri = ARRAY_SIZE(s5p6450_pdma_peri),
.id = -1, .peri = s5p6450_pdma_peri,
.num_resources = ARRAY_SIZE(s5p64x0_pdma_resource), };
.resource = s5p64x0_pdma_resource,
struct amba_device s5p64x0_device_pdma = {
.dev = { .dev = {
.init_name = "dma-pl330",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .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) static int __init s5p64x0_dma_init(void)
{ {
unsigned int id; unsigned int id = __raw_readl(S5P64X0_SYS_ID) & 0xFF000;
id = __raw_readl(S5P64X0_SYS_ID) & 0xFF000;
if (id == 0x50000) if (id == 0x50000)
s5p64x0_device_pdma.dev.platform_data = &s5p6450_pdma_pdata; s5p64x0_device_pdma.dev.platform_data = &s5p6450_pdma_pdata;
else else
s5p64x0_device_pdma.dev.platform_data = &s5p6440_pdma_pdata; 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; return 0;
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H #ifndef __MACH_DMA_H
#define __MACH_DMA_H #define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */ /* This platform uses the common common DMA API driver for PL330 */
#include <plat/s3c-dma-pl330.h> #include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */ #endif /* __MACH_DMA_H */
...@@ -10,7 +10,7 @@ if ARCH_S5PC100 ...@@ -10,7 +10,7 @@ if ARCH_S5PC100
config CPU_S5PC100 config CPU_S5PC100
bool bool
select S5P_EXT_INT select S5P_EXT_INT
select S3C_PL330_DMA select SAMSUNG_DMADEV
help help
Enable S5PC100 CPU support Enable S5PC100 CPU support
......
...@@ -33,6 +33,11 @@ static struct clk s5p_clk_otgphy = { ...@@ -33,6 +33,11 @@ static struct clk s5p_clk_otgphy = {
.name = "otg_phy", .name = "otg_phy",
}; };
static struct clk dummy_apb_pclk = {
.name = "apb_pclk",
.id = -1,
};
static struct clk *clk_src_mout_href_list[] = { static struct clk *clk_src_mout_href_list[] = {
[0] = &s5p_clk_27m, [0] = &s5p_clk_27m,
[1] = &clk_fin_hpll, [1] = &clk_fin_hpll,
...@@ -454,13 +459,13 @@ static struct clk init_clocks_off[] = { ...@@ -454,13 +459,13 @@ static struct clk init_clocks_off[] = {
.enable = s5pc100_d1_0_ctrl, .enable = s5pc100_d1_0_ctrl,
.ctrlbit = (1 << 2), .ctrlbit = (1 << 2),
}, { }, {
.name = "pdma", .name = "dma",
.devname = "s3c-pl330.1", .devname = "s3c-pl330.1",
.parent = &clk_div_d1_bus.clk, .parent = &clk_div_d1_bus.clk,
.enable = s5pc100_d1_0_ctrl, .enable = s5pc100_d1_0_ctrl,
.ctrlbit = (1 << 1), .ctrlbit = (1 << 1),
}, { }, {
.name = "pdma", .name = "dma",
.devname = "s3c-pl330.0", .devname = "s3c-pl330.0",
.parent = &clk_div_d1_bus.clk, .parent = &clk_div_d1_bus.clk,
.enable = s5pc100_d1_0_ctrl, .enable = s5pc100_d1_0_ctrl,
...@@ -1276,5 +1281,7 @@ void __init s5pc100_register_clocks(void) ...@@ -1276,5 +1281,7 @@ void __init s5pc100_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_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(); s3c_pwmclk_init();
} }
/* /* 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. * Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com> * Jaswinder Singh <jassi.brar@samsung.com>
* *
...@@ -17,150 +21,245 @@ ...@@ -17,150 +21,245 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/platform_device.h>
#include <linux/dma-mapping.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/devs.h>
#include <plat/irqs.h>
#include <mach/map.h> #include <mach/map.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <mach/dma.h>
#include <plat/s3c-pl330-pdata.h>
static u64 dma_dmamask = DMA_BIT_MASK(32); static u64 dma_dmamask = DMA_BIT_MASK(32);
static struct resource s5pc100_pdma0_resource[] = { struct dma_pl330_peri pdma0_peri[30] = {
[0] = { {
.start = S5PC100_PA_PDMA0, .peri_id = (u8)DMACH_UART0_RX,
.end = S5PC100_PA_PDMA0 + SZ_4K, .rqtype = DEVTOMEM,
.flags = IORESOURCE_MEM, }, {
}, .peri_id = (u8)DMACH_UART0_TX,
[1] = { .rqtype = MEMTODEV,
.start = IRQ_PDMA0, }, {
.end = IRQ_PDMA0, .peri_id = (u8)DMACH_UART1_RX,
.flags = IORESOURCE_IRQ, .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 = { struct dma_pl330_platdata s5pc100_pdma0_pdata = {
.peri = { .nr_valid_peri = ARRAY_SIZE(pdma0_peri),
[0] = DMACH_UART0_RX, .peri = pdma0_peri,
[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,
},
}; };
static struct platform_device s5pc100_device_pdma0 = { struct amba_device s5pc100_device_pdma0 = {
.name = "s3c-pl330",
.id = 0,
.num_resources = ARRAY_SIZE(s5pc100_pdma0_resource),
.resource = s5pc100_pdma0_resource,
.dev = { .dev = {
.init_name = "dma-pl330.0",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s5pc100_pdma0_pdata, .platform_data = &s5pc100_pdma0_pdata,
}, },
}; .res = {
.start = S5PC100_PA_PDMA0,
static struct resource s5pc100_pdma1_resource[] = { .end = S5PC100_PA_PDMA0 + SZ_4K,
[0] = {
.start = S5PC100_PA_PDMA1,
.end = S5PC100_PA_PDMA1 + SZ_4K,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { .irq = {IRQ_PDMA0, NO_IRQ},
.start = IRQ_PDMA1, .periphid = 0x00041330,
.end = IRQ_PDMA1,
.flags = IORESOURCE_IRQ,
},
}; };
static struct s3c_pl330_platdata s5pc100_pdma1_pdata = { struct dma_pl330_peri pdma1_peri[30] = {
.peri = { {
[0] = DMACH_UART0_RX, .peri_id = (u8)DMACH_UART0_RX,
[1] = DMACH_UART0_TX, .rqtype = DEVTOMEM,
[2] = DMACH_UART1_RX, }, {
[3] = DMACH_UART1_TX, .peri_id = (u8)DMACH_UART0_TX,
[4] = DMACH_UART2_RX, .rqtype = MEMTODEV,
[5] = DMACH_UART2_TX, }, {
[6] = DMACH_UART3_RX, .peri_id = (u8)DMACH_UART1_RX,
[7] = DMACH_UART3_TX, .rqtype = DEVTOMEM,
[8] = DMACH_IRDA, }, {
[9] = DMACH_I2S0_RX, .peri_id = (u8)DMACH_UART1_TX,
[10] = DMACH_I2S0_TX, .rqtype = MEMTODEV,
[11] = DMACH_I2S0S_TX, }, {
[12] = DMACH_I2S1_RX, .peri_id = (u8)DMACH_UART2_RX,
[13] = DMACH_I2S1_TX, .rqtype = DEVTOMEM,
[14] = DMACH_I2S2_RX, }, {
[15] = DMACH_I2S2_TX, .peri_id = (u8)DMACH_UART2_TX,
[16] = DMACH_SPI0_RX, .rqtype = MEMTODEV,
[17] = DMACH_SPI0_TX, }, {
[18] = DMACH_SPI1_RX, .peri_id = (u8)DMACH_UART3_RX,
[19] = DMACH_SPI1_TX, .rqtype = DEVTOMEM,
[20] = DMACH_SPI2_RX, }, {
[21] = DMACH_SPI2_TX, .peri_id = (u8)DMACH_UART3_TX,
[22] = DMACH_PCM0_RX, .rqtype = MEMTODEV,
[23] = DMACH_PCM0_TX, }, {
[24] = DMACH_PCM1_RX, .peri_id = DMACH_IRDA,
[25] = DMACH_PCM1_TX, }, {
[26] = DMACH_MSM_REQ0, .peri_id = (u8)DMACH_I2S0_RX,
[27] = DMACH_MSM_REQ1, .rqtype = DEVTOMEM,
[28] = DMACH_MSM_REQ2, }, {
[29] = DMACH_MSM_REQ3, .peri_id = (u8)DMACH_I2S0_TX,
[30] = DMACH_MAX, .rqtype = MEMTODEV,
[31] = DMACH_MAX, }, {
.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 = { struct dma_pl330_platdata s5pc100_pdma1_pdata = {
.name = "s3c-pl330", .nr_valid_peri = ARRAY_SIZE(pdma1_peri),
.id = 1, .peri = pdma1_peri,
.num_resources = ARRAY_SIZE(s5pc100_pdma1_resource), };
.resource = s5pc100_pdma1_resource,
struct amba_device s5pc100_device_pdma1 = {
.dev = { .dev = {
.init_name = "dma-pl330.1",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s5pc100_pdma1_pdata, .platform_data = &s5pc100_pdma1_pdata,
}, },
}; .res = {
.start = S5PC100_PA_PDMA1,
static struct platform_device *s5pc100_dmacs[] __initdata = { .end = S5PC100_PA_PDMA1 + SZ_4K,
&s5pc100_device_pdma0, .flags = IORESOURCE_MEM,
&s5pc100_device_pdma1, },
.irq = {IRQ_PDMA1, NO_IRQ},
.periphid = 0x00041330,
}; };
static int __init s5pc100_dma_init(void) static int __init s5pc100_dma_init(void)
{ {
platform_add_devices(s5pc100_dmacs, ARRAY_SIZE(s5pc100_dmacs)); amba_device_register(&s5pc100_device_pdma0, &iomem_resource);
return 0; return 0;
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H #ifndef __MACH_DMA_H
#define __MACH_DMA_H #define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */ /* This platform uses the common DMA API driver for PL330 */
#include <plat/s3c-dma-pl330.h> #include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */ #endif /* __MACH_DMA_H */
...@@ -11,7 +11,7 @@ if ARCH_S5PV210 ...@@ -11,7 +11,7 @@ if ARCH_S5PV210
config CPU_S5PV210 config CPU_S5PV210
bool bool
select S3C_PL330_DMA select SAMSUNG_DMADEV
select S5P_EXT_INT select S5P_EXT_INT
select S5P_HRT select S5P_HRT
select S5PV210_PM if PM select S5PV210_PM if PM
......
...@@ -203,6 +203,11 @@ static struct clk clk_pcmcdclk2 = { ...@@ -203,6 +203,11 @@ static struct clk clk_pcmcdclk2 = {
.name = "pcmcdclk", .name = "pcmcdclk",
}; };
static struct clk dummy_apb_pclk = {
.name = "apb_pclk",
.id = -1,
};
static struct clk *clkset_vpllsrc_list[] = { static struct clk *clkset_vpllsrc_list[] = {
[0] = &clk_fin_vpll, [0] = &clk_fin_vpll,
[1] = &clk_sclk_hdmi27m, [1] = &clk_sclk_hdmi27m,
...@@ -289,13 +294,13 @@ static struct clk_ops clk_fout_apll_ops = { ...@@ -289,13 +294,13 @@ static struct clk_ops clk_fout_apll_ops = {
static struct clk init_clocks_off[] = { static struct clk init_clocks_off[] = {
{ {
.name = "pdma", .name = "dma",
.devname = "s3c-pl330.0", .devname = "s3c-pl330.0",
.parent = &clk_hclk_psys.clk, .parent = &clk_hclk_psys.clk,
.enable = s5pv210_clk_ip0_ctrl, .enable = s5pv210_clk_ip0_ctrl,
.ctrlbit = (1 << 3), .ctrlbit = (1 << 3),
}, { }, {
.name = "pdma", .name = "dma",
.devname = "s3c-pl330.1", .devname = "s3c-pl330.1",
.parent = &clk_hclk_psys.clk, .parent = &clk_hclk_psys.clk,
.enable = s5pv210_clk_ip0_ctrl, .enable = s5pv210_clk_ip0_ctrl,
...@@ -1161,5 +1166,6 @@ void __init s5pv210_register_clocks(void) ...@@ -1161,5 +1166,6 @@ void __init s5pv210_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_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(); s3c_pwmclk_init();
} }
/* /* 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. * Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com> * Jaswinder Singh <jassi.brar@samsung.com>
* *
...@@ -17,151 +21,239 @@ ...@@ -17,151 +21,239 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/platform_device.h>
#include <linux/dma-mapping.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/devs.h>
#include <plat/irqs.h> #include <plat/irqs.h>
#include <mach/map.h> #include <mach/map.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <mach/dma.h>
#include <plat/s3c-pl330-pdata.h>
static u64 dma_dmamask = DMA_BIT_MASK(32); static u64 dma_dmamask = DMA_BIT_MASK(32);
static struct resource s5pv210_pdma0_resource[] = { struct dma_pl330_peri pdma0_peri[28] = {
[0] = { {
.start = S5PV210_PA_PDMA0, .peri_id = (u8)DMACH_UART0_RX,
.end = S5PV210_PA_PDMA0 + SZ_4K, .rqtype = DEVTOMEM,
.flags = IORESOURCE_MEM, }, {
}, .peri_id = (u8)DMACH_UART0_TX,
[1] = { .rqtype = MEMTODEV,
.start = IRQ_PDMA0, }, {
.end = IRQ_PDMA0, .peri_id = (u8)DMACH_UART1_RX,
.flags = IORESOURCE_IRQ, .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 = { struct dma_pl330_platdata s5pv210_pdma0_pdata = {
.peri = { .nr_valid_peri = ARRAY_SIZE(pdma0_peri),
[0] = DMACH_UART0_RX, .peri = pdma0_peri,
[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,
},
}; };
static struct platform_device s5pv210_device_pdma0 = { struct amba_device s5pv210_device_pdma0 = {
.name = "s3c-pl330",
.id = 0,
.num_resources = ARRAY_SIZE(s5pv210_pdma0_resource),
.resource = s5pv210_pdma0_resource,
.dev = { .dev = {
.init_name = "dma-pl330.0",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s5pv210_pdma0_pdata, .platform_data = &s5pv210_pdma0_pdata,
}, },
}; .res = {
.start = S5PV210_PA_PDMA0,
static struct resource s5pv210_pdma1_resource[] = { .end = S5PV210_PA_PDMA0 + SZ_4K,
[0] = {
.start = S5PV210_PA_PDMA1,
.end = S5PV210_PA_PDMA1 + SZ_4K,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
}, },
[1] = { .irq = {IRQ_PDMA0, NO_IRQ},
.start = IRQ_PDMA1, .periphid = 0x00041330,
.end = IRQ_PDMA1,
.flags = IORESOURCE_IRQ,
},
}; };
static struct s3c_pl330_platdata s5pv210_pdma1_pdata = { struct dma_pl330_peri pdma1_peri[32] = {
.peri = { {
[0] = DMACH_UART0_RX, .peri_id = (u8)DMACH_UART0_RX,
[1] = DMACH_UART0_TX, .rqtype = DEVTOMEM,
[2] = DMACH_UART1_RX, }, {
[3] = DMACH_UART1_TX, .peri_id = (u8)DMACH_UART0_TX,
[4] = DMACH_UART2_RX, .rqtype = MEMTODEV,
[5] = DMACH_UART2_TX, }, {
[6] = DMACH_UART3_RX, .peri_id = (u8)DMACH_UART1_RX,
[7] = DMACH_UART3_TX, .rqtype = DEVTOMEM,
[8] = DMACH_MAX, }, {
[9] = DMACH_I2S0_RX, .peri_id = (u8)DMACH_UART1_TX,
[10] = DMACH_I2S0_TX, .rqtype = MEMTODEV,
[11] = DMACH_I2S0S_TX, }, {
[12] = DMACH_I2S1_RX, .peri_id = (u8)DMACH_UART2_RX,
[13] = DMACH_I2S1_TX, .rqtype = DEVTOMEM,
[14] = DMACH_I2S2_RX, }, {
[15] = DMACH_I2S2_TX, .peri_id = (u8)DMACH_UART2_TX,
[16] = DMACH_SPI0_RX, .rqtype = MEMTODEV,
[17] = DMACH_SPI0_TX, }, {
[18] = DMACH_SPI1_RX, .peri_id = (u8)DMACH_UART3_RX,
[19] = DMACH_SPI1_TX, .rqtype = DEVTOMEM,
[20] = DMACH_MAX, }, {
[21] = DMACH_MAX, .peri_id = (u8)DMACH_UART3_TX,
[22] = DMACH_PCM0_RX, .rqtype = MEMTODEV,
[23] = DMACH_PCM0_TX, }, {
[24] = DMACH_PCM1_RX, .peri_id = DMACH_MAX,
[25] = DMACH_PCM1_TX, }, {
[26] = DMACH_MSM_REQ0, .peri_id = (u8)DMACH_I2S0_RX,
[27] = DMACH_MSM_REQ1, .rqtype = DEVTOMEM,
[28] = DMACH_MSM_REQ2, }, {
[29] = DMACH_MSM_REQ3, .peri_id = (u8)DMACH_I2S0_TX,
[30] = DMACH_PCM2_RX, .rqtype = MEMTODEV,
[31] = DMACH_PCM2_TX, }, {
.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 = { struct dma_pl330_platdata s5pv210_pdma1_pdata = {
.name = "s3c-pl330", .nr_valid_peri = ARRAY_SIZE(pdma1_peri),
.id = 1, .peri = pdma1_peri,
.num_resources = ARRAY_SIZE(s5pv210_pdma1_resource), };
.resource = s5pv210_pdma1_resource,
struct amba_device s5pv210_device_pdma1 = {
.dev = { .dev = {
.init_name = "dma-pl330.1",
.dma_mask = &dma_dmamask, .dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32), .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s5pv210_pdma1_pdata, .platform_data = &s5pv210_pdma1_pdata,
}, },
}; .res = {
.start = S5PV210_PA_PDMA1,
static struct platform_device *s5pv210_dmacs[] __initdata = { .end = S5PV210_PA_PDMA1 + SZ_4K,
&s5pv210_device_pdma0, .flags = IORESOURCE_MEM,
&s5pv210_device_pdma1, },
.irq = {IRQ_PDMA1, NO_IRQ},
.periphid = 0x00041330,
}; };
static int __init s5pv210_dma_init(void) static int __init s5pv210_dma_init(void)
{ {
platform_add_devices(s5pv210_dmacs, ARRAY_SIZE(s5pv210_dmacs)); amba_device_register(&s5pv210_device_pdma0, &iomem_resource);
return 0; return 0;
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H #ifndef __MACH_DMA_H
#define __MACH_DMA_H #define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */ /* This platform uses the common DMA API driver for PL330 */
#include <plat/s3c-dma-pl330.h> #include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */ #endif /* __MACH_DMA_H */
...@@ -1094,14 +1094,14 @@ EXPORT_SYMBOL(s3c2410_dma_config); ...@@ -1094,14 +1094,14 @@ EXPORT_SYMBOL(s3c2410_dma_config);
* *
* configure the dma source/destination hardware type and address * configure the dma source/destination hardware type and address
* *
* source: S3C2410_DMASRC_HW: source is hardware * source: DMA_FROM_DEVICE: source is hardware
* S3C2410_DMASRC_MEM: source is memory * DMA_TO_DEVICE: source is memory
* *
* devaddr: physical address of the source * devaddr: physical address of the source
*/ */
int s3c2410_dma_devconfig(enum dma_ch channel, int s3c2410_dma_devconfig(enum dma_ch channel,
enum s3c2410_dmasrc source, enum dma_data_direction source,
unsigned long devaddr) unsigned long devaddr)
{ {
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
...@@ -1131,7 +1131,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel, ...@@ -1131,7 +1131,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel,
hwcfg |= S3C2410_DISRCC_INC; hwcfg |= S3C2410_DISRCC_INC;
switch (source) { switch (source) {
case S3C2410_DMASRC_HW: case DMA_FROM_DEVICE:
/* source is hardware */ /* source is hardware */
pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n",
__func__, devaddr, hwcfg); __func__, devaddr, hwcfg);
...@@ -1142,7 +1142,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel, ...@@ -1142,7 +1142,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel,
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
break; break;
case S3C2410_DMASRC_MEM: case DMA_TO_DEVICE:
/* source is memory */ /* source is memory */
pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d\n", pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d\n",
__func__, devaddr, hwcfg); __func__, devaddr, hwcfg);
......
...@@ -300,11 +300,14 @@ config S3C_DMA ...@@ -300,11 +300,14 @@ config S3C_DMA
help help
Internal configuration for S3C DMA core Internal configuration for S3C DMA core
config S3C_PL330_DMA config SAMSUNG_DMADEV
bool bool
select PL330 select DMADEVICES
select PL330_DMA if (CPU_EXYNOS4210 || CPU_S5PV210 || CPU_S5PC100 || \
CPU_S5P6450 || CPU_S5P6440)
select ARM_AMBA
help help
S3C DMA API Driver for PL330 DMAC. Use DMA device engine for PL330 DMAC.
comment "Power management" comment "Power management"
......
...@@ -63,9 +63,9 @@ obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT) += dev-backlight.o ...@@ -63,9 +63,9 @@ obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT) += dev-backlight.o
# DMA support # DMA support
obj-$(CONFIG_S3C_DMA) += dma.o obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o
obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o
# PM support # PM support
......
/* 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
*
* 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_ */
...@@ -8,11 +8,8 @@ ...@@ -8,11 +8,8 @@
* (at your option) any later version. * (at your option) any later version.
*/ */
#ifndef __S3C_DMA_PL330_H_ #ifndef __DMA_PL330_H_
#define __S3C_DMA_PL330_H_ #define __DMA_PL330_H_ __FILE__
#define S3C2410_DMAF_AUTOSTART (1 << 0)
#define S3C2410_DMAF_CIRCULAR (1 << 1)
/* /*
* PL330 can assign any channel to communicate with * PL330 can assign any channel to communicate with
...@@ -20,7 +17,7 @@ ...@@ -20,7 +17,7 @@
* For the sake of consistency across client drivers, * For the sake of consistency across client drivers,
* We keep the channel names unchanged and only add * We keep the channel names unchanged and only add
* missing peripherals are added. * 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. * use these just as IDs.
*/ */
enum dma_ch { enum dma_ch {
...@@ -88,11 +85,20 @@ enum dma_ch { ...@@ -88,11 +85,20 @@ enum dma_ch {
DMACH_MAX, 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; return true;
} }
#include <plat/dma.h> #include <plat/dma-ops.h>
#endif /* __S3C_DMA_PL330_H_ */ #endif /* __DMA_PL330_H_ */
...@@ -47,7 +47,7 @@ struct s3c24xx_dma_selection { ...@@ -47,7 +47,7 @@ struct s3c24xx_dma_selection {
void (*direction)(struct s3c2410_dma_chan *chan, void (*direction)(struct s3c2410_dma_chan *chan,
struct s3c24xx_dma_map *map, 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); extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel);
......
...@@ -10,17 +10,14 @@ ...@@ -10,17 +10,14 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/dma-mapping.h>
enum s3c2410_dma_buffresult { enum s3c2410_dma_buffresult {
S3C2410_RES_OK, S3C2410_RES_OK,
S3C2410_RES_ERR, S3C2410_RES_ERR,
S3C2410_RES_ABORT S3C2410_RES_ABORT
}; };
enum s3c2410_dmasrc {
S3C2410_DMASRC_HW, /* source is memory */
S3C2410_DMASRC_MEM /* source is hardware */
};
/* enum s3c2410_chan_op /* enum s3c2410_chan_op
* *
* operation codes passed to the DMA code by the user, and also used * 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); ...@@ -112,7 +109,7 @@ extern int s3c2410_dma_config(enum dma_ch channel, int xferunit);
*/ */
extern int s3c2410_dma_devconfig(enum dma_ch channel, 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 /* s3c2410_dma_getposition
* *
...@@ -126,3 +123,4 @@ extern int s3c2410_dma_set_opfn(enum dma_ch, s3c2410_dma_opfn_t rtn); ...@@ -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); extern int s3c2410_dma_set_buffdone_fn(enum dma_ch, s3c2410_dma_cbfn_t rtn);
#include <plat/dma-ops.h>
/* 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 */
/* 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);
/* 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
"\tDBUFF-%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");
...@@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL ...@@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
config PL330_DMA config PL330_DMA
tristate "DMA API Driver for PL330" tristate "DMA API Driver for PL330"
select DMA_ENGINE select DMA_ENGINE
depends on PL330 depends on ARM_AMBA
select PL330
help help
Select if your platform has one or more PL330 DMACs. Select if your platform has one or more PL330 DMACs.
You need to provide platform specific settings via You need to provide platform specific settings via
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <linux/amba/pl330.h> #include <linux/amba/pl330.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#define NR_DEFAULT_DESC 16 #define NR_DEFAULT_DESC 16
...@@ -68,6 +70,14 @@ struct dma_pl330_chan { ...@@ -68,6 +70,14 @@ struct dma_pl330_chan {
* NULL if the channel is available to be acquired. * NULL if the channel is available to be acquired.
*/ */
void *pl330_chid; 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 { struct dma_pl330_dmac {
...@@ -83,6 +93,8 @@ struct dma_pl330_dmac { ...@@ -83,6 +93,8 @@ struct dma_pl330_dmac {
/* Peripheral channels connected to this DMAC */ /* Peripheral channels connected to this DMAC */
struct dma_pl330_chan *peripherals; /* keep at end */ struct dma_pl330_chan *peripherals; /* keep at end */
struct clk *clk;
}; };
struct dma_pl330_desc { struct dma_pl330_desc {
...@@ -152,6 +164,31 @@ static inline void free_desc_list(struct list_head *list) ...@@ -152,6 +164,31 @@ static inline void free_desc_list(struct list_head *list)
spin_unlock_irqrestore(&pdmac->pool_lock, flags); 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) static inline void fill_queue(struct dma_pl330_chan *pch)
{ {
struct dma_pl330_desc *desc; struct dma_pl330_desc *desc;
...@@ -205,6 +242,9 @@ static void pl330_tasklet(unsigned long data) ...@@ -205,6 +242,9 @@ static void pl330_tasklet(unsigned long data)
spin_unlock_irqrestore(&pch->lock, flags); spin_unlock_irqrestore(&pch->lock, flags);
if (pch->cyclic)
handle_cyclic_desc_list(&list);
else
free_desc_list(&list); free_desc_list(&list);
} }
...@@ -236,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) ...@@ -236,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&pch->lock, flags); spin_lock_irqsave(&pch->lock, flags);
pch->completed = chan->cookie = 1; pch->completed = chan->cookie = 1;
pch->cyclic = false;
pch->pl330_chid = pl330_request_channel(&pdmac->pif); pch->pl330_chid = pl330_request_channel(&pdmac->pif);
if (!pch->pl330_chid) { if (!pch->pl330_chid) {
...@@ -253,25 +294,52 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) ...@@ -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) 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_chan *pch = to_pchan(chan);
struct dma_pl330_desc *desc; struct dma_pl330_desc *desc, *_dt;
unsigned long flags; unsigned long flags;
struct dma_pl330_dmac *pdmac = pch->dmac;
struct dma_slave_config *slave_config;
LIST_HEAD(list);
/* Only supports DMA_TERMINATE_ALL */ switch (cmd) {
if (cmd != DMA_TERMINATE_ALL) case DMA_TERMINATE_ALL:
return -ENXIO;
spin_lock_irqsave(&pch->lock, flags); spin_lock_irqsave(&pch->lock, flags);
/* FLUSH the PL330 Channel thread */ /* FLUSH the PL330 Channel thread */
pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
/* Mark all desc done */ /* 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; 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); spin_unlock_irqrestore(&pch->lock, flags);
break;
pl330_tasklet((unsigned long) pch); 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; return 0;
} }
...@@ -288,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan) ...@@ -288,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
pl330_release_channel(pch->pl330_chid); pl330_release_channel(pch->pl330_chid);
pch->pl330_chid = NULL; pch->pl330_chid = NULL;
if (pch->cyclic)
list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags); spin_unlock_irqrestore(&pch->lock, flags);
} }
...@@ -453,7 +524,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) ...@@ -453,7 +524,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
if (peri) { if (peri) {
desc->req.rqtype = peri->rqtype; desc->req.rqtype = peri->rqtype;
desc->req.peri = peri->peri_id; desc->req.peri = pch->chan.chan_id;
} else { } else {
desc->req.rqtype = MEMTOMEM; desc->req.rqtype = MEMTOMEM;
desc->req.peri = 0; desc->req.peri = 0;
...@@ -524,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) ...@@ -524,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
return burst_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 * static struct dma_async_tx_descriptor *
pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
dma_addr_t src, size_t len, unsigned long flags) 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, ...@@ -579,7 +695,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dma_pl330_peri *peri = chan->private; struct dma_pl330_peri *peri = chan->private;
struct scatterlist *sg; struct scatterlist *sg;
unsigned long flags; unsigned long flags;
int i, burst_size; int i;
dma_addr_t addr; dma_addr_t addr;
if (unlikely(!pch || !sgl || !sg_len || !peri)) if (unlikely(!pch || !sgl || !sg_len || !peri))
...@@ -595,8 +711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -595,8 +711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL; return NULL;
} }
addr = peri->fifo_addr; addr = pch->fifo_addr;
burst_size = peri->burst_sz;
first = NULL; first = NULL;
...@@ -644,7 +759,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -644,7 +759,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
sg_dma_address(sg), addr, sg_dma_len(sg)); 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; desc->rqcfg.brst_len = 1;
} }
...@@ -696,6 +811,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -696,6 +811,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err1; 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]; irq = adev->irq[0];
ret = request_irq(irq, pl330_irq_handler, 0, ret = request_irq(irq, pl330_irq_handler, 0,
dev_name(&adev->dev), pi); dev_name(&adev->dev), pi);
...@@ -732,6 +871,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -732,6 +871,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
case MEMTODEV: case MEMTODEV:
case DEVTOMEM: case DEVTOMEM:
dma_cap_set(DMA_SLAVE, pd->cap_mask); dma_cap_set(DMA_SLAVE, pd->cap_mask);
dma_cap_set(DMA_CYCLIC, pd->cap_mask);
break; break;
default: default:
dev_err(&adev->dev, "DEVTODEV Not Supported\n"); dev_err(&adev->dev, "DEVTODEV Not Supported\n");
...@@ -758,6 +898,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -758,6 +898,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; 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_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_control = pl330_control; pd->device_control = pl330_control;
...@@ -769,8 +910,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -769,8 +910,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err4; goto probe_err4;
} }
amba_set_drvdata(adev, pdmac);
dev_info(&adev->dev, dev_info(&adev->dev,
"Loaded driver for PL330 DMAC-%d\n", adev->periphid); "Loaded driver for PL330 DMAC-%d\n", adev->periphid);
dev_info(&adev->dev, dev_info(&adev->dev,
...@@ -831,6 +970,13 @@ static int __devexit pl330_remove(struct amba_device *adev) ...@@ -831,6 +970,13 @@ static int __devexit pl330_remove(struct amba_device *adev)
res = &adev->res; res = &adev->res;
release_mem_region(res->start, resource_size(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); kfree(pdmac);
return 0; return 0;
...@@ -844,10 +990,49 @@ static struct amba_id pl330_ids[] = { ...@@ -844,10 +990,49 @@ static struct amba_id pl330_ids[] = {
{ 0, 0 }, { 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 = { static struct amba_driver pl330_driver = {
.drv = { .drv = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "dma-pl330", .name = "dma-pl330",
.pm = &pl330_pm_ops,
}, },
.id_table = pl330_ids, .id_table = pl330_ids,
.probe = pl330_probe, .probe = pl330_probe,
......
...@@ -913,9 +913,9 @@ static void finalize_request(struct s3cmci_host *host) ...@@ -913,9 +913,9 @@ static void finalize_request(struct s3cmci_host *host)
} }
static void s3cmci_dma_setup(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; static int setup_ok;
if (last_source == source) if (last_source == source)
...@@ -1087,7 +1087,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) ...@@ -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); 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); s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
......
...@@ -131,6 +131,12 @@ ...@@ -131,6 +131,12 @@
#define RXBUSY (1<<2) #define RXBUSY (1<<2)
#define TXBUSY (1<<3) #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. * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
* @clk: Pointer to the spi clock. * @clk: Pointer to the spi clock.
...@@ -164,13 +170,14 @@ struct s3c64xx_spi_driver_data { ...@@ -164,13 +170,14 @@ struct s3c64xx_spi_driver_data {
struct work_struct work; struct work_struct work;
struct list_head queue; struct list_head queue;
spinlock_t lock; spinlock_t lock;
enum dma_ch rx_dmach;
enum dma_ch tx_dmach;
unsigned long sfr_start; unsigned long sfr_start;
struct completion xfer_completion; struct completion xfer_completion;
unsigned state; unsigned state;
unsigned cur_mode, cur_bpw; unsigned cur_mode, cur_bpw;
unsigned cur_speed; 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 = { static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
...@@ -226,6 +233,78 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) ...@@ -226,6 +233,78 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
writel(val, regs + S3C64XX_SPI_CH_CFG); 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, static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi, struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode) struct spi_transfer *xfer, int dma_mode)
...@@ -258,10 +337,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, ...@@ -258,10 +337,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
chcfg |= S3C64XX_SPI_CH_TXCH_ON; chcfg |= S3C64XX_SPI_CH_TXCH_ON;
if (dma_mode) { if (dma_mode) {
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8); prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma);
s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
xfer->tx_dma, xfer->len);
s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
} else { } else {
switch (sdd->cur_bpw) { switch (sdd->cur_bpw) {
case 32: case 32:
...@@ -293,10 +369,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, ...@@ -293,10 +369,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
| S3C64XX_SPI_PACKET_CNT_EN, | S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT); regs + S3C64XX_SPI_PACKET_CNT);
s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8); prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma);
s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
xfer->rx_dma, xfer->len);
s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
} }
} }
...@@ -482,46 +555,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) ...@@ -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) #define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, 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, ...@@ -696,12 +729,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
if (use_dma) { if (use_dma) {
if (xfer->tx_buf != NULL if (xfer->tx_buf != NULL
&& (sdd->state & TXBUSY)) && (sdd->state & TXBUSY))
s3c2410_dma_ctrl(sdd->tx_dmach, sdd->ops->stop(sdd->tx_dma.ch);
S3C2410_DMAOP_FLUSH);
if (xfer->rx_buf != NULL if (xfer->rx_buf != NULL
&& (sdd->state & RXBUSY)) && (sdd->state & RXBUSY))
s3c2410_dma_ctrl(sdd->rx_dmach, sdd->ops->stop(sdd->rx_dma.ch);
S3C2410_DMAOP_FLUSH);
} }
goto out; goto out;
...@@ -739,30 +770,6 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, ...@@ -739,30 +770,6 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
msg->complete(msg->context); 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) static void s3c64xx_spi_work(struct work_struct *work)
{ {
struct s3c64xx_spi_driver_data *sdd = container_of(work, struct s3c64xx_spi_driver_data *sdd = container_of(work,
...@@ -799,8 +806,8 @@ static void s3c64xx_spi_work(struct work_struct *work) ...@@ -799,8 +806,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
spin_unlock_irqrestore(&sdd->lock, flags); spin_unlock_irqrestore(&sdd->lock, flags);
/* Free DMA channels */ /* Free DMA channels */
s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client); sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client); sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
} }
static int s3c64xx_spi_transfer(struct spi_device *spi, static int s3c64xx_spi_transfer(struct spi_device *spi,
...@@ -1017,8 +1024,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1017,8 +1024,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cntrlr_info = sci; sdd->cntrlr_info = sci;
sdd->pdev = pdev; sdd->pdev = pdev;
sdd->sfr_start = mem_res->start; sdd->sfr_start = mem_res->start;
sdd->tx_dmach = dmatx_res->start; sdd->tx_dma.dmach = dmatx_res->start;
sdd->rx_dmach = dmarx_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; sdd->cur_bpw = 8;
...@@ -1106,7 +1115,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1106,7 +1115,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
pdev->id, master->num_chipselect); pdev->id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start, mem_res->end, mem_res->start,
sdd->rx_dmach, sdd->tx_dmach); sdd->rx_dma.dmach, sdd->tx_dma.dmach);
return 0; return 0;
......
...@@ -19,12 +19,8 @@ struct dma_pl330_peri { ...@@ -19,12 +19,8 @@ struct dma_pl330_peri {
* Peri_Req i/f of the DMAC that is * Peri_Req i/f of the DMAC that is
* peripheral could be reached from. * peripheral could be reached from.
*/ */
u8 peri_id; /* {0, 31} */ u8 peri_id; /* specific dma id */
enum pl330_reqtype rqtype; 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 { struct dma_pl330_platdata {
......
...@@ -271,7 +271,10 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -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); 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; return 0;
} }
...@@ -317,7 +320,10 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, ...@@ -317,7 +320,10 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 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; return 0;
} }
......
...@@ -54,7 +54,6 @@ struct runtime_data { ...@@ -54,7 +54,6 @@ struct runtime_data {
spinlock_t lock; spinlock_t lock;
int state; int state;
unsigned int dma_loaded; unsigned int dma_loaded;
unsigned int dma_limit;
unsigned int dma_period; unsigned int dma_period;
dma_addr_t dma_start; dma_addr_t dma_start;
dma_addr_t dma_pos; dma_addr_t dma_pos;
...@@ -62,77 +61,79 @@ struct runtime_data { ...@@ -62,77 +61,79 @@ struct runtime_data {
struct s3c_dma_params *params; struct s3c_dma_params *params;
}; };
static void audio_buffdone(void *data);
/* dma_enqueue /* dma_enqueue
* *
* place a dma buffer onto the queue for the dma system * place a dma buffer onto the queue for the dma system
* to handle. * to handle.
*/ */
static void dma_enqueue(struct snd_pcm_substream *substream) static void dma_enqueue(struct snd_pcm_substream *substream)
{ {
struct runtime_data *prtd = substream->runtime->private_data; struct runtime_data *prtd = substream->runtime->private_data;
dma_addr_t pos = prtd->dma_pos; dma_addr_t pos = prtd->dma_pos;
unsigned int limit; unsigned int limit;
int ret; struct samsung_dma_prep_info dma_info;
pr_debug("Entered %s\n", __func__); pr_debug("Entered %s\n", __func__);
if (s3c_dma_has_circular())
limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
else
limit = prtd->dma_limit;
pr_debug("%s: loaded %d, limit %d\n", pr_debug("%s: loaded %d, limit %d\n",
__func__, prtd->dma_loaded, limit); __func__, prtd->dma_loaded, limit);
while (prtd->dma_loaded < limit) { dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE);
unsigned long len = prtd->dma_period; 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); pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
if ((pos + len) > prtd->dma_end) { if ((pos + dma_info.period) > prtd->dma_end) {
len = prtd->dma_end - pos; dma_info.period = prtd->dma_end - pos;
pr_debug("%s: corrected dma len %ld\n", __func__, len); pr_debug("%s: corrected dma len %ld\n",
__func__, dma_info.period);
} }
ret = s3c2410_dma_enqueue(prtd->params->channel, dma_info.buf = pos;
substream, pos, len); prtd->params->ops->prepare(prtd->params->ch, &dma_info);
if (ret == 0) {
prtd->dma_loaded++; prtd->dma_loaded++;
pos += prtd->dma_period; pos += prtd->dma_period;
if (pos >= prtd->dma_end) if (pos >= prtd->dma_end)
pos = prtd->dma_start; pos = prtd->dma_start;
} else
break;
} }
prtd->dma_pos = pos; prtd->dma_pos = pos;
} }
static void audio_buffdone(struct s3c2410_dma_chan *channel, static void audio_buffdone(void *data)
void *dev_id, int size,
enum s3c2410_dma_buffresult result)
{ {
struct snd_pcm_substream *substream = dev_id; struct snd_pcm_substream *substream = data;
struct runtime_data *prtd; struct runtime_data *prtd = substream->runtime->private_data;
pr_debug("Entered %s\n", __func__); pr_debug("Entered %s\n", __func__);
if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) if (prtd->state & ST_RUNNING) {
return; prtd->dma_pos += prtd->dma_period;
if (prtd->dma_pos >= prtd->dma_end)
prtd = substream->runtime->private_data; prtd->dma_pos = prtd->dma_start;
if (substream) if (substream)
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
spin_lock(&prtd->lock); spin_lock(&prtd->lock);
if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { if (!samsung_dma_has_circular()) {
prtd->dma_loaded--; prtd->dma_loaded--;
dma_enqueue(substream); dma_enqueue(substream);
} }
spin_unlock(&prtd->lock); spin_unlock(&prtd->lock);
}
} }
static int dma_hw_params(struct snd_pcm_substream *substream, static int dma_hw_params(struct snd_pcm_substream *substream,
...@@ -144,8 +145,7 @@ 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); unsigned long totbytes = params_buffer_bytes(params);
struct s3c_dma_params *dma = struct s3c_dma_params *dma =
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 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__); pr_debug("Entered %s\n", __func__);
...@@ -163,30 +163,26 @@ static int dma_hw_params(struct snd_pcm_substream *substream, ...@@ -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, pr_debug("params %p, client %p, channel %d\n", prtd->params,
prtd->params->client, prtd->params->channel); prtd->params->client, prtd->params->channel);
ret = s3c2410_dma_request(prtd->params->channel, prtd->params->ops = samsung_dma_get_ops();
prtd->params->client, NULL);
dma_info.cap = (samsung_dma_has_circular() ?
if (ret < 0) { DMA_CYCLIC : DMA_SLAVE);
printk(KERN_ERR "failed to get dma channel\n"); dma_info.client = prtd->params->client;
return ret; dma_info.direction =
} (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
? DMA_TO_DEVICE : DMA_FROM_DEVICE);
/* use the circular buffering if we have it available. */ dma_info.width = prtd->params->dma_size;
if (s3c_dma_has_circular()) dma_info.fifo = prtd->params->dma_addr;
s3c2410_dma_setflags(prtd->params->channel, prtd->params->ch = prtd->params->ops->request(
S3C2410_DMAF_CIRCULAR); prtd->params->channel, &dma_info);
} }
s3c2410_dma_set_buffdone_fn(prtd->params->channel,
audio_buffdone);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totbytes; runtime->dma_bytes = totbytes;
spin_lock_irq(&prtd->lock); spin_lock_irq(&prtd->lock);
prtd->dma_loaded = 0; prtd->dma_loaded = 0;
prtd->dma_limit = runtime->hw.periods_min;
prtd->dma_period = params_period_bytes(params); prtd->dma_period = params_period_bytes(params);
prtd->dma_start = runtime->dma_addr; prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start; prtd->dma_pos = prtd->dma_start;
...@@ -206,7 +202,8 @@ static int dma_hw_free(struct snd_pcm_substream *substream) ...@@ -206,7 +202,8 @@ static int dma_hw_free(struct snd_pcm_substream *substream)
snd_pcm_set_runtime_buffer(substream, NULL); snd_pcm_set_runtime_buffer(substream, NULL);
if (prtd->params) { 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; prtd->params = NULL;
} }
...@@ -225,23 +222,9 @@ static int dma_prepare(struct snd_pcm_substream *substream) ...@@ -225,23 +222,9 @@ static int dma_prepare(struct snd_pcm_substream *substream)
if (!prtd->params) if (!prtd->params)
return 0; 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 */ /* 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_loaded = 0;
prtd->dma_pos = prtd->dma_start; prtd->dma_pos = prtd->dma_start;
...@@ -265,14 +248,14 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -265,14 +248,14 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
prtd->state |= ST_RUNNING; prtd->state |= ST_RUNNING;
s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START); prtd->params->ops->trigger(prtd->params->ch);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
prtd->state &= ~ST_RUNNING; prtd->state &= ~ST_RUNNING;
s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP); prtd->params->ops->stop(prtd->params->ch);
break; break;
default: default:
...@@ -291,21 +274,12 @@ dma_pointer(struct snd_pcm_substream *substream) ...@@ -291,21 +274,12 @@ dma_pointer(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct runtime_data *prtd = runtime->private_data; struct runtime_data *prtd = runtime->private_data;
unsigned long res; unsigned long res;
dma_addr_t src, dst;
pr_debug("Entered %s\n", __func__); pr_debug("Entered %s\n", __func__);
spin_lock(&prtd->lock); res = prtd->dma_pos - prtd->dma_start;
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);
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 /* 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 * to out-of-bounds pointers. this is maybe due to the dma engine
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
* *
* ALSA PCM interface for the Samsung S3C24xx CPU * ALSA PCM interface for the Samsung SoC
*/ */
#ifndef _S3C_AUDIO_H #ifndef _S3C_AUDIO_H
...@@ -17,6 +17,8 @@ struct s3c_dma_params { ...@@ -17,6 +17,8 @@ struct s3c_dma_params {
int channel; /* Channel ID */ int channel; /* Channel ID */
dma_addr_t dma_addr; dma_addr_t dma_addr;
int dma_size; /* Size of the DMA transfer */ int dma_size; /* Size of the DMA transfer */
unsigned ch;
struct samsung_dma_ops *ops;
}; };
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment