xref: /openbmc/linux/drivers/usb/chipidea/debug.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2e443b333SAlexander Shishkin #include <linux/kernel.h>
33c37bb68SAlexander Shishkin #include <linux/device.h>
43c37bb68SAlexander Shishkin #include <linux/types.h>
53c37bb68SAlexander Shishkin #include <linux/spinlock.h>
62d651289SAlexander Shishkin #include <linux/debugfs.h>
72d651289SAlexander Shishkin #include <linux/seq_file.h>
82d651289SAlexander Shishkin #include <linux/uaccess.h>
9e443b333SAlexander Shishkin #include <linux/usb/ch9.h>
10e443b333SAlexander Shishkin #include <linux/usb/gadget.h>
1188bdffc8SLi Jun #include <linux/usb/phy.h>
1288bdffc8SLi Jun #include <linux/usb/otg.h>
1388bdffc8SLi Jun #include <linux/usb/otg-fsm.h>
14b0930d4cSLi Jun #include <linux/usb/chipidea.h>
15e443b333SAlexander Shishkin 
16e443b333SAlexander Shishkin #include "ci.h"
17e443b333SAlexander Shishkin #include "udc.h"
18e443b333SAlexander Shishkin #include "bits.h"
19c4a8b639SLi Jun #include "otg.h"
20e443b333SAlexander Shishkin 
215722a8efSLee Jones /*
222d651289SAlexander Shishkin  * ci_device_show: prints information about device capabilities and status
23e443b333SAlexander Shishkin  */
ci_device_show(struct seq_file * s,void * data)242d651289SAlexander Shishkin static int ci_device_show(struct seq_file *s, void *data)
25e443b333SAlexander Shishkin {
268e22978cSAlexander Shishkin 	struct ci_hdrc *ci = s->private;
2726c696c6SRichard Zhao 	struct usb_gadget *gadget = &ci->gadget;
28e443b333SAlexander Shishkin 
292d651289SAlexander Shishkin 	seq_printf(s, "speed             = %d\n", gadget->speed);
302d651289SAlexander Shishkin 	seq_printf(s, "max_speed         = %d\n", gadget->max_speed);
312d651289SAlexander Shishkin 	seq_printf(s, "is_otg            = %d\n", gadget->is_otg);
322d651289SAlexander Shishkin 	seq_printf(s, "is_a_peripheral   = %d\n", gadget->is_a_peripheral);
332d651289SAlexander Shishkin 	seq_printf(s, "b_hnp_enable      = %d\n", gadget->b_hnp_enable);
342d651289SAlexander Shishkin 	seq_printf(s, "a_hnp_support     = %d\n", gadget->a_hnp_support);
352d651289SAlexander Shishkin 	seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support);
362d651289SAlexander Shishkin 	seq_printf(s, "name              = %s\n",
37e443b333SAlexander Shishkin 		   (gadget->name ? gadget->name : ""));
38e443b333SAlexander Shishkin 
392d651289SAlexander Shishkin 	if (!ci->driver)
402d651289SAlexander Shishkin 		return 0;
41e443b333SAlexander Shishkin 
422d651289SAlexander Shishkin 	seq_printf(s, "gadget function   = %s\n",
432d651289SAlexander Shishkin 		       (ci->driver->function ? ci->driver->function : ""));
442d651289SAlexander Shishkin 	seq_printf(s, "gadget max speed  = %d\n", ci->driver->max_speed);
45e443b333SAlexander Shishkin 
46e443b333SAlexander Shishkin 	return 0;
47e443b333SAlexander Shishkin }
48e8c56f27SAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(ci_device);
49e443b333SAlexander Shishkin 
505722a8efSLee Jones /*
512d651289SAlexander Shishkin  * ci_port_test_show: reads port test mode
52e443b333SAlexander Shishkin  */
ci_port_test_show(struct seq_file * s,void * data)532d651289SAlexander Shishkin static int ci_port_test_show(struct seq_file *s, void *data)
54e443b333SAlexander Shishkin {
558e22978cSAlexander Shishkin 	struct ci_hdrc *ci = s->private;
56e443b333SAlexander Shishkin 	unsigned long flags;
57e443b333SAlexander Shishkin 	unsigned mode;
58e443b333SAlexander Shishkin 
59bc249379SLi Jun 	pm_runtime_get_sync(ci->dev);
6026c696c6SRichard Zhao 	spin_lock_irqsave(&ci->lock, flags);
6126c696c6SRichard Zhao 	mode = hw_port_test_get(ci);
6226c696c6SRichard Zhao 	spin_unlock_irqrestore(&ci->lock, flags);
63bc249379SLi Jun 	pm_runtime_put_sync(ci->dev);
64e443b333SAlexander Shishkin 
652d651289SAlexander Shishkin 	seq_printf(s, "mode = %u\n", mode);
662d651289SAlexander Shishkin 
672d651289SAlexander Shishkin 	return 0;
68e443b333SAlexander Shishkin }
69e443b333SAlexander Shishkin 
705722a8efSLee Jones /*
712d651289SAlexander Shishkin  * ci_port_test_write: writes port test mode
72e443b333SAlexander Shishkin  */
ci_port_test_write(struct file * file,const char __user * ubuf,size_t count,loff_t * ppos)732d651289SAlexander Shishkin static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
742d651289SAlexander Shishkin 				  size_t count, loff_t *ppos)
75e443b333SAlexander Shishkin {
762d651289SAlexander Shishkin 	struct seq_file *s = file->private_data;
778e22978cSAlexander Shishkin 	struct ci_hdrc *ci = s->private;
78e443b333SAlexander Shishkin 	unsigned long flags;
79e443b333SAlexander Shishkin 	unsigned mode;
802d651289SAlexander Shishkin 	char buf[32];
812d651289SAlexander Shishkin 	int ret;
82e443b333SAlexander Shishkin 
83bd5fb0aeSHeinrich Schuchardt 	count = min_t(size_t, sizeof(buf) - 1, count);
84bd5fb0aeSHeinrich Schuchardt 	if (copy_from_user(buf, ubuf, count))
852d651289SAlexander Shishkin 		return -EFAULT;
86e443b333SAlexander Shishkin 
87bd5fb0aeSHeinrich Schuchardt 	/* sscanf requires a zero terminated string */
88bd5fb0aeSHeinrich Schuchardt 	buf[count] = '\0';
89bd5fb0aeSHeinrich Schuchardt 
902d651289SAlexander Shishkin 	if (sscanf(buf, "%u", &mode) != 1)
912d651289SAlexander Shishkin 		return -EINVAL;
92e443b333SAlexander Shishkin 
933c724888SAlan 	if (mode > 255)
943c724888SAlan 		return -EBADRQC;
953c724888SAlan 
96bc249379SLi Jun 	pm_runtime_get_sync(ci->dev);
9726c696c6SRichard Zhao 	spin_lock_irqsave(&ci->lock, flags);
982d651289SAlexander Shishkin 	ret = hw_port_test_set(ci, mode);
9926c696c6SRichard Zhao 	spin_unlock_irqrestore(&ci->lock, flags);
100bc249379SLi Jun 	pm_runtime_put_sync(ci->dev);
101e443b333SAlexander Shishkin 
1022d651289SAlexander Shishkin 	return ret ? ret : count;
103e443b333SAlexander Shishkin }
1042d651289SAlexander Shishkin 
ci_port_test_open(struct inode * inode,struct file * file)1052d651289SAlexander Shishkin static int ci_port_test_open(struct inode *inode, struct file *file)
1062d651289SAlexander Shishkin {
1072d651289SAlexander Shishkin 	return single_open(file, ci_port_test_show, inode->i_private);
1082d651289SAlexander Shishkin }
1092d651289SAlexander Shishkin 
1102d651289SAlexander Shishkin static const struct file_operations ci_port_test_fops = {
1112d651289SAlexander Shishkin 	.open		= ci_port_test_open,
1122d651289SAlexander Shishkin 	.write		= ci_port_test_write,
1132d651289SAlexander Shishkin 	.read		= seq_read,
1142d651289SAlexander Shishkin 	.llseek		= seq_lseek,
1152d651289SAlexander Shishkin 	.release	= single_release,
1162d651289SAlexander Shishkin };
117e443b333SAlexander Shishkin 
1185722a8efSLee Jones /*
1192d651289SAlexander Shishkin  * ci_qheads_show: DMA contents of all queue heads
120e443b333SAlexander Shishkin  */
ci_qheads_show(struct seq_file * s,void * data)1212d651289SAlexander Shishkin static int ci_qheads_show(struct seq_file *s, void *data)
122e443b333SAlexander Shishkin {
1238e22978cSAlexander Shishkin 	struct ci_hdrc *ci = s->private;
124e443b333SAlexander Shishkin 	unsigned long flags;
1252d651289SAlexander Shishkin 	unsigned i, j;
126e443b333SAlexander Shishkin 
1272d651289SAlexander Shishkin 	if (ci->role != CI_ROLE_GADGET) {
1282d651289SAlexander Shishkin 		seq_printf(s, "not in gadget mode\n");
129e443b333SAlexander Shishkin 		return 0;
130e443b333SAlexander Shishkin 	}
131e443b333SAlexander Shishkin 
13226c696c6SRichard Zhao 	spin_lock_irqsave(&ci->lock, flags);
13326c696c6SRichard Zhao 	for (i = 0; i < ci->hw_ep_max/2; i++) {
1348e22978cSAlexander Shishkin 		struct ci_hw_ep *hweprx = &ci->ci_hw_ep[i];
1358e22978cSAlexander Shishkin 		struct ci_hw_ep *hweptx =
1368e22978cSAlexander Shishkin 			&ci->ci_hw_ep[i + ci->hw_ep_max/2];
1372d651289SAlexander Shishkin 		seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n",
1382dbc5c4cSAlexander Shishkin 			   i, (u32)hweprx->qh.dma, (u32)hweptx->qh.dma);
1398e22978cSAlexander Shishkin 		for (j = 0; j < (sizeof(struct ci_hw_qh)/sizeof(u32)); j++)
1402d651289SAlexander Shishkin 			seq_printf(s, " %04X:    %08X    %08X\n", j,
1412dbc5c4cSAlexander Shishkin 				   *((u32 *)hweprx->qh.ptr + j),
1422dbc5c4cSAlexander Shishkin 				   *((u32 *)hweptx->qh.ptr + j));
143e443b333SAlexander Shishkin 	}
14426c696c6SRichard Zhao 	spin_unlock_irqrestore(&ci->lock, flags);
145e443b333SAlexander Shishkin 
146e443b333SAlexander Shishkin 	return 0;
147e443b333SAlexander Shishkin }
148e8c56f27SAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(ci_qheads);
149e443b333SAlexander Shishkin 
1505722a8efSLee Jones /*
1512d651289SAlexander Shishkin  * ci_requests_show: DMA contents of all requests currently queued (all endpts)
152e443b333SAlexander Shishkin  */
ci_requests_show(struct seq_file * s,void * data)1532d651289SAlexander Shishkin static int ci_requests_show(struct seq_file *s, void *data)
154e443b333SAlexander Shishkin {
1558e22978cSAlexander Shishkin 	struct ci_hdrc *ci = s->private;
156e443b333SAlexander Shishkin 	unsigned long flags;
1578e22978cSAlexander Shishkin 	struct ci_hw_req *req = NULL;
158cc9e6c49SMichael Grzeschik 	struct td_node *node, *tmpnode;
1598e22978cSAlexander Shishkin 	unsigned i, j, qsize = sizeof(struct ci_hw_td)/sizeof(u32);
160e443b333SAlexander Shishkin 
1612d651289SAlexander Shishkin 	if (ci->role != CI_ROLE_GADGET) {
1622d651289SAlexander Shishkin 		seq_printf(s, "not in gadget mode\n");
163e443b333SAlexander Shishkin 		return 0;
164e443b333SAlexander Shishkin 	}
165e443b333SAlexander Shishkin 
16626c696c6SRichard Zhao 	spin_lock_irqsave(&ci->lock, flags);
16726c696c6SRichard Zhao 	for (i = 0; i < ci->hw_ep_max; i++)
16832540ba2SGeliang Tang 		list_for_each_entry(req, &ci->ci_hw_ep[i].qh.queue, queue) {
169cc9e6c49SMichael Grzeschik 			list_for_each_entry_safe(node, tmpnode, &req->tds, td) {
1702d651289SAlexander Shishkin 				seq_printf(s, "EP=%02i: TD=%08X %s\n",
171cc9e6c49SMichael Grzeschik 					   i % (ci->hw_ep_max / 2),
172cc9e6c49SMichael Grzeschik 					   (u32)node->dma,
173cc9e6c49SMichael Grzeschik 					   ((i < ci->hw_ep_max/2) ?
174cc9e6c49SMichael Grzeschik 					   "RX" : "TX"));
175e443b333SAlexander Shishkin 
1762d651289SAlexander Shishkin 				for (j = 0; j < qsize; j++)
1772d651289SAlexander Shishkin 					seq_printf(s, " %04X:    %08X\n", j,
178cc9e6c49SMichael Grzeschik 						   *((u32 *)node->ptr + j));
179cc9e6c49SMichael Grzeschik 			}
180e443b333SAlexander Shishkin 		}
18126c696c6SRichard Zhao 	spin_unlock_irqrestore(&ci->lock, flags);
182e443b333SAlexander Shishkin 
1832d651289SAlexander Shishkin 	return 0;
184e443b333SAlexander Shishkin }
185e8c56f27SAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(ci_requests);
186e443b333SAlexander Shishkin 
ci_otg_show(struct seq_file * s,void * unused)187df40f8d3SWei Yongjun static int ci_otg_show(struct seq_file *s, void *unused)
18888bdffc8SLi Jun {
18988bdffc8SLi Jun 	struct ci_hdrc *ci = s->private;
19088bdffc8SLi Jun 	struct otg_fsm *fsm;
19188bdffc8SLi Jun 
19288bdffc8SLi Jun 	if (!ci || !ci_otg_is_fsm_mode(ci))
19388bdffc8SLi Jun 		return 0;
19488bdffc8SLi Jun 
19588bdffc8SLi Jun 	fsm = &ci->fsm;
19688bdffc8SLi Jun 
19788bdffc8SLi Jun 	/* ------ State ----- */
19888bdffc8SLi Jun 	seq_printf(s, "OTG state: %s\n\n",
199ef44cb42SAntoine Tenart 			usb_otg_state_string(ci->otg.state));
20088bdffc8SLi Jun 
20188bdffc8SLi Jun 	/* ------ State Machine Variables ----- */
20288bdffc8SLi Jun 	seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
20388bdffc8SLi Jun 
20488bdffc8SLi Jun 	seq_printf(s, "a_bus_req: %d\n", fsm->a_bus_req);
20588bdffc8SLi Jun 
20688bdffc8SLi Jun 	seq_printf(s, "a_srp_det: %d\n", fsm->a_srp_det);
20788bdffc8SLi Jun 
20888bdffc8SLi Jun 	seq_printf(s, "a_vbus_vld: %d\n", fsm->a_vbus_vld);
20988bdffc8SLi Jun 
21088bdffc8SLi Jun 	seq_printf(s, "b_conn: %d\n", fsm->b_conn);
21188bdffc8SLi Jun 
21288bdffc8SLi Jun 	seq_printf(s, "adp_change: %d\n", fsm->adp_change);
21388bdffc8SLi Jun 
21488bdffc8SLi Jun 	seq_printf(s, "power_up: %d\n", fsm->power_up);
21588bdffc8SLi Jun 
21688bdffc8SLi Jun 	seq_printf(s, "a_bus_resume: %d\n", fsm->a_bus_resume);
21788bdffc8SLi Jun 
21888bdffc8SLi Jun 	seq_printf(s, "a_bus_suspend: %d\n", fsm->a_bus_suspend);
21988bdffc8SLi Jun 
22088bdffc8SLi Jun 	seq_printf(s, "a_conn: %d\n", fsm->a_conn);
22188bdffc8SLi Jun 
22288bdffc8SLi Jun 	seq_printf(s, "b_bus_req: %d\n", fsm->b_bus_req);
22388bdffc8SLi Jun 
22488bdffc8SLi Jun 	seq_printf(s, "b_bus_suspend: %d\n", fsm->b_bus_suspend);
22588bdffc8SLi Jun 
22688bdffc8SLi Jun 	seq_printf(s, "b_se0_srp: %d\n", fsm->b_se0_srp);
22788bdffc8SLi Jun 
22888bdffc8SLi Jun 	seq_printf(s, "b_ssend_srp: %d\n", fsm->b_ssend_srp);
22988bdffc8SLi Jun 
23088bdffc8SLi Jun 	seq_printf(s, "b_sess_vld: %d\n", fsm->b_sess_vld);
23188bdffc8SLi Jun 
23288bdffc8SLi Jun 	seq_printf(s, "b_srp_done: %d\n", fsm->b_srp_done);
23388bdffc8SLi Jun 
23488bdffc8SLi Jun 	seq_printf(s, "drv_vbus: %d\n", fsm->drv_vbus);
23588bdffc8SLi Jun 
23688bdffc8SLi Jun 	seq_printf(s, "loc_conn: %d\n", fsm->loc_conn);
23788bdffc8SLi Jun 
23888bdffc8SLi Jun 	seq_printf(s, "loc_sof: %d\n", fsm->loc_sof);
23988bdffc8SLi Jun 
24088bdffc8SLi Jun 	seq_printf(s, "adp_prb: %d\n", fsm->adp_prb);
24188bdffc8SLi Jun 
24288bdffc8SLi Jun 	seq_printf(s, "id: %d\n", fsm->id);
24388bdffc8SLi Jun 
24488bdffc8SLi Jun 	seq_printf(s, "protocol: %d\n", fsm->protocol);
24588bdffc8SLi Jun 
24688bdffc8SLi Jun 	return 0;
24788bdffc8SLi Jun }
248e8c56f27SAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(ci_otg);
24988bdffc8SLi Jun 
ci_registers_show(struct seq_file * s,void * unused)250df40f8d3SWei Yongjun static int ci_registers_show(struct seq_file *s, void *unused)
251c4a8b639SLi Jun {
252c4a8b639SLi Jun 	struct ci_hdrc *ci = s->private;
253c4a8b639SLi Jun 	u32 tmp_reg;
254c4a8b639SLi Jun 
2550c4d6af4SLi Jun 	if (!ci || ci->in_lpm)
2560c4d6af4SLi Jun 		return -EPERM;
257c4a8b639SLi Jun 
258c4a8b639SLi Jun 	/* ------ Registers ----- */
259c4a8b639SLi Jun 	tmp_reg = hw_read_intr_enable(ci);
260c4a8b639SLi Jun 	seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
261c4a8b639SLi Jun 
262c4a8b639SLi Jun 	tmp_reg = hw_read_intr_status(ci);
263c4a8b639SLi Jun 	seq_printf(s, "USBSTS reg: %08x\n", tmp_reg);
264c4a8b639SLi Jun 
265c4a8b639SLi Jun 	tmp_reg = hw_read(ci, OP_USBMODE, ~0);
266c4a8b639SLi Jun 	seq_printf(s, "USBMODE reg: %08x\n", tmp_reg);
267c4a8b639SLi Jun 
268c4a8b639SLi Jun 	tmp_reg = hw_read(ci, OP_USBCMD, ~0);
269c4a8b639SLi Jun 	seq_printf(s, "USBCMD reg: %08x\n", tmp_reg);
270c4a8b639SLi Jun 
271c4a8b639SLi Jun 	tmp_reg = hw_read(ci, OP_PORTSC, ~0);
272c4a8b639SLi Jun 	seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
273c4a8b639SLi Jun 
274c4a8b639SLi Jun 	if (ci->is_otg) {
275c4a8b639SLi Jun 		tmp_reg = hw_read_otgsc(ci, ~0);
276c4a8b639SLi Jun 		seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
277c4a8b639SLi Jun 	}
278c4a8b639SLi Jun 
279c4a8b639SLi Jun 	return 0;
280c4a8b639SLi Jun }
281e8c56f27SAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(ci_registers);
282c4a8b639SLi Jun 
283e443b333SAlexander Shishkin /**
284e443b333SAlexander Shishkin  * dbg_create_files: initializes the attribute interface
2852d651289SAlexander Shishkin  * @ci: device
286e443b333SAlexander Shishkin  *
287e443b333SAlexander Shishkin  * This function returns an error code
288e443b333SAlexander Shishkin  */
dbg_create_files(struct ci_hdrc * ci)289a61b75d1SGreg Kroah-Hartman void dbg_create_files(struct ci_hdrc *ci)
290e443b333SAlexander Shishkin {
2918f6c7c5aSGreg Kroah-Hartman 	struct dentry *dir;
2922d651289SAlexander Shishkin 
2938f6c7c5aSGreg Kroah-Hartman 	dir = debugfs_create_dir(dev_name(ci->dev), usb_debug_root);
294c8e333a3SAlexander Shishkin 
2958f6c7c5aSGreg Kroah-Hartman 	debugfs_create_file("device", S_IRUGO, dir, ci, &ci_device_fops);
2968f6c7c5aSGreg Kroah-Hartman 	debugfs_create_file("port_test", S_IRUGO | S_IWUSR, dir, ci, &ci_port_test_fops);
2978f6c7c5aSGreg Kroah-Hartman 	debugfs_create_file("qheads", S_IRUGO, dir, ci, &ci_qheads_fops);
2988f6c7c5aSGreg Kroah-Hartman 	debugfs_create_file("requests", S_IRUGO, dir, ci, &ci_requests_fops);
29988bdffc8SLi Jun 
3008f6c7c5aSGreg Kroah-Hartman 	if (ci_otg_is_fsm_mode(ci))
3018f6c7c5aSGreg Kroah-Hartman 		debugfs_create_file("otg", S_IRUGO, dir, ci, &ci_otg_fops);
3028f6c7c5aSGreg Kroah-Hartman 
3038f6c7c5aSGreg Kroah-Hartman 	debugfs_create_file("registers", S_IRUGO, dir, ci, &ci_registers_fops);
304e443b333SAlexander Shishkin }
305e443b333SAlexander Shishkin 
306e443b333SAlexander Shishkin /**
307e443b333SAlexander Shishkin  * dbg_remove_files: destroys the attribute interface
3082d651289SAlexander Shishkin  * @ci: device
309e443b333SAlexander Shishkin  */
dbg_remove_files(struct ci_hdrc * ci)3108e22978cSAlexander Shishkin void dbg_remove_files(struct ci_hdrc *ci)
311e443b333SAlexander Shishkin {
312*ff35f3eaSGreg Kroah-Hartman 	debugfs_lookup_and_remove(dev_name(ci->dev), usb_debug_root);
313e443b333SAlexander Shishkin }
314