diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2009-08-08 17:07:22 -0400 |
---|---|---|
committer | Eric Miao <eric.y.miao@gmail.com> | 2009-09-10 06:49:29 -0400 |
commit | d294948c2ce4e1c85f452154469752cc9b8e876d (patch) | |
tree | a7d598a98344beec321e8b95bf18c204debd9a97 /arch/arm/plat-pxa | |
parent | d46f5e4a20867da84d12a35407cf7dcc8713c9cc (diff) |
[ARM] pxa/dma: add debugfs entries
Add some debug information for PXA DMA :
- descriptors queued
- channels state
- global state
--
Since V1: reverted to old register access (no more dma_readl() or dma_writel()).
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
Diffstat (limited to 'arch/arm/plat-pxa')
-rw-r--r-- | arch/arm/plat-pxa/dma.c | 249 |
1 files changed, 248 insertions, 1 deletions
diff --git a/arch/arm/plat-pxa/dma.c b/arch/arm/plat-pxa/dma.c index 56b51cf0718b..2975798d411f 100644 --- a/arch/arm/plat-pxa/dma.c +++ b/arch/arm/plat-pxa/dma.c | |||
@@ -17,22 +17,266 @@ | |||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | #include <linux/errno.h> | 19 | #include <linux/errno.h> |
20 | #include <linux/dma-mapping.h> | ||
20 | 21 | ||
21 | #include <asm/system.h> | 22 | #include <asm/system.h> |
22 | #include <asm/irq.h> | 23 | #include <asm/irq.h> |
24 | #include <asm/memory.h> | ||
23 | #include <mach/hardware.h> | 25 | #include <mach/hardware.h> |
24 | #include <mach/dma.h> | 26 | #include <mach/dma.h> |
25 | 27 | ||
28 | #define DMA_DEBUG_NAME "pxa_dma" | ||
29 | #define DMA_MAX_REQUESTERS 64 | ||
30 | |||
26 | struct dma_channel { | 31 | struct dma_channel { |
27 | char *name; | 32 | char *name; |
28 | pxa_dma_prio prio; | 33 | pxa_dma_prio prio; |
29 | void (*irq_handler)(int, void *); | 34 | void (*irq_handler)(int, void *); |
30 | void *data; | 35 | void *data; |
36 | spinlock_t lock; | ||
31 | }; | 37 | }; |
32 | 38 | ||
33 | static struct dma_channel *dma_channels; | 39 | static struct dma_channel *dma_channels; |
34 | static int num_dma_channels; | 40 | static int num_dma_channels; |
35 | 41 | ||
42 | /* | ||
43 | * Debug fs | ||
44 | */ | ||
45 | #ifdef CONFIG_DEBUG_FS | ||
46 | #include <linux/debugfs.h> | ||
47 | #include <linux/uaccess.h> | ||
48 | #include <linux/seq_file.h> | ||
49 | |||
50 | static struct dentry *dbgfs_root, *dbgfs_state, **dbgfs_chan; | ||
51 | |||
52 | static int dbg_show_requester_chan(struct seq_file *s, void *p) | ||
53 | { | ||
54 | int pos = 0; | ||
55 | int chan = (int)s->private; | ||
56 | int i; | ||
57 | u32 drcmr; | ||
58 | |||
59 | pos += seq_printf(s, "DMA channel %d requesters list :\n", chan); | ||
60 | for (i = 0; i < DMA_MAX_REQUESTERS; i++) { | ||
61 | drcmr = DRCMR(i); | ||
62 | if ((drcmr & DRCMR_CHLNUM) == chan) | ||
63 | pos += seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i, | ||
64 | !!(drcmr & DRCMR_MAPVLD)); | ||
65 | } | ||
66 | return pos; | ||
67 | } | ||
68 | |||
69 | static inline int dbg_burst_from_dcmd(u32 dcmd) | ||
70 | { | ||
71 | int burst = (dcmd >> 16) & 0x3; | ||
72 | |||
73 | return burst ? 4 << burst : 0; | ||
74 | } | ||
75 | |||
76 | static int is_phys_valid(unsigned long addr) | ||
77 | { | ||
78 | return pfn_valid(__phys_to_pfn(addr)); | ||
79 | } | ||
80 | |||
81 | #define DCSR_STR(flag) (dcsr & DCSR_##flag ? #flag" " : "") | ||
82 | #define DCMD_STR(flag) (dcmd & DCMD_##flag ? #flag" " : "") | ||
83 | |||
84 | static int dbg_show_descriptors(struct seq_file *s, void *p) | ||
85 | { | ||
86 | int pos = 0; | ||
87 | int chan = (int)s->private; | ||
88 | int i, max_show = 20, burst, width; | ||
89 | u32 dcmd; | ||
90 | unsigned long phys_desc; | ||
91 | struct pxa_dma_desc *desc; | ||
92 | unsigned long flags; | ||
93 | |||
94 | spin_lock_irqsave(&dma_channels[chan].lock, flags); | ||
95 | phys_desc = DDADR(chan); | ||
96 | |||
97 | pos += seq_printf(s, "DMA channel %d descriptors :\n", chan); | ||
98 | pos += seq_printf(s, "[%03d] First descriptor unknown\n", 0); | ||
99 | for (i = 1; i < max_show && is_phys_valid(phys_desc); i++) { | ||
100 | desc = phys_to_virt(phys_desc); | ||
101 | dcmd = desc->dcmd; | ||
102 | burst = dbg_burst_from_dcmd(dcmd); | ||
103 | width = (1 << ((dcmd >> 14) & 0x3)) >> 1; | ||
104 | |||
105 | pos += seq_printf(s, "[%03d] Desc at %08lx(virt %p)\n", | ||
106 | i, phys_desc, desc); | ||
107 | pos += seq_printf(s, "\tDDADR = %08x\n", desc->ddadr); | ||
108 | pos += seq_printf(s, "\tDSADR = %08x\n", desc->dsadr); | ||
109 | pos += seq_printf(s, "\tDTADR = %08x\n", desc->dtadr); | ||
110 | pos += seq_printf(s, "\tDCMD = %08x (%s%s%s%s%s%s%sburst=%d" | ||
111 | " width=%d len=%d)\n", | ||
112 | dcmd, | ||
113 | DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR), | ||
114 | DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG), | ||
115 | DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN), | ||
116 | DCMD_STR(ENDIAN), burst, width, | ||
117 | dcmd & DCMD_LENGTH); | ||
118 | phys_desc = desc->ddadr; | ||
119 | } | ||
120 | if (i == max_show) | ||
121 | pos += seq_printf(s, "[%03d] Desc at %08lx ... max display reached\n", | ||
122 | i, phys_desc); | ||
123 | else | ||
124 | pos += seq_printf(s, "[%03d] Desc at %08lx is %s\n", | ||
125 | i, phys_desc, phys_desc == DDADR_STOP ? | ||
126 | "DDADR_STOP" : "invalid"); | ||
127 | |||
128 | spin_unlock_irqrestore(&dma_channels[chan].lock, flags); | ||
129 | return pos; | ||
130 | } | ||
131 | |||
132 | static int dbg_show_chan_state(struct seq_file *s, void *p) | ||
133 | { | ||
134 | int pos = 0; | ||
135 | int chan = (int)s->private; | ||
136 | u32 dcsr, dcmd; | ||
137 | int burst, width; | ||
138 | static char *str_prio[] = { "high", "normal", "low" }; | ||
139 | |||
140 | dcsr = DCSR(chan); | ||
141 | dcmd = DCMD(chan); | ||
142 | burst = dbg_burst_from_dcmd(dcmd); | ||
143 | width = (1 << ((dcmd >> 14) & 0x3)) >> 1; | ||
144 | |||
145 | pos += seq_printf(s, "DMA channel %d\n", chan); | ||
146 | pos += seq_printf(s, "\tPriority : %s\n", | ||
147 | str_prio[dma_channels[chan].prio]); | ||
148 | pos += seq_printf(s, "\tUnaligned transfer bit: %s\n", | ||
149 | DALGN & (1 << chan) ? "yes" : "no"); | ||
150 | pos += seq_printf(s, "\tDCSR = %08x (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", | ||
151 | dcsr, DCSR_STR(RUN), DCSR_STR(NODESC), | ||
152 | DCSR_STR(STOPIRQEN), DCSR_STR(EORIRQEN), | ||
153 | DCSR_STR(EORJMPEN), DCSR_STR(EORSTOPEN), | ||
154 | DCSR_STR(SETCMPST), DCSR_STR(CLRCMPST), | ||
155 | DCSR_STR(CMPST), DCSR_STR(EORINTR), DCSR_STR(REQPEND), | ||
156 | DCSR_STR(STOPSTATE), DCSR_STR(ENDINTR), | ||
157 | DCSR_STR(STARTINTR), DCSR_STR(BUSERR)); | ||
158 | |||
159 | pos += seq_printf(s, "\tDCMD = %08x (%s%s%s%s%s%s%sburst=%d width=%d" | ||
160 | " len=%d)\n", | ||
161 | dcmd, | ||
162 | DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR), | ||
163 | DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG), | ||
164 | DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN), | ||
165 | DCMD_STR(ENDIAN), burst, width, dcmd & DCMD_LENGTH); | ||
166 | pos += seq_printf(s, "\tDSADR = %08x\n", DSADR(chan)); | ||
167 | pos += seq_printf(s, "\tDTADR = %08x\n", DTADR(chan)); | ||
168 | pos += seq_printf(s, "\tDDADR = %08x\n", DDADR(chan)); | ||
169 | return pos; | ||
170 | } | ||
171 | |||
172 | static int dbg_show_state(struct seq_file *s, void *p) | ||
173 | { | ||
174 | int pos = 0; | ||
175 | |||
176 | /* basic device status */ | ||
177 | pos += seq_printf(s, "DMA engine status\n"); | ||
178 | pos += seq_printf(s, "\tChannel number: %d\n", num_dma_channels); | ||
179 | |||
180 | return pos; | ||
181 | } | ||
182 | |||
183 | #define DBGFS_FUNC_DECL(name) \ | ||
184 | static int dbg_open_##name(struct inode *inode, struct file *file) \ | ||
185 | { \ | ||
186 | return single_open(file, dbg_show_##name, inode->i_private); \ | ||
187 | } \ | ||
188 | static const struct file_operations dbg_fops_##name = { \ | ||
189 | .owner = THIS_MODULE, \ | ||
190 | .open = dbg_open_##name, \ | ||
191 | .llseek = seq_lseek, \ | ||
192 | .read = seq_read, \ | ||
193 | .release = single_release, \ | ||
194 | } | ||
195 | |||
196 | DBGFS_FUNC_DECL(state); | ||
197 | DBGFS_FUNC_DECL(chan_state); | ||
198 | DBGFS_FUNC_DECL(descriptors); | ||
199 | DBGFS_FUNC_DECL(requester_chan); | ||
200 | |||
201 | static struct dentry *pxa_dma_dbg_alloc_chan(int ch, struct dentry *chandir) | ||
202 | { | ||
203 | char chan_name[11]; | ||
204 | struct dentry *chan, *chan_state = NULL, *chan_descr = NULL; | ||
205 | struct dentry *chan_reqs = NULL; | ||
206 | void *dt; | ||
207 | |||
208 | scnprintf(chan_name, sizeof(chan_name), "%d", ch); | ||
209 | chan = debugfs_create_dir(chan_name, chandir); | ||
210 | dt = (void *)ch; | ||
211 | |||
212 | if (chan) | ||
213 | chan_state = debugfs_create_file("state", 0400, chan, dt, | ||
214 | &dbg_fops_chan_state); | ||
215 | if (chan_state) | ||
216 | chan_descr = debugfs_create_file("descriptors", 0400, chan, dt, | ||
217 | &dbg_fops_descriptors); | ||
218 | if (chan_descr) | ||
219 | chan_reqs = debugfs_create_file("requesters", 0400, chan, dt, | ||
220 | &dbg_fops_requester_chan); | ||
221 | if (!chan_reqs) | ||
222 | goto err_state; | ||
223 | |||
224 | return chan; | ||
225 | |||
226 | err_state: | ||
227 | debugfs_remove_recursive(chan); | ||
228 | return NULL; | ||
229 | } | ||
230 | |||
231 | static void pxa_dma_init_debugfs(void) | ||
232 | { | ||
233 | int i; | ||
234 | struct dentry *chandir; | ||
235 | |||
236 | dbgfs_root = debugfs_create_dir(DMA_DEBUG_NAME, NULL); | ||
237 | if (IS_ERR(dbgfs_root) || !dbgfs_root) | ||
238 | goto err_root; | ||
239 | |||
240 | dbgfs_state = debugfs_create_file("state", 0400, dbgfs_root, NULL, | ||
241 | &dbg_fops_state); | ||
242 | if (!dbgfs_state) | ||
243 | goto err_state; | ||
244 | |||
245 | dbgfs_chan = kmalloc(sizeof(*dbgfs_state) * num_dma_channels, | ||
246 | GFP_KERNEL); | ||
247 | if (!dbgfs_state) | ||
248 | goto err_alloc; | ||
249 | |||
250 | chandir = debugfs_create_dir("channels", dbgfs_root); | ||
251 | if (!chandir) | ||
252 | goto err_chandir; | ||
253 | |||
254 | for (i = 0; i < num_dma_channels; i++) { | ||
255 | dbgfs_chan[i] = pxa_dma_dbg_alloc_chan(i, chandir); | ||
256 | if (!dbgfs_chan[i]) | ||
257 | goto err_chans; | ||
258 | } | ||
259 | |||
260 | return; | ||
261 | err_chans: | ||
262 | err_chandir: | ||
263 | kfree(dbgfs_chan); | ||
264 | err_alloc: | ||
265 | err_state: | ||
266 | debugfs_remove_recursive(dbgfs_root); | ||
267 | err_root: | ||
268 | pr_err("pxa_dma: debugfs is not available\n"); | ||
269 | } | ||
270 | |||
271 | static void __exit pxa_dma_cleanup_debugfs(void) | ||
272 | { | ||
273 | debugfs_remove_recursive(dbgfs_root); | ||
274 | } | ||
275 | #else | ||
276 | static inline void pxa_dma_init_debugfs(void) {} | ||
277 | static inline void pxa_dma_cleanup_debugfs(void) {} | ||
278 | #endif | ||
279 | |||
36 | int pxa_request_dma (char *name, pxa_dma_prio prio, | 280 | int pxa_request_dma (char *name, pxa_dma_prio prio, |
37 | void (*irq_handler)(int, void *), | 281 | void (*irq_handler)(int, void *), |
38 | void *data) | 282 | void *data) |
@@ -130,6 +374,7 @@ int __init pxa_init_dma(int irq, int num_ch) | |||
130 | for (i = 0; i < num_ch; i++) { | 374 | for (i = 0; i < num_ch; i++) { |
131 | DCSR(i) = 0; | 375 | DCSR(i) = 0; |
132 | dma_channels[i].prio = min((i & 0xf) >> 2, DMA_PRIO_LOW); | 376 | dma_channels[i].prio = min((i & 0xf) >> 2, DMA_PRIO_LOW); |
377 | spin_lock_init(&dma_channels[i].lock); | ||
133 | } | 378 | } |
134 | 379 | ||
135 | ret = request_irq(irq, dma_irq_handler, IRQF_DISABLED, "DMA", NULL); | 380 | ret = request_irq(irq, dma_irq_handler, IRQF_DISABLED, "DMA", NULL); |
@@ -138,7 +383,9 @@ int __init pxa_init_dma(int irq, int num_ch) | |||
138 | kfree(dma_channels); | 383 | kfree(dma_channels); |
139 | return ret; | 384 | return ret; |
140 | } | 385 | } |
141 | |||
142 | num_dma_channels = num_ch; | 386 | num_dma_channels = num_ch; |
387 | |||
388 | pxa_dma_init_debugfs(); | ||
389 | |||
143 | return 0; | 390 | return 0; |
144 | } | 391 | } |