1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2009 by Martin Fuzzey
4  */
5 
6 /* this file is part of imx21-hcd.c */
7 
8 #ifdef CONFIG_DYNAMIC_DEBUG
9 #define DEBUG
10 #endif
11 
12 #ifndef DEBUG
13 
create_debug_files(struct imx21 * imx21)14 static inline void create_debug_files(struct imx21 *imx21) { }
remove_debug_files(struct imx21 * imx21)15 static inline void remove_debug_files(struct imx21 *imx21) { }
debug_urb_submitted(struct imx21 * imx21,struct urb * urb)16 static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {}
debug_urb_completed(struct imx21 * imx21,struct urb * urb,int status)17 static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb,
18 	int status) {}
debug_urb_unlinked(struct imx21 * imx21,struct urb * urb)19 static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {}
debug_urb_queued_for_etd(struct imx21 * imx21,struct urb * urb)20 static inline void debug_urb_queued_for_etd(struct imx21 *imx21,
21 	struct urb *urb) {}
debug_urb_queued_for_dmem(struct imx21 * imx21,struct urb * urb)22 static inline void debug_urb_queued_for_dmem(struct imx21 *imx21,
23 	struct urb *urb) {}
debug_etd_allocated(struct imx21 * imx21)24 static inline void debug_etd_allocated(struct imx21 *imx21) {}
debug_etd_freed(struct imx21 * imx21)25 static inline void debug_etd_freed(struct imx21 *imx21) {}
debug_dmem_allocated(struct imx21 * imx21,int size)26 static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {}
debug_dmem_freed(struct imx21 * imx21,int size)27 static inline void debug_dmem_freed(struct imx21 *imx21, int size) {}
debug_isoc_submitted(struct imx21 * imx21,int frame,struct td * td)28 static inline void debug_isoc_submitted(struct imx21 *imx21,
29 	int frame, struct td *td) {}
debug_isoc_completed(struct imx21 * imx21,int frame,struct td * td,int cc,int len)30 static inline void debug_isoc_completed(struct imx21 *imx21,
31 	int frame, struct td *td, int cc, int len) {}
32 
33 #else
34 
35 #include <linux/debugfs.h>
36 #include <linux/seq_file.h>
37 
38 static const char *dir_labels[] = {
39 	"TD 0",
40 	"OUT",
41 	"IN",
42 	"TD 1"
43 };
44 
45 static const char *speed_labels[] = {
46 	"Full",
47 	"Low"
48 };
49 
50 static const char *format_labels[] = {
51 	"Control",
52 	"ISO",
53 	"Bulk",
54 	"Interrupt"
55 };
56 
stats_for_urb(struct imx21 * imx21,struct urb * urb)57 static inline struct debug_stats *stats_for_urb(struct imx21 *imx21,
58 	struct urb *urb)
59 {
60 	return usb_pipeisoc(urb->pipe) ?
61 		&imx21->isoc_stats : &imx21->nonisoc_stats;
62 }
63 
debug_urb_submitted(struct imx21 * imx21,struct urb * urb)64 static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb)
65 {
66 	stats_for_urb(imx21, urb)->submitted++;
67 }
68 
debug_urb_completed(struct imx21 * imx21,struct urb * urb,int st)69 static void debug_urb_completed(struct imx21 *imx21, struct urb *urb, int st)
70 {
71 	if (st)
72 		stats_for_urb(imx21, urb)->completed_failed++;
73 	else
74 		stats_for_urb(imx21, urb)->completed_ok++;
75 }
76 
debug_urb_unlinked(struct imx21 * imx21,struct urb * urb)77 static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb)
78 {
79 	stats_for_urb(imx21, urb)->unlinked++;
80 }
81 
debug_urb_queued_for_etd(struct imx21 * imx21,struct urb * urb)82 static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb)
83 {
84 	stats_for_urb(imx21, urb)->queue_etd++;
85 }
86 
debug_urb_queued_for_dmem(struct imx21 * imx21,struct urb * urb)87 static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb)
88 {
89 	stats_for_urb(imx21, urb)->queue_dmem++;
90 }
91 
debug_etd_allocated(struct imx21 * imx21)92 static inline void debug_etd_allocated(struct imx21 *imx21)
93 {
94 	imx21->etd_usage.maximum = max(
95 			++(imx21->etd_usage.value),
96 			imx21->etd_usage.maximum);
97 }
98 
debug_etd_freed(struct imx21 * imx21)99 static inline void debug_etd_freed(struct imx21 *imx21)
100 {
101 	imx21->etd_usage.value--;
102 }
103 
debug_dmem_allocated(struct imx21 * imx21,int size)104 static inline void debug_dmem_allocated(struct imx21 *imx21, int size)
105 {
106 	imx21->dmem_usage.value += size;
107 	imx21->dmem_usage.maximum = max(
108 			imx21->dmem_usage.value,
109 			imx21->dmem_usage.maximum);
110 }
111 
debug_dmem_freed(struct imx21 * imx21,int size)112 static inline void debug_dmem_freed(struct imx21 *imx21, int size)
113 {
114 	imx21->dmem_usage.value -= size;
115 }
116 
117 
debug_isoc_submitted(struct imx21 * imx21,int frame,struct td * td)118 static void debug_isoc_submitted(struct imx21 *imx21,
119 	int frame, struct td *td)
120 {
121 	struct debug_isoc_trace *trace = &imx21->isoc_trace[
122 		imx21->isoc_trace_index++];
123 
124 	imx21->isoc_trace_index %= ARRAY_SIZE(imx21->isoc_trace);
125 	trace->schedule_frame = td->frame;
126 	trace->submit_frame = frame;
127 	trace->request_len = td->len;
128 	trace->td = td;
129 }
130 
debug_isoc_completed(struct imx21 * imx21,int frame,struct td * td,int cc,int len)131 static inline void debug_isoc_completed(struct imx21 *imx21,
132 	int frame, struct td *td, int cc, int len)
133 {
134 	struct debug_isoc_trace *trace, *trace_failed;
135 	int i;
136 	int found = 0;
137 
138 	trace = imx21->isoc_trace;
139 	for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) {
140 		if (trace->td == td) {
141 			trace->done_frame = frame;
142 			trace->done_len = len;
143 			trace->cc = cc;
144 			trace->td = NULL;
145 			found = 1;
146 			break;
147 		}
148 	}
149 
150 	if (found && cc) {
151 		trace_failed = &imx21->isoc_trace_failed[
152 					imx21->isoc_trace_index_failed++];
153 
154 		imx21->isoc_trace_index_failed %= ARRAY_SIZE(
155 						imx21->isoc_trace_failed);
156 		*trace_failed = *trace;
157 	}
158 }
159 
160 
format_ep(struct usb_host_endpoint * ep,char * buf,int bufsize)161 static char *format_ep(struct usb_host_endpoint *ep, char *buf, int bufsize)
162 {
163 	if (ep)
164 		snprintf(buf, bufsize, "ep_%02x (type:%02X kaddr:%p)",
165 			ep->desc.bEndpointAddress,
166 			usb_endpoint_type(&ep->desc),
167 			ep);
168 	else
169 		snprintf(buf, bufsize, "none");
170 	return buf;
171 }
172 
format_etd_dword0(u32 value,char * buf,int bufsize)173 static char *format_etd_dword0(u32 value, char *buf, int bufsize)
174 {
175 	snprintf(buf, bufsize,
176 		"addr=%d ep=%d dir=%s speed=%s format=%s halted=%d",
177 		value & 0x7F,
178 		(value >> DW0_ENDPNT) & 0x0F,
179 		dir_labels[(value >> DW0_DIRECT) & 0x03],
180 		speed_labels[(value >> DW0_SPEED) & 0x01],
181 		format_labels[(value >> DW0_FORMAT) & 0x03],
182 		(value >> DW0_HALTED) & 0x01);
183 	return buf;
184 }
185 
debug_status_show(struct seq_file * s,void * v)186 static int debug_status_show(struct seq_file *s, void *v)
187 {
188 	struct imx21 *imx21 = s->private;
189 	int etds_allocated = 0;
190 	int etds_sw_busy = 0;
191 	int etds_hw_busy = 0;
192 	int dmem_blocks = 0;
193 	int queued_for_etd = 0;
194 	int queued_for_dmem = 0;
195 	unsigned int dmem_bytes = 0;
196 	int i;
197 	struct etd_priv *etd;
198 	u32 etd_enable_mask;
199 	unsigned long flags;
200 	struct imx21_dmem_area *dmem;
201 	struct ep_priv *ep_priv;
202 
203 	spin_lock_irqsave(&imx21->lock, flags);
204 
205 	etd_enable_mask = readl(imx21->regs + USBH_ETDENSET);
206 	for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
207 		if (etd->alloc)
208 			etds_allocated++;
209 		if (etd->urb)
210 			etds_sw_busy++;
211 		if (etd_enable_mask & (1<<i))
212 			etds_hw_busy++;
213 	}
214 
215 	list_for_each_entry(dmem, &imx21->dmem_list, list) {
216 		dmem_bytes += dmem->size;
217 		dmem_blocks++;
218 	}
219 
220 	list_for_each_entry(ep_priv, &imx21->queue_for_etd, queue)
221 		queued_for_etd++;
222 
223 	list_for_each_entry(etd, &imx21->queue_for_dmem, queue)
224 		queued_for_dmem++;
225 
226 	spin_unlock_irqrestore(&imx21->lock, flags);
227 
228 	seq_printf(s,
229 		"Frame: %d\n"
230 		"ETDs allocated: %d/%d (max=%d)\n"
231 		"ETDs in use sw: %d\n"
232 		"ETDs in use hw: %d\n"
233 		"DMEM allocated: %d/%d (max=%d)\n"
234 		"DMEM blocks: %d\n"
235 		"Queued waiting for ETD: %d\n"
236 		"Queued waiting for DMEM: %d\n",
237 		readl(imx21->regs + USBH_FRMNUB) & 0xFFFF,
238 		etds_allocated, USB_NUM_ETD, imx21->etd_usage.maximum,
239 		etds_sw_busy,
240 		etds_hw_busy,
241 		dmem_bytes, DMEM_SIZE, imx21->dmem_usage.maximum,
242 		dmem_blocks,
243 		queued_for_etd,
244 		queued_for_dmem);
245 
246 	return 0;
247 }
248 DEFINE_SHOW_ATTRIBUTE(debug_status);
249 
debug_dmem_show(struct seq_file * s,void * v)250 static int debug_dmem_show(struct seq_file *s, void *v)
251 {
252 	struct imx21 *imx21 = s->private;
253 	struct imx21_dmem_area *dmem;
254 	unsigned long flags;
255 	char ep_text[40];
256 
257 	spin_lock_irqsave(&imx21->lock, flags);
258 
259 	list_for_each_entry(dmem, &imx21->dmem_list, list)
260 		seq_printf(s,
261 			"%04X: size=0x%X "
262 			"ep=%s\n",
263 			dmem->offset, dmem->size,
264 			format_ep(dmem->ep, ep_text, sizeof(ep_text)));
265 
266 	spin_unlock_irqrestore(&imx21->lock, flags);
267 
268 	return 0;
269 }
270 DEFINE_SHOW_ATTRIBUTE(debug_dmem);
271 
debug_etd_show(struct seq_file * s,void * v)272 static int debug_etd_show(struct seq_file *s, void *v)
273 {
274 	struct imx21 *imx21 = s->private;
275 	struct etd_priv *etd;
276 	char buf[60];
277 	u32 dword;
278 	int i, j;
279 	unsigned long flags;
280 
281 	spin_lock_irqsave(&imx21->lock, flags);
282 
283 	for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
284 		int state = -1;
285 		struct urb_priv *urb_priv;
286 		if (etd->urb) {
287 			urb_priv = etd->urb->hcpriv;
288 			if (urb_priv)
289 				state = urb_priv->state;
290 		}
291 
292 		seq_printf(s,
293 			"etd_num: %d\n"
294 			"ep: %s\n"
295 			"alloc: %d\n"
296 			"len: %d\n"
297 			"busy sw: %d\n"
298 			"busy hw: %d\n"
299 			"urb state: %d\n"
300 			"current urb: %p\n",
301 
302 			i,
303 			format_ep(etd->ep, buf, sizeof(buf)),
304 			etd->alloc,
305 			etd->len,
306 			etd->urb != NULL,
307 			(readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0,
308 			state,
309 			etd->urb);
310 
311 		for (j = 0; j < 4; j++) {
312 			dword = etd_readl(imx21, i, j);
313 			switch (j) {
314 			case 0:
315 				format_etd_dword0(dword, buf, sizeof(buf));
316 				break;
317 			case 2:
318 				snprintf(buf, sizeof(buf),
319 					"cc=0X%02X", dword >> DW2_COMPCODE);
320 				break;
321 			default:
322 				*buf = 0;
323 				break;
324 			}
325 			seq_printf(s,
326 				"dword %d: submitted=%08X cur=%08X [%s]\n",
327 				j,
328 				etd->submitted_dwords[j],
329 				dword,
330 				buf);
331 		}
332 		seq_printf(s, "\n");
333 	}
334 
335 	spin_unlock_irqrestore(&imx21->lock, flags);
336 
337 	return 0;
338 }
339 DEFINE_SHOW_ATTRIBUTE(debug_etd);
340 
debug_statistics_show_one(struct seq_file * s,const char * name,struct debug_stats * stats)341 static void debug_statistics_show_one(struct seq_file *s,
342 	const char *name, struct debug_stats *stats)
343 {
344 	seq_printf(s, "%s:\n"
345 		"submitted URBs: %lu\n"
346 		"completed OK: %lu\n"
347 		"completed failed: %lu\n"
348 		"unlinked: %lu\n"
349 		"queued for ETD: %lu\n"
350 		"queued for DMEM: %lu\n\n",
351 		name,
352 		stats->submitted,
353 		stats->completed_ok,
354 		stats->completed_failed,
355 		stats->unlinked,
356 		stats->queue_etd,
357 		stats->queue_dmem);
358 }
359 
debug_statistics_show(struct seq_file * s,void * v)360 static int debug_statistics_show(struct seq_file *s, void *v)
361 {
362 	struct imx21 *imx21 = s->private;
363 	unsigned long flags;
364 
365 	spin_lock_irqsave(&imx21->lock, flags);
366 
367 	debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats);
368 	debug_statistics_show_one(s, "isoc", &imx21->isoc_stats);
369 	seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks);
370 	spin_unlock_irqrestore(&imx21->lock, flags);
371 
372 	return 0;
373 }
374 DEFINE_SHOW_ATTRIBUTE(debug_statistics);
375 
debug_isoc_show_one(struct seq_file * s,const char * name,int index,struct debug_isoc_trace * trace)376 static void debug_isoc_show_one(struct seq_file *s,
377 	const char *name, int index, 	struct debug_isoc_trace *trace)
378 {
379 	seq_printf(s, "%s %d:\n"
380 		"cc=0X%02X\n"
381 		"scheduled frame %d (%d)\n"
382 		"submitted frame %d (%d)\n"
383 		"completed frame %d (%d)\n"
384 		"requested length=%d\n"
385 		"completed length=%d\n\n",
386 		name, index,
387 		trace->cc,
388 		trace->schedule_frame, trace->schedule_frame & 0xFFFF,
389 		trace->submit_frame, trace->submit_frame & 0xFFFF,
390 		trace->done_frame, trace->done_frame & 0xFFFF,
391 		trace->request_len,
392 		trace->done_len);
393 }
394 
debug_isoc_show(struct seq_file * s,void * v)395 static int debug_isoc_show(struct seq_file *s, void *v)
396 {
397 	struct imx21 *imx21 = s->private;
398 	struct debug_isoc_trace *trace;
399 	unsigned long flags;
400 	int i;
401 
402 	spin_lock_irqsave(&imx21->lock, flags);
403 
404 	trace = imx21->isoc_trace_failed;
405 	for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++)
406 		debug_isoc_show_one(s, "isoc failed", i, trace);
407 
408 	trace = imx21->isoc_trace;
409 	for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++)
410 		debug_isoc_show_one(s, "isoc", i, trace);
411 
412 	spin_unlock_irqrestore(&imx21->lock, flags);
413 
414 	return 0;
415 }
416 DEFINE_SHOW_ATTRIBUTE(debug_isoc);
417 
create_debug_files(struct imx21 * imx21)418 static void create_debug_files(struct imx21 *imx21)
419 {
420 	struct dentry *root;
421 
422 	root = debugfs_create_dir(dev_name(imx21->dev), usb_debug_root);
423 	imx21->debug_root = root;
424 
425 	debugfs_create_file("status", S_IRUGO, root, imx21, &debug_status_fops);
426 	debugfs_create_file("dmem", S_IRUGO, root, imx21, &debug_dmem_fops);
427 	debugfs_create_file("etd", S_IRUGO, root, imx21, &debug_etd_fops);
428 	debugfs_create_file("statistics", S_IRUGO, root, imx21,
429 			    &debug_statistics_fops);
430 	debugfs_create_file("isoc", S_IRUGO, root, imx21, &debug_isoc_fops);
431 }
432 
remove_debug_files(struct imx21 * imx21)433 static void remove_debug_files(struct imx21 *imx21)
434 {
435 	debugfs_remove_recursive(imx21->debug_root);
436 }
437 
438 #endif
439 
440