linux/drivers/usb/host/imx21-dbg.c
<<
>>
Prefs
   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
  14static inline void create_debug_files(struct imx21 *imx21) { }
  15static inline void remove_debug_files(struct imx21 *imx21) { }
  16static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {}
  17static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb,
  18        int status) {}
  19static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {}
  20static inline void debug_urb_queued_for_etd(struct imx21 *imx21,
  21        struct urb *urb) {}
  22static inline void debug_urb_queued_for_dmem(struct imx21 *imx21,
  23        struct urb *urb) {}
  24static inline void debug_etd_allocated(struct imx21 *imx21) {}
  25static inline void debug_etd_freed(struct imx21 *imx21) {}
  26static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {}
  27static inline void debug_dmem_freed(struct imx21 *imx21, int size) {}
  28static inline void debug_isoc_submitted(struct imx21 *imx21,
  29        int frame, struct td *td) {}
  30static 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
  38static const char *dir_labels[] = {
  39        "TD 0",
  40        "OUT",
  41        "IN",
  42        "TD 1"
  43};
  44
  45static const char *speed_labels[] = {
  46        "Full",
  47        "Low"
  48};
  49
  50static const char *format_labels[] = {
  51        "Control",
  52        "ISO",
  53        "Bulk",
  54        "Interrupt"
  55};
  56
  57static 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
  64static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb)
  65{
  66        stats_for_urb(imx21, urb)->submitted++;
  67}
  68
  69static 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
  77static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb)
  78{
  79        stats_for_urb(imx21, urb)->unlinked++;
  80}
  81
  82static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb)
  83{
  84        stats_for_urb(imx21, urb)->queue_etd++;
  85}
  86
  87static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb)
  88{
  89        stats_for_urb(imx21, urb)->queue_dmem++;
  90}
  91
  92static 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
  99static inline void debug_etd_freed(struct imx21 *imx21)
 100{
 101        imx21->etd_usage.value--;
 102}
 103
 104static 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
 112static inline void debug_dmem_freed(struct imx21 *imx21, int size)
 113{
 114        imx21->dmem_usage.value -= size;
 115}
 116
 117
 118static 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
 131static 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
 161static 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
 173static 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
 186static 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}
 248DEFINE_SHOW_ATTRIBUTE(debug_status);
 249
 250static 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}
 270DEFINE_SHOW_ATTRIBUTE(debug_dmem);
 271
 272static 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}
 339DEFINE_SHOW_ATTRIBUTE(debug_etd);
 340
 341static 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
 360static 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}
 374DEFINE_SHOW_ATTRIBUTE(debug_statistics);
 375
 376static 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
 395static 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}
 416DEFINE_SHOW_ATTRIBUTE(debug_isoc);
 417
 418static void create_debug_files(struct imx21 *imx21)
 419{
 420        struct dentry *root;
 421
 422        root = debugfs_create_dir(dev_name(imx21->dev), NULL);
 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
 433static void remove_debug_files(struct imx21 *imx21)
 434{
 435        debugfs_remove_recursive(imx21->debug_root);
 436}
 437
 438#endif
 439
 440