| 1 | |
| 2 | #include <linux/kernel.h>
|
| 3 | #include <linux/device.h>
|
| 4 | #include <linux/types.h>
|
| 5 | #include <linux/spinlock.h>
|
| 6 | #include <linux/debugfs.h>
|
| 7 | #include <linux/seq_file.h>
|
| 8 | #include <linux/uaccess.h>
|
| 9 | #include <linux/usb/ch9.h>
|
| 10 | #include <linux/usb/gadget.h>
|
| 11 | #include <linux/usb/phy.h>
|
| 12 | #include <linux/usb/otg.h>
|
| 13 | #include <linux/usb/otg-fsm.h>
|
| 14 | #include <linux/usb/chipidea.h>
|
| 15 |
|
| 16 | #include "ci.h"
|
| 17 | #include "udc.h"
|
| 18 | #include "bits.h"
|
| 19 | #include "otg.h"
|
| 20 |
|
| 21 | |
| 22 | * ci_device_show: prints information about device capabilities and status
|
| 23 |
|
| 24 | static int ci_device_show(struct seq_file *s, void *data)
|
| 25 | {
|
| 26 | struct ci_hdrc *ci = s->private;
|
| 27 | struct usb_gadget *gadget = &ci->gadget;
|
| 28 |
|
| 29 | seq_printf(s, "speed = %d\n", gadget->speed);
|
| 30 | seq_printf(s, "max_speed = %d\n", gadget->max_speed);
|
| 31 | seq_printf(s, "is_otg = %d\n", gadget->is_otg);
|
| 32 | seq_printf(s, "is_a_peripheral = %d\n", gadget->is_a_peripheral);
|
| 33 | seq_printf(s, "b_hnp_enable = %d\n", gadget->b_hnp_enable);
|
| 34 | seq_printf(s, "a_hnp_support = %d\n", gadget->a_hnp_support);
|
| 35 | seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support);
|
| 36 | seq_printf(s, "name = %s\n",
|
| 37 | (gadget->name ? gadget->name : ""));
|
| 38 |
|
| 39 | if (!ci->driver)
|
| 40 | return 0;
|
| 41 |
|
| 42 | seq_printf(s, "gadget function = %s\n",
|
| 43 | (ci->driver->function ? ci->driver->function : ""));
|
| 44 | seq_printf(s, "gadget max speed = %d\n", ci->driver->max_speed);
|
| 45 |
|
| 46 | return 0;
|
| 47 | }
|
| 48 | DEFINE_SHOW_ATTRIBUTE(ci_device);
|
| 49 |
|
| 50 | |
| 51 | * ci_port_test_show: reads port test mode
|
| 52 |
|
| 53 | static int ci_port_test_show(struct seq_file *s, void *data)
|
| 54 | {
|
| 55 | struct ci_hdrc *ci = s->private;
|
| 56 | unsigned long flags;
|
| 57 | unsigned mode;
|
| 58 |
|
| 59 | pm_runtime_get_sync(ci->dev);
|
| 60 | spin_lock_irqsave(&ci->lock, flags);
|
| 61 | mode = hw_port_test_get(ci);
|
| 62 | spin_unlock_irqrestore(&ci->lock, flags);
|
| 63 | pm_runtime_put_sync(ci->dev);
|
| 64 |
|
| 65 | seq_printf(s, "mode = %u\n", mode);
|
| 66 |
|
| 67 | return 0;
|
| 68 | }
|
| 69 |
|
| 70 | |
| 71 | * ci_port_test_write: writes port test mode
|
| 72 |
|
| 73 | static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
|
| 74 | size_t count, loff_t *ppos)
|
| 75 | {
|
| 76 | struct seq_file *s = file->private_data;
|
| 77 | struct ci_hdrc *ci = s->private;
|
| 78 | unsigned long flags;
|
| 79 | unsigned mode;
|
| 80 | char buf[32];
|
| 81 | int ret;
|
| 82 |
|
| 83 | count = min_t(size_t, sizeof(buf) - 1, count);
|
| 84 | if (copy_from_user(buf, ubuf, count))
|
| 85 | return -EFAULT;
|
| 86 |
|
| 87 |
|
| 88 | buf[count] = '\0';
|
| 89 |
|
| 90 | if (sscanf(buf, "%u", &mode) != 1)
|
| 91 | return -EINVAL;
|
| 92 |
|
| 93 | if (mode > 255)
|
| 94 | return -EBADRQC;
|
| 95 |
|
| 96 | pm_runtime_get_sync(ci->dev);
|
| 97 | spin_lock_irqsave(&ci->lock, flags);
|
| 98 | ret = hw_port_test_set(ci, mode);
|
| 99 | spin_unlock_irqrestore(&ci->lock, flags);
|
| 100 | pm_runtime_put_sync(ci->dev);
|
| 101 |
|
| 102 | return ret ? ret : count;
|
| 103 | }
|
| 104 |
|
| 105 | static int ci_port_test_open(struct inode *inode, struct file *file)
|
| 106 | {
|
| 107 | return single_open(file, ci_port_test_show, inode->i_private);
|
| 108 | }
|
| 109 |
|
| 110 | static const struct file_operations ci_port_test_fops = {
|
| 111 | .open = ci_port_test_open,
|
| 112 | .write = ci_port_test_write,
|
| 113 | .read = seq_read,
|
| 114 | .llseek = seq_lseek,
|
| 115 | .release = single_release,
|
| 116 | };
|
| 117 |
|
| 118 | |
| 119 | * ci_qheads_show: DMA contents of all queue heads
|
| 120 |
|
| 121 | static int ci_qheads_show(struct seq_file *s, void *data)
|
| 122 | {
|
| 123 | struct ci_hdrc *ci = s->private;
|
| 124 | unsigned long flags;
|
| 125 | unsigned i, j;
|
| 126 |
|
| 127 | if (ci->role != CI_ROLE_GADGET) {
|
| 128 | seq_printf(s, "not in gadget mode\n");
|
| 129 | return 0;
|
| 130 | }
|
| 131 |
|
| 132 | spin_lock_irqsave(&ci->lock, flags);
|
| 133 | for (i = 0; i < ci->hw_ep_max/2; i++) {
|
| 134 | struct ci_hw_ep *hweprx = &ci->ci_hw_ep[i];
|
| 135 | struct ci_hw_ep *hweptx =
|
| 136 | &ci->ci_hw_ep[i + ci->hw_ep_max/2];
|
| 137 | seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n",
|
| 138 | i, (u32)hweprx->qh.dma, (u32)hweptx->qh.dma);
|
| 139 | for (j = 0; j < (sizeof(struct ci_hw_qh)/sizeof(u32)); j++)
|
| 140 | seq_printf(s, " %04X: %08X %08X\n", j,
|
| 141 | *((u32 *)hweprx->qh.ptr + j),
|
| 142 | *((u32 *)hweptx->qh.ptr + j));
|
| 143 | }
|
| 144 | spin_unlock_irqrestore(&ci->lock, flags);
|
| 145 |
|
| 146 | return 0;
|
| 147 | }
|
| 148 | DEFINE_SHOW_ATTRIBUTE(ci_qheads);
|
| 149 |
|
| 150 | |
| 151 | * ci_requests_show: DMA contents of all requests currently queued (all endpts)
|
| 152 |
|
| 153 | static int ci_requests_show(struct seq_file *s, void *data)
|
| 154 | {
|
| 155 | struct ci_hdrc *ci = s->private;
|
| 156 | unsigned long flags;
|
| 157 | struct ci_hw_req *req = NULL;
|
| 158 | struct td_node *node, *tmpnode;
|
| 159 | unsigned i, j, qsize = sizeof(struct ci_hw_td)/sizeof(u32);
|
| 160 |
|
| 161 | if (ci->role != CI_ROLE_GADGET) {
|
| 162 | seq_printf(s, "not in gadget mode\n");
|
| 163 | return 0;
|
| 164 | }
|
| 165 |
|
| 166 | spin_lock_irqsave(&ci->lock, flags);
|
| 167 | for (i = 0; i < ci->hw_ep_max; i++)
|
| 168 | list_for_each_entry(req, &ci->ci_hw_ep[i].qh.queue, queue) {
|
| 169 | list_for_each_entry_safe(node, tmpnode, &req->tds, td) {
|
| 170 | seq_printf(s, "EP=%02i: TD=%08X %s\n",
|
| 171 | i % (ci->hw_ep_max / 2),
|
| 172 | (u32)node->dma,
|
| 173 | ((i < ci->hw_ep_max/2) ?
|
| 174 | "RX" : "TX"));
|
| 175 |
|
| 176 | for (j = 0; j < qsize; j++)
|
| 177 | seq_printf(s, " %04X: %08X\n", j,
|
| 178 | *((u32 *)node->ptr + j));
|
| 179 | }
|
| 180 | }
|
| 181 | spin_unlock_irqrestore(&ci->lock, flags);
|
| 182 |
|
| 183 | return 0;
|
| 184 | }
|
| 185 | DEFINE_SHOW_ATTRIBUTE(ci_requests);
|
| 186 |
|
| 187 | static int ci_otg_show(struct seq_file *s, void *unused)
|
| 188 | {
|
| 189 | struct ci_hdrc *ci = s->private;
|
| 190 | struct otg_fsm *fsm;
|
| 191 |
|
| 192 | if (!ci || !ci_otg_is_fsm_mode(ci))
|
| 193 | return 0;
|
| 194 |
|
| 195 | fsm = &ci->fsm;
|
| 196 |
|
| 197 |
|
| 198 | seq_printf(s, "OTG state: %s\n\n",
|
| 199 | usb_otg_state_string(ci->otg.state));
|
| 200 |
|
| 201 |
|
| 202 | seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
|
| 203 |
|
| 204 | seq_printf(s, "a_bus_req: %d\n", fsm->a_bus_req);
|
| 205 |
|
| 206 | seq_printf(s, "a_srp_det: %d\n", fsm->a_srp_det);
|
| 207 |
|
| 208 | seq_printf(s, "a_vbus_vld: %d\n", fsm->a_vbus_vld);
|
| 209 |
|
| 210 | seq_printf(s, "b_conn: %d\n", fsm->b_conn);
|
| 211 |
|
| 212 | seq_printf(s, "adp_change: %d\n", fsm->adp_change);
|
| 213 |
|
| 214 | seq_printf(s, "power_up: %d\n", fsm->power_up);
|
| 215 |
|
| 216 | seq_printf(s, "a_bus_resume: %d\n", fsm->a_bus_resume);
|
| 217 |
|
| 218 | seq_printf(s, "a_bus_suspend: %d\n", fsm->a_bus_suspend);
|
| 219 |
|
| 220 | seq_printf(s, "a_conn: %d\n", fsm->a_conn);
|
| 221 |
|
| 222 | seq_printf(s, "b_bus_req: %d\n", fsm->b_bus_req);
|
| 223 |
|
| 224 | seq_printf(s, "b_bus_suspend: %d\n", fsm->b_bus_suspend);
|
| 225 |
|
| 226 | seq_printf(s, "b_se0_srp: %d\n", fsm->b_se0_srp);
|
| 227 |
|
| 228 | seq_printf(s, "b_ssend_srp: %d\n", fsm->b_ssend_srp);
|
| 229 |
|
| 230 | seq_printf(s, "b_sess_vld: %d\n", fsm->b_sess_vld);
|
| 231 |
|
| 232 | seq_printf(s, "b_srp_done: %d\n", fsm->b_srp_done);
|
| 233 |
|
| 234 | seq_printf(s, "drv_vbus: %d\n", fsm->drv_vbus);
|
| 235 |
|
| 236 | seq_printf(s, "loc_conn: %d\n", fsm->loc_conn);
|
| 237 |
|
| 238 | seq_printf(s, "loc_sof: %d\n", fsm->loc_sof);
|
| 239 |
|
| 240 | seq_printf(s, "adp_prb: %d\n", fsm->adp_prb);
|
| 241 |
|
| 242 | seq_printf(s, "id: %d\n", fsm->id);
|
| 243 |
|
| 244 | seq_printf(s, "protocol: %d\n", fsm->protocol);
|
| 245 |
|
| 246 | return 0;
|
| 247 | }
|
| 248 | DEFINE_SHOW_ATTRIBUTE(ci_otg);
|
| 249 |
|
| 250 | static int ci_registers_show(struct seq_file *s, void *unused)
|
| 251 | {
|
| 252 | struct ci_hdrc *ci = s->private;
|
| 253 | u32 tmp_reg;
|
| 254 |
|
| 255 | if (!ci || ci->in_lpm)
|
| 256 | return -EPERM;
|
| 257 |
|
| 258 |
|
| 259 | tmp_reg = hw_read_intr_enable(ci);
|
| 260 | seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
|
| 261 |
|
| 262 | tmp_reg = hw_read_intr_status(ci);
|
| 263 | seq_printf(s, "USBSTS reg: %08x\n", tmp_reg);
|
| 264 |
|
| 265 | tmp_reg = hw_read(ci, OP_USBMODE, ~0);
|
| 266 | seq_printf(s, "USBMODE reg: %08x\n", tmp_reg);
|
| 267 |
|
| 268 | tmp_reg = hw_read(ci, OP_USBCMD, ~0);
|
| 269 | seq_printf(s, "USBCMD reg: %08x\n", tmp_reg);
|
| 270 |
|
| 271 | tmp_reg = hw_read(ci, OP_PORTSC, ~0);
|
| 272 | seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
|
| 273 |
|
| 274 | if (ci->is_otg) {
|
| 275 | tmp_reg = hw_read_otgsc(ci, ~0);
|
| 276 | seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
|
| 277 | }
|
| 278 |
|
| 279 | return 0;
|
| 280 | }
|
| 281 | DEFINE_SHOW_ATTRIBUTE(ci_registers);
|
| 282 |
|
| 283 | |
| 284 | * dbg_create_files: initializes the attribute interface
|
| 285 | * @ci: device
|
| 286 | *
|
| 287 | * This function returns an error code
|
| 288 |
|
| 289 | void dbg_create_files(struct ci_hdrc *ci)
|
| 290 | {
|
| 291 | struct dentry *dir;
|
| 292 |
|
| 293 | dir = debugfs_create_dir(dev_name(ci->dev), usb_debug_root);
|
| 294 |
|
| 295 | debugfs_create_file("device", S_IRUGO, dir, ci, &ci_device_fops);
|
| 296 | debugfs_create_file("port_test", S_IRUGO | S_IWUSR, dir, ci, &ci_port_test_fops);
|
| 297 | debugfs_create_file("qheads", S_IRUGO, dir, ci, &ci_qheads_fops);
|
| 298 | debugfs_create_file("requests", S_IRUGO, dir, ci, &ci_requests_fops);
|
| 299 |
|
| 300 | if (ci_otg_is_fsm_mode(ci))
|
| 301 | debugfs_create_file("otg", S_IRUGO, dir, ci, &ci_otg_fops);
|
| 302 |
|
| 303 | debugfs_create_file("registers", S_IRUGO, dir, ci, &ci_registers_fops);
|
| 304 | }
|
| 305 |
|
| 306 | |
| 307 | * dbg_remove_files: destroys the attribute interface
|
| 308 | * @ci: device
|
| 309 |
|
| 310 | void dbg_remove_files(struct ci_hdrc *ci)
|
| 311 | {
|
| 312 | debugfs_lookup_and_remove(dev_name(ci->dev), usb_debug_root);
|
| 313 | }
|