linux/drivers/usb/chipidea/debug.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   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 */
  24static 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}
  48DEFINE_SHOW_ATTRIBUTE(ci_device);
  49
  50/**
  51 * ci_port_test_show: reads port test mode
  52 */
  53static 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 */
  73static 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        /* sscanf requires a zero terminated string */
  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
 105static 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
 110static 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 */
 121static 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}
 148DEFINE_SHOW_ATTRIBUTE(ci_qheads);
 149
 150/**
 151 * ci_requests_show: DMA contents of all requests currently queued (all endpts)
 152 */
 153static 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}
 185DEFINE_SHOW_ATTRIBUTE(ci_requests);
 186
 187static 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        /* ------ State ----- */
 198        seq_printf(s, "OTG state: %s\n\n",
 199                        usb_otg_state_string(ci->otg.state));
 200
 201        /* ------ State Machine Variables ----- */
 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}
 248DEFINE_SHOW_ATTRIBUTE(ci_otg);
 249
 250static int ci_role_show(struct seq_file *s, void *data)
 251{
 252        struct ci_hdrc *ci = s->private;
 253
 254        if (ci->role != CI_ROLE_END)
 255                seq_printf(s, "%s\n", ci_role(ci)->name);
 256
 257        return 0;
 258}
 259
 260static ssize_t ci_role_write(struct file *file, const char __user *ubuf,
 261                             size_t count, loff_t *ppos)
 262{
 263        struct seq_file *s = file->private_data;
 264        struct ci_hdrc *ci = s->private;
 265        enum ci_role role;
 266        char buf[8];
 267        int ret;
 268
 269        if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 270                return -EFAULT;
 271
 272        for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
 273                if (ci->roles[role] &&
 274                    !strncmp(buf, ci->roles[role]->name,
 275                             strlen(ci->roles[role]->name)))
 276                        break;
 277
 278        if (role == CI_ROLE_END || role == ci->role)
 279                return -EINVAL;
 280
 281        pm_runtime_get_sync(ci->dev);
 282        disable_irq(ci->irq);
 283        ci_role_stop(ci);
 284        ret = ci_role_start(ci, role);
 285        enable_irq(ci->irq);
 286        pm_runtime_put_sync(ci->dev);
 287
 288        return ret ? ret : count;
 289}
 290
 291static int ci_role_open(struct inode *inode, struct file *file)
 292{
 293        return single_open(file, ci_role_show, inode->i_private);
 294}
 295
 296static const struct file_operations ci_role_fops = {
 297        .open           = ci_role_open,
 298        .write          = ci_role_write,
 299        .read           = seq_read,
 300        .llseek         = seq_lseek,
 301        .release        = single_release,
 302};
 303
 304static int ci_registers_show(struct seq_file *s, void *unused)
 305{
 306        struct ci_hdrc *ci = s->private;
 307        u32 tmp_reg;
 308
 309        if (!ci || ci->in_lpm)
 310                return -EPERM;
 311
 312        /* ------ Registers ----- */
 313        tmp_reg = hw_read_intr_enable(ci);
 314        seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
 315
 316        tmp_reg = hw_read_intr_status(ci);
 317        seq_printf(s, "USBSTS reg: %08x\n", tmp_reg);
 318
 319        tmp_reg = hw_read(ci, OP_USBMODE, ~0);
 320        seq_printf(s, "USBMODE reg: %08x\n", tmp_reg);
 321
 322        tmp_reg = hw_read(ci, OP_USBCMD, ~0);
 323        seq_printf(s, "USBCMD reg: %08x\n", tmp_reg);
 324
 325        tmp_reg = hw_read(ci, OP_PORTSC, ~0);
 326        seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
 327
 328        if (ci->is_otg) {
 329                tmp_reg = hw_read_otgsc(ci, ~0);
 330                seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
 331        }
 332
 333        return 0;
 334}
 335DEFINE_SHOW_ATTRIBUTE(ci_registers);
 336
 337/**
 338 * dbg_create_files: initializes the attribute interface
 339 * @ci: device
 340 *
 341 * This function returns an error code
 342 */
 343void dbg_create_files(struct ci_hdrc *ci)
 344{
 345        ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
 346
 347        debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
 348                            &ci_device_fops);
 349        debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs, ci,
 350                            &ci_port_test_fops);
 351        debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
 352                            &ci_qheads_fops);
 353        debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
 354                            &ci_requests_fops);
 355
 356        if (ci_otg_is_fsm_mode(ci)) {
 357                debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci,
 358                                    &ci_otg_fops);
 359        }
 360
 361        debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
 362                            &ci_role_fops);
 363        debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
 364                            &ci_registers_fops);
 365}
 366
 367/**
 368 * dbg_remove_files: destroys the attribute interface
 369 * @ci: device
 370 */
 371void dbg_remove_files(struct ci_hdrc *ci)
 372{
 373        debugfs_remove_recursive(ci->debugfs);
 374}
 375