[ARM] 4177/1: S3C24XX: Add DMA channel allocation order
Allow the CPU code, and any board specific initialisation code to change the allocation order of the DMA channels, or stop a peripheral allocating any DMA at-all. This is due to the scarce mapping of DMA channels on some earlier S3C24XX cpus, where the selection changes depending on the channel in use. 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
d2a76020e3
commit
0c6022d453
|
@ -0,0 +1,46 @@
|
||||||
|
S3C2410 DMA
|
||||||
|
===========
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
The kernel provides an interface to manage DMA transfers
|
||||||
|
using the DMA channels in the cpu, so that the central
|
||||||
|
duty of managing channel mappings, and programming the
|
||||||
|
channel generators is in one place.
|
||||||
|
|
||||||
|
|
||||||
|
DMA Channel Ordering
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Many of the range do not have connections for the DMA
|
||||||
|
channels to all sources, which means that some devices
|
||||||
|
have a restricted number of channels that can be used.
|
||||||
|
|
||||||
|
To allow flexibilty for each cpu type and board, the
|
||||||
|
dma code can be given an dma ordering structure which
|
||||||
|
allows the order of channel search to be specified, as
|
||||||
|
well as allowing the prohibition of certain claims.
|
||||||
|
|
||||||
|
struct s3c24xx_dma_order has a list of channels, and
|
||||||
|
each channel within has a slot for a list of dma
|
||||||
|
channel numbers. The slots are searched in order, for
|
||||||
|
the presence of a dma channel number with DMA_CH_VALID
|
||||||
|
orred in.
|
||||||
|
|
||||||
|
If the order has the flag DMA_CH_NEVER set, then after
|
||||||
|
checking the channel list, the system will return no
|
||||||
|
found channel, thus denying the request.
|
||||||
|
|
||||||
|
A board support file can call s3c24xx_dma_order_set()
|
||||||
|
to register an complete ordering set. The routine will
|
||||||
|
copy the data, so the original can be discared with
|
||||||
|
__initdata.
|
||||||
|
|
||||||
|
|
||||||
|
Authour
|
||||||
|
-------
|
||||||
|
|
||||||
|
Ben Dooks,
|
||||||
|
Copyright (c) 2007 Ben Dooks, Simtec Electronics
|
||||||
|
Licensed under the GPL v2
|
|
@ -1354,18 +1354,22 @@ static inline int is_channel_valid(unsigned int channel)
|
||||||
return (channel & DMA_CH_VALID);
|
return (channel & DMA_CH_VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct s3c24xx_dma_order *dma_order;
|
||||||
|
|
||||||
|
|
||||||
/* s3c2410_dma_map_channel()
|
/* s3c2410_dma_map_channel()
|
||||||
*
|
*
|
||||||
* turn the virtual channel number into a real, and un-used hardware
|
* turn the virtual channel number into a real, and un-used hardware
|
||||||
* channel.
|
* channel.
|
||||||
*
|
*
|
||||||
* currently this code uses first-free channel from the specified harware
|
* first, try the dma ordering given to us by either the relevant
|
||||||
* map, not taking into account anything that the board setup code may
|
* dma code, or the board. Then just find the first usable free
|
||||||
* have to say about the likely peripheral set to be in use.
|
* channel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
|
struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
|
||||||
{
|
{
|
||||||
|
struct s3c24xx_dma_order_ch *ord = NULL;
|
||||||
struct s3c24xx_dma_map *ch_map;
|
struct s3c24xx_dma_map *ch_map;
|
||||||
struct s3c2410_dma_chan *dmach;
|
struct s3c2410_dma_chan *dmach;
|
||||||
int ch;
|
int ch;
|
||||||
|
@ -1375,6 +1379,27 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
|
||||||
|
|
||||||
ch_map = dma_sel.map + channel;
|
ch_map = dma_sel.map + channel;
|
||||||
|
|
||||||
|
/* first, try the board mapping */
|
||||||
|
|
||||||
|
if (dma_order) {
|
||||||
|
ord = &dma_order->channels[channel];
|
||||||
|
|
||||||
|
for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) {
|
||||||
|
if (!is_channel_valid(ord->list[ch]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (s3c2410_chans[ord->list[ch]].in_use == 0) {
|
||||||
|
ch = ord->list[ch] & ~DMA_CH_VALID;
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ord->flags & DMA_CH_NEVER)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* second, search the channel map for first free */
|
||||||
|
|
||||||
for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) {
|
for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) {
|
||||||
if (!is_channel_valid(ch_map->channels[ch]))
|
if (!is_channel_valid(ch_map->channels[ch]))
|
||||||
continue;
|
continue;
|
||||||
|
@ -1390,6 +1415,7 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
|
||||||
|
|
||||||
/* update our channel mapping */
|
/* update our channel mapping */
|
||||||
|
|
||||||
|
found:
|
||||||
dmach = &s3c2410_chans[ch];
|
dmach = &s3c2410_chans[ch];
|
||||||
dma_chan_map[channel] = dmach;
|
dma_chan_map[channel] = dmach;
|
||||||
|
|
||||||
|
@ -1439,3 +1465,20 @@ int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
|
||||||
|
{
|
||||||
|
struct s3c24xx_dma_order *nord = dma_order;
|
||||||
|
|
||||||
|
if (nord == NULL)
|
||||||
|
nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (nord == NULL) {
|
||||||
|
printk(KERN_ERR "no memory to store dma channel order\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_order = nord;
|
||||||
|
memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ extern struct sysdev_class dma_sysclass;
|
||||||
extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS];
|
extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS];
|
||||||
|
|
||||||
#define DMA_CH_VALID (1<<31)
|
#define DMA_CH_VALID (1<<31)
|
||||||
|
#define DMA_CH_NEVER (1<<30)
|
||||||
|
|
||||||
struct s3c24xx_dma_addr {
|
struct s3c24xx_dma_addr {
|
||||||
unsigned long from;
|
unsigned long from;
|
||||||
|
@ -43,3 +44,27 @@ struct s3c24xx_dma_selection {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel);
|
extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel);
|
||||||
|
|
||||||
|
/* struct s3c24xx_dma_order_ch
|
||||||
|
*
|
||||||
|
* channel map for one of the `enum dma_ch` dma channels. the list
|
||||||
|
* entry contains a set of low-level channel numbers, orred with
|
||||||
|
* DMA_CH_VALID, which are checked in the order in the array.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct s3c24xx_dma_order_ch {
|
||||||
|
unsigned int list[S3C2410_DMA_CHANNELS]; /* list of channels */
|
||||||
|
unsigned int flags; /* flags */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* struct s3c24xx_dma_order
|
||||||
|
*
|
||||||
|
* information provided by either the core or the board to give the
|
||||||
|
* dma system a hint on how to allocate channels
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct s3c24xx_dma_order {
|
||||||
|
struct s3c24xx_dma_order_ch channels[DMACH_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int s3c24xx_dma_order_set(struct s3c24xx_dma_order *map);
|
||||||
|
|
Loading…
Reference in New Issue