[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:
Ben Dooks 2007-02-13 13:02:52 +01:00 committed by Russell King
parent d2a76020e3
commit 0c6022d453
3 changed files with 117 additions and 3 deletions

View file

@ -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

View file

@ -1354,18 +1354,22 @@ static inline int is_channel_valid(unsigned int channel)
return (channel & DMA_CH_VALID);
}
static struct s3c24xx_dma_order *dma_order;
/* s3c2410_dma_map_channel()
*
* turn the virtual channel number into a real, and un-used hardware
* channel.
*
* currently this code uses first-free channel from the specified harware
* map, not taking into account anything that the board setup code may
* have to say about the likely peripheral set to be in use.
* first, try the dma ordering given to us by either the relevant
* dma code, or the board. Then just find the first usable free
* 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 s3c2410_dma_chan *dmach;
int ch;
@ -1375,6 +1379,27 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int 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++) {
if (!is_channel_valid(ch_map->channels[ch]))
continue;
@ -1390,6 +1415,7 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
/* update our channel mapping */
found:
dmach = &s3c2410_chans[ch];
dma_chan_map[channel] = dmach;
@ -1439,3 +1465,20 @@ int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
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;
}

View file

@ -14,6 +14,7 @@ extern struct sysdev_class dma_sysclass;
extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS];
#define DMA_CH_VALID (1<<31)
#define DMA_CH_NEVER (1<<30)
struct s3c24xx_dma_addr {
unsigned long from;
@ -43,3 +44,27 @@ struct s3c24xx_dma_selection {
};
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);