diff options
author | Ben Dooks <ben-linux@fluff.org> | 2007-02-13 07:02:52 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-14 10:04:22 -0500 |
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 @@ | |||
1 | S3C2410 DMA | ||
2 | =========== | ||
3 | |||
4 | Introduction | ||
5 | ------------ | ||
6 | |||
7 | The kernel provides an interface to manage DMA transfers | ||
8 | using the DMA channels in the cpu, so that the central | ||
9 | duty of managing channel mappings, and programming the | ||
10 | channel generators is in one place. | ||
11 | |||
12 | |||
13 | DMA Channel Ordering | ||
14 | -------------------- | ||
15 | |||
16 | Many of the range do not have connections for the DMA | ||
17 | channels to all sources, which means that some devices | ||
18 | have a restricted number of channels that can be used. | ||
19 | |||
20 | To allow flexibilty for each cpu type and board, the | ||
21 | dma code can be given an dma ordering structure which | ||
22 | allows the order of channel search to be specified, as | ||
23 | well as allowing the prohibition of certain claims. | ||
24 | |||
25 | struct s3c24xx_dma_order has a list of channels, and | ||
26 | each channel within has a slot for a list of dma | ||
27 | channel numbers. The slots are searched in order, for | ||
28 | the presence of a dma channel number with DMA_CH_VALID | ||
29 | orred in. | ||
30 | |||
31 | If the order has the flag DMA_CH_NEVER set, then after | ||
32 | checking the channel list, the system will return no | ||
33 | found channel, thus denying the request. | ||
34 | |||
35 | A board support file can call s3c24xx_dma_order_set() | ||
36 | to register an complete ordering set. The routine will | ||
37 | copy the data, so the original can be discared with | ||
38 | __initdata. | ||
39 | |||
40 | |||
41 | Authour | ||
42 | ------- | ||
43 | |||
44 | Ben Dooks, | ||
45 | Copyright (c) 2007 Ben Dooks, Simtec Electronics | ||
46 | 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) | |||
1354 | return (channel & DMA_CH_VALID); | 1354 | return (channel & DMA_CH_VALID); |
1355 | } | 1355 | } |
1356 | 1356 | ||
1357 | static struct s3c24xx_dma_order *dma_order; | ||
1358 | |||
1359 | |||
1357 | /* s3c2410_dma_map_channel() | 1360 | /* s3c2410_dma_map_channel() |
1358 | * | 1361 | * |
1359 | * turn the virtual channel number into a real, and un-used hardware | 1362 | * turn the virtual channel number into a real, and un-used hardware |
1360 | * channel. | 1363 | * channel. |
1361 | * | 1364 | * |
1362 | * currently this code uses first-free channel from the specified harware | 1365 | * first, try the dma ordering given to us by either the relevant |
1363 | * map, not taking into account anything that the board setup code may | 1366 | * dma code, or the board. Then just find the first usable free |
1364 | * have to say about the likely peripheral set to be in use. | 1367 | * channel |
1365 | */ | 1368 | */ |
1366 | 1369 | ||
1367 | struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) | 1370 | struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) |
1368 | { | 1371 | { |
1372 | struct s3c24xx_dma_order_ch *ord = NULL; | ||
1369 | struct s3c24xx_dma_map *ch_map; | 1373 | struct s3c24xx_dma_map *ch_map; |
1370 | struct s3c2410_dma_chan *dmach; | 1374 | struct s3c2410_dma_chan *dmach; |
1371 | int ch; | 1375 | int ch; |
@@ -1375,6 +1379,27 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) | |||
1375 | 1379 | ||
1376 | ch_map = dma_sel.map + channel; | 1380 | ch_map = dma_sel.map + channel; |
1377 | 1381 | ||
1382 | /* first, try the board mapping */ | ||
1383 | |||
1384 | if (dma_order) { | ||
1385 | ord = &dma_order->channels[channel]; | ||
1386 | |||
1387 | for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { | ||
1388 | if (!is_channel_valid(ord->list[ch])) | ||
1389 | continue; | ||
1390 | |||
1391 | if (s3c2410_chans[ord->list[ch]].in_use == 0) { | ||
1392 | ch = ord->list[ch] & ~DMA_CH_VALID; | ||
1393 | goto found; | ||
1394 | } | ||
1395 | } | ||
1396 | |||
1397 | if (ord->flags & DMA_CH_NEVER) | ||
1398 | return NULL; | ||
1399 | } | ||
1400 | |||
1401 | /* second, search the channel map for first free */ | ||
1402 | |||
1378 | for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { | 1403 | for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { |
1379 | if (!is_channel_valid(ch_map->channels[ch])) | 1404 | if (!is_channel_valid(ch_map->channels[ch])) |
1380 | continue; | 1405 | continue; |
@@ -1390,6 +1415,7 @@ struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) | |||
1390 | 1415 | ||
1391 | /* update our channel mapping */ | 1416 | /* update our channel mapping */ |
1392 | 1417 | ||
1418 | found: | ||
1393 | dmach = &s3c2410_chans[ch]; | 1419 | dmach = &s3c2410_chans[ch]; |
1394 | dma_chan_map[channel] = dmach; | 1420 | dma_chan_map[channel] = dmach; |
1395 | 1421 | ||
@@ -1439,3 +1465,20 @@ int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel) | |||
1439 | 1465 | ||
1440 | return 0; | 1466 | return 0; |
1441 | } | 1467 | } |
1468 | |||
1469 | int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord) | ||
1470 | { | ||
1471 | struct s3c24xx_dma_order *nord = dma_order; | ||
1472 | |||
1473 | if (nord == NULL) | ||
1474 | nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL); | ||
1475 | |||
1476 | if (nord == NULL) { | ||
1477 | printk(KERN_ERR "no memory to store dma channel order\n"); | ||
1478 | return -ENOMEM; | ||
1479 | } | ||
1480 | |||
1481 | dma_order = nord; | ||
1482 | memcpy(nord, ord, sizeof(struct s3c24xx_dma_order)); | ||
1483 | return 0; | ||
1484 | } | ||
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; | |||
14 | extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; | 14 | extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; |
15 | 15 | ||
16 | #define DMA_CH_VALID (1<<31) | 16 | #define DMA_CH_VALID (1<<31) |
17 | #define DMA_CH_NEVER (1<<30) | ||
17 | 18 | ||
18 | struct s3c24xx_dma_addr { | 19 | struct s3c24xx_dma_addr { |
19 | unsigned long from; | 20 | unsigned long from; |
@@ -43,3 +44,27 @@ struct s3c24xx_dma_selection { | |||
43 | }; | 44 | }; |
44 | 45 | ||
45 | extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel); | 46 | extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel); |
47 | |||
48 | /* struct s3c24xx_dma_order_ch | ||
49 | * | ||
50 | * channel map for one of the `enum dma_ch` dma channels. the list | ||
51 | * entry contains a set of low-level channel numbers, orred with | ||
52 | * DMA_CH_VALID, which are checked in the order in the array. | ||
53 | */ | ||
54 | |||
55 | struct s3c24xx_dma_order_ch { | ||
56 | unsigned int list[S3C2410_DMA_CHANNELS]; /* list of channels */ | ||
57 | unsigned int flags; /* flags */ | ||
58 | }; | ||
59 | |||
60 | /* struct s3c24xx_dma_order | ||
61 | * | ||
62 | * information provided by either the core or the board to give the | ||
63 | * dma system a hint on how to allocate channels | ||
64 | */ | ||
65 | |||
66 | struct s3c24xx_dma_order { | ||
67 | struct s3c24xx_dma_order_ch channels[DMACH_MAX]; | ||
68 | }; | ||
69 | |||
70 | extern int s3c24xx_dma_order_set(struct s3c24xx_dma_order *map); | ||