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