[ARM] 4780/1: S3C2412: Allow for seperate DMA channels for TX and RX
The current S3C24XX DMA code does not allow for an peripheral that has one channel for RX and another for TX. This patch adds a per-cpu dma operation to select the transmit or receive channel, and adds support to the S3C2412 for the seperate DMA channels for TX and RX. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
67d729adc0
commit
c6709e8ef5
|
@ -40,106 +40,141 @@ static struct s3c24xx_dma_map __initdata s3c2412_dma_mappings[] = {
|
||||||
[DMACH_XD0] = {
|
[DMACH_XD0] = {
|
||||||
.name = "xdreq0",
|
.name = "xdreq0",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_XDREQ0),
|
.channels = MAP(S3C2412_DMAREQSEL_XDREQ0),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_XDREQ0),
|
||||||
},
|
},
|
||||||
[DMACH_XD1] = {
|
[DMACH_XD1] = {
|
||||||
.name = "xdreq1",
|
.name = "xdreq1",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_XDREQ1),
|
.channels = MAP(S3C2412_DMAREQSEL_XDREQ1),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_XDREQ1),
|
||||||
},
|
},
|
||||||
[DMACH_SDI] = {
|
[DMACH_SDI] = {
|
||||||
.name = "sdi",
|
.name = "sdi",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_SDI),
|
.channels = MAP(S3C2412_DMAREQSEL_SDI),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_SDI),
|
||||||
.hw_addr.to = S3C2410_PA_SDI + S3C2410_SDIDATA,
|
.hw_addr.to = S3C2410_PA_SDI + S3C2410_SDIDATA,
|
||||||
.hw_addr.from = S3C2410_PA_SDI + S3C2410_SDIDATA,
|
.hw_addr.from = S3C2410_PA_SDI + S3C2410_SDIDATA,
|
||||||
},
|
},
|
||||||
[DMACH_SPI0] = {
|
[DMACH_SPI0] = {
|
||||||
.name = "spi0",
|
.name = "spi0",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_SPI0TX),
|
.channels = MAP(S3C2412_DMAREQSEL_SPI0TX),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_SPI0RX),
|
||||||
.hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,
|
.hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,
|
||||||
.hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT,
|
.hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT,
|
||||||
},
|
},
|
||||||
[DMACH_SPI1] = {
|
[DMACH_SPI1] = {
|
||||||
.name = "spi1",
|
.name = "spi1",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_SPI1TX),
|
.channels = MAP(S3C2412_DMAREQSEL_SPI1TX),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_SPI1RX),
|
||||||
.hw_addr.to = S3C2410_PA_SPI + S3C2412_SPI1 + S3C2410_SPTDAT,
|
.hw_addr.to = S3C2410_PA_SPI + S3C2412_SPI1 + S3C2410_SPTDAT,
|
||||||
.hw_addr.from = S3C2410_PA_SPI + S3C2412_SPI1 + S3C2410_SPRDAT,
|
.hw_addr.from = S3C2410_PA_SPI + S3C2412_SPI1 + S3C2410_SPRDAT,
|
||||||
},
|
},
|
||||||
[DMACH_UART0] = {
|
[DMACH_UART0] = {
|
||||||
.name = "uart0",
|
.name = "uart0",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_UART0_0),
|
.channels = MAP(S3C2412_DMAREQSEL_UART0_0),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_UART0_0),
|
||||||
.hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,
|
.hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,
|
||||||
.hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH,
|
.hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH,
|
||||||
},
|
},
|
||||||
[DMACH_UART1] = {
|
[DMACH_UART1] = {
|
||||||
.name = "uart1",
|
.name = "uart1",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_UART1_0),
|
.channels = MAP(S3C2412_DMAREQSEL_UART1_0),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_UART1_0),
|
||||||
.hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,
|
.hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,
|
||||||
.hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH,
|
.hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH,
|
||||||
},
|
},
|
||||||
[DMACH_UART2] = {
|
[DMACH_UART2] = {
|
||||||
.name = "uart2",
|
.name = "uart2",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_UART2_0),
|
.channels = MAP(S3C2412_DMAREQSEL_UART2_0),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_UART2_0),
|
||||||
.hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,
|
.hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,
|
||||||
.hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH,
|
.hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH,
|
||||||
},
|
},
|
||||||
[DMACH_UART0_SRC2] = {
|
[DMACH_UART0_SRC2] = {
|
||||||
.name = "uart0",
|
.name = "uart0",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_UART0_1),
|
.channels = MAP(S3C2412_DMAREQSEL_UART0_1),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_UART0_1),
|
||||||
.hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,
|
.hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,
|
||||||
.hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH,
|
.hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH,
|
||||||
},
|
},
|
||||||
[DMACH_UART1_SRC2] = {
|
[DMACH_UART1_SRC2] = {
|
||||||
.name = "uart1",
|
.name = "uart1",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_UART1_1),
|
.channels = MAP(S3C2412_DMAREQSEL_UART1_1),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_UART1_1),
|
||||||
.hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,
|
.hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,
|
||||||
.hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH,
|
.hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH,
|
||||||
},
|
},
|
||||||
[DMACH_UART2_SRC2] = {
|
[DMACH_UART2_SRC2] = {
|
||||||
.name = "uart2",
|
.name = "uart2",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_UART2_1),
|
.channels = MAP(S3C2412_DMAREQSEL_UART2_1),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_UART2_1),
|
||||||
.hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,
|
.hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,
|
||||||
.hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH,
|
.hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH,
|
||||||
},
|
},
|
||||||
[DMACH_TIMER] = {
|
[DMACH_TIMER] = {
|
||||||
.name = "timer",
|
.name = "timer",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_TIMER),
|
.channels = MAP(S3C2412_DMAREQSEL_TIMER),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_TIMER),
|
||||||
},
|
},
|
||||||
[DMACH_I2S_IN] = {
|
[DMACH_I2S_IN] = {
|
||||||
.name = "i2s-sdi",
|
.name = "i2s-sdi",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_I2SRX),
|
.channels = MAP(S3C2412_DMAREQSEL_I2SRX),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_I2SRX),
|
||||||
.hw_addr.from = S3C2410_PA_IIS + S3C2412_IISRXD,
|
.hw_addr.from = S3C2410_PA_IIS + S3C2412_IISRXD,
|
||||||
},
|
},
|
||||||
[DMACH_I2S_OUT] = {
|
[DMACH_I2S_OUT] = {
|
||||||
.name = "i2s-sdo",
|
.name = "i2s-sdo",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_I2STX),
|
.channels = MAP(S3C2412_DMAREQSEL_I2STX),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_I2STX),
|
||||||
.hw_addr.to = S3C2410_PA_IIS + S3C2412_IISTXD,
|
.hw_addr.to = S3C2410_PA_IIS + S3C2412_IISTXD,
|
||||||
},
|
},
|
||||||
[DMACH_USB_EP1] = {
|
[DMACH_USB_EP1] = {
|
||||||
.name = "usb-ep1",
|
.name = "usb-ep1",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_USBEP1),
|
.channels = MAP(S3C2412_DMAREQSEL_USBEP1),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_USBEP1),
|
||||||
},
|
},
|
||||||
[DMACH_USB_EP2] = {
|
[DMACH_USB_EP2] = {
|
||||||
.name = "usb-ep2",
|
.name = "usb-ep2",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_USBEP2),
|
.channels = MAP(S3C2412_DMAREQSEL_USBEP2),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_USBEP2),
|
||||||
},
|
},
|
||||||
[DMACH_USB_EP3] = {
|
[DMACH_USB_EP3] = {
|
||||||
.name = "usb-ep3",
|
.name = "usb-ep3",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_USBEP3),
|
.channels = MAP(S3C2412_DMAREQSEL_USBEP3),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_USBEP3),
|
||||||
},
|
},
|
||||||
[DMACH_USB_EP4] = {
|
[DMACH_USB_EP4] = {
|
||||||
.name = "usb-ep4",
|
.name = "usb-ep4",
|
||||||
.channels = MAP(S3C2412_DMAREQSEL_USBEP4),
|
.channels = MAP(S3C2412_DMAREQSEL_USBEP4),
|
||||||
|
.channels_rx = MAP(S3C2412_DMAREQSEL_USBEP4),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void s3c2412_dma_direction(struct s3c2410_dma_chan *chan,
|
||||||
|
struct s3c24xx_dma_map *map,
|
||||||
|
enum s3c2410_dmasrc dir)
|
||||||
|
{
|
||||||
|
unsigned long chsel;
|
||||||
|
|
||||||
|
if (dir == S3C2410_DMASRC_HW)
|
||||||
|
chsel = map->channels_rx[0];
|
||||||
|
else
|
||||||
|
chsel = map->channels[0];
|
||||||
|
|
||||||
|
chsel &= ~DMA_CH_VALID;
|
||||||
|
chsel |= S3C2412_DMAREQSEL_HW;
|
||||||
|
|
||||||
|
writel(chsel, chan->regs + S3C2412_DMA_DMAREQSEL);
|
||||||
|
}
|
||||||
|
|
||||||
static void s3c2412_dma_select(struct s3c2410_dma_chan *chan,
|
static void s3c2412_dma_select(struct s3c2410_dma_chan *chan,
|
||||||
struct s3c24xx_dma_map *map)
|
struct s3c24xx_dma_map *map)
|
||||||
{
|
{
|
||||||
writel(map->channels[0] | S3C2412_DMAREQSEL_HW,
|
s3c2412_dma_direction(chan, map, chan->source);
|
||||||
chan->regs + S3C2412_DMA_DMAREQSEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct s3c24xx_dma_selection __initdata s3c2412_dma_sel = {
|
static struct s3c24xx_dma_selection __initdata s3c2412_dma_sel = {
|
||||||
.select = s3c2412_dma_select,
|
.select = s3c2412_dma_select,
|
||||||
|
.direction = s3c2412_dma_direction,
|
||||||
.dcon_mask = 0,
|
.dcon_mask = 0,
|
||||||
.map = s3c2412_dma_mappings,
|
.map = s3c2412_dma_mappings,
|
||||||
.map_size = ARRAY_SIZE(s3c2412_dma_mappings),
|
.map_size = ARRAY_SIZE(s3c2412_dma_mappings),
|
||||||
|
|
|
@ -1184,7 +1184,7 @@ int s3c2410_dma_devconfig(int channel,
|
||||||
dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));
|
dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));
|
||||||
|
|
||||||
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
|
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
|
||||||
return 0;
|
break;
|
||||||
|
|
||||||
case S3C2410_DMASRC_MEM:
|
case S3C2410_DMASRC_MEM:
|
||||||
/* source is memory */
|
/* source is memory */
|
||||||
|
@ -1195,11 +1195,19 @@ int s3c2410_dma_devconfig(int channel,
|
||||||
dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);
|
dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);
|
||||||
|
|
||||||
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
|
chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
|
||||||
return 0;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printk(KERN_ERR "dma%d: invalid source type (%d)\n",
|
||||||
|
channel, source);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source);
|
if (dma_sel.direction != NULL)
|
||||||
return -EINVAL;
|
(dma_sel.direction)(chan, chan->map, source);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(s3c2410_dma_devconfig);
|
EXPORT_SYMBOL(s3c2410_dma_devconfig);
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct s3c24xx_dma_map {
|
||||||
struct s3c24xx_dma_addr hw_addr;
|
struct s3c24xx_dma_addr hw_addr;
|
||||||
|
|
||||||
unsigned long channels[S3C2410_DMA_CHANNELS];
|
unsigned long channels[S3C2410_DMA_CHANNELS];
|
||||||
|
unsigned long channels_rx[S3C2410_DMA_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s3c24xx_dma_selection {
|
struct s3c24xx_dma_selection {
|
||||||
|
@ -41,6 +42,10 @@ struct s3c24xx_dma_selection {
|
||||||
|
|
||||||
void (*select)(struct s3c2410_dma_chan *chan,
|
void (*select)(struct s3c2410_dma_chan *chan,
|
||||||
struct s3c24xx_dma_map *map);
|
struct s3c24xx_dma_map *map);
|
||||||
|
|
||||||
|
void (*direction)(struct s3c2410_dma_chan *chan,
|
||||||
|
struct s3c24xx_dma_map *map,
|
||||||
|
enum s3c2410_dmasrc dir);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel);
|
extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel);
|
||||||
|
|
Loading…
Reference in New Issue