diff options
author | Ben Dooks <ben-linux@fluff.org> | 2007-02-13 13:02:52 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-14 15:04:22 +0000 |
commit | 0c6022d453ecebdace0ce15434c7108e158149ca (patch) | |
tree | 153ce6dd661059601fa8591d36e0b81c6663566a | |
parent | d2a76020e3a52c6370a7d603082b4cdb3db0703e (diff) |
[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>
-rw-r--r-- | Documentation/arm/Samsung-S3C24XX/DMA.txt | 46 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/dma.c | 49 | ||||
-rw-r--r-- | include/asm-arm/plat-s3c24xx/dma.h | 25 |
3 files changed, 117 insertions, 3 deletions
diff --git a/Documentation/arm/Samsung-S3C24XX/DMA.txt b/Documentation/arm/Samsung-S3C24XX/DMA.txt new file mode 100644 index 000000000000..37f4edcc5d87 --- /dev/null +++ b/Documentation/arm/Samsung-S3C24XX/DMA.txt @@ -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 diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index c784e1f816bb..44e39438b9d8 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -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; +} diff --git a/include/asm-arm/plat-s3c24xx/dma.h b/include/asm-arm/plat-s3c24xx/dma.h index 421b567fa019..15e140c2d4fc 100644 --- a/include/asm-arm/plat-s3c24xx/dma.h +++ b/include/asm-arm/plat-s3c24xx/dma.h @@ -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); |