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}
 248
 249static int debug_dmem_show(struct seq_file *s, void *v)
 250{
 251        struct imx21 *imx21 = s->private;
 252        struct imx21_dmem_area *dmem;
 253        unsigned long flags;
 254        char ep_text[40];
 255
 256        spin_lock_irqsave(&imx21->lock, flags);
 257
 258        list_for_each_entry(dmem, &imx21->dmem_list, list)
 259                seq_printf(s,
 260                        "%04X: size=0x%X "
 261                        "ep=%s\n",
 262                        dmem->offset, dmem->size,
 263                        format_ep(dmem->ep, ep_text, sizeof(ep_text)));
 264
 265        spin_unlock_irqrestore(&imx21->lock, flags);
 266
 267        return 0;
 268}
 269
 270static int debug_etd_show(struct seq_file *s, void *v)
 271{
 272        struct imx21 *imx21 = s->private;
 273        struct etd_priv *etd;
 274        char buf[60];
 275        u32 dword;
 276        int i, j;
 277        unsigned long flags;
 278
 279        spin_lock_irqsave(&imx21->lock, flags);
 280
 281        for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
 282                int state = -1;
 283                struct urb_priv *urb_priv;
 284                if (etd->urb) {
 285                        urb_priv = etd->urb->hcpriv;
 286                        if (urb_priv)
 287                                state = urb_priv->state;
 288                }
 289
 290                seq_printf(s,
 291                        "etd_num: %d\n"
 292                        "ep: %s\n"
 293                        "alloc: %d\n"
 294                        "len: %d\n"
 295                        "busy sw: %d\n"
 296                        "busy hw: %d\n"
 297                        "urb state: %d\n"
 298                        "current urb: %p\n",
 299
 300                        i,
 301                        format_ep(etd->ep, buf, sizeof(buf)),
 302                        etd->alloc,
 303                        etd->len,
 304                        etd->urb != NULL,
 305                        (readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0,
 306                        state,
 307                        etd->urb);
 308
 309                for (j = 0; j < 4; j++) {
 310                        dword = etd_readl(imx21, i, j);
 311                        switch (j) {
 312                        case 0:
 313                                format_etd_dword0(dword, buf, sizeof(buf));
 314                                break;
 315                        case 2:
 316                                snprintf(buf, sizeof(buf),
 317                                        "cc=0X%02X", dword >> DW2_COMPCODE);
 318                                break;
 319                        default:
 320                                *buf = 0;
 321                                break;
 322                        }
 323                        seq_printf(s,
 324                                "dword %d: submitted=%08X cur=%08X [%s]\n",
 325                                j,
 326                                etd->submitted_dwords[j],
 327                                dword,
 328                                buf);
 329                }
 330                seq_printf(s, "\n");
 331        }
 332
 333        spin_unlock_irqrestore(&imx21->lock, flags);
 334
 335        return 0;
 336}
 337
 338static void debug_statistics_show_one(struct seq_file *s,
 339        const char *name, struct debug_stats *stats)
 340{
 341        seq_printf(s, "%s:\n"
 342                "submitted URBs: %lu\n"
 343                "completed OK: %lu\n"
 344                "completed failed: %lu\n"
 345                "unlinked: %lu\n"
 346                "queued for ETD: %lu\n"
 347                "queued for DMEM: %lu\n\n",
 348                name,
 349                stats->submitted,
 350                stats->completed_ok,
 351                stats->completed_failed,
 352                stats->unlinked,
 353                stats->queue_etd,
 354                stats->queue_dmem);
 355}
 356
 357static int debug_statistics_show(struct seq_file *s, void *v)
 358{
 359        struct imx21 *imx21 = s->private;
 360        unsigned long flags;
 361
 362        spin_lock_irqsave(&imx21->lock, flags);
 363
 364        debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats);
 365        debug_statistics_show_one(s, "isoc", &imx21->isoc_stats);
 366        seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks);
 367        spin_unlock_irqrestore(&imx21->lock, flags);
 368
 369        return 0;
 370}
 371
 372static void debug_isoc_show_one(struct seq_file *s,
 373        const char *name, int index,    struct debug_isoc_trace *trace)
 374{
 375        seq_printf(s, "%s %d:\n"
 376                "cc=0X%02X\n"
 377                "scheduled frame %d (%d)\n"
 378                "submitted frame %d (%d)\n"
 379                "completed frame %d (%d)\n"
 380                "requested length=%d\n"
 381                "completed length=%d\n\n",
 382                name, index,
 383                trace->cc,
 384                trace->schedule_frame, trace->schedule_frame & 0xFFFF,
 385                trace->submit_frame, trace->submit_frame & 0xFFFF,
 386                trace->done_frame, trace->done_frame & 0xFFFF,
 387                trace->request_len,
 388                trace->done_len);
 389}
 390
 391static int debug_isoc_show(struct seq_file *s, void *v)
 392{
 393        struct imx21 *imx21 = s->private;
 394        struct debug_isoc_trace *trace;
 395        unsigned long flags;
 396        int i;
 397
 398        spin_lock_irqsave(&imx21->lock, flags);
 399
 400        trace = imx21->isoc_trace_failed;
 401        for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++)
 402                debug_isoc_show_one(s, "isoc failed", i, trace);
 403
 404        trace = imx21->isoc_trace;
 405        for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++)
 406                debug_isoc_show_one(s, "isoc", i, trace);
 407
 408        spin_unlock_irqrestore(&imx21->lock, flags);
 409
 410        return 0;
 411}
 412
 413static int debug_status_open(struct inode *inode, struct file *file)
 414{
 415        return single_open(file, debug_status_show, inode->i_private);
 416}
 417
 418static int debug_dmem_open(struct inode *inode, struct file *file)
 419{
 420        return single_open(file, debug_dmem_show, inode->i_private);
 421}
 422
 423static int debug_etd_open(struct inode *inode, struct file *file)
 424{
 425        return single_open(file, debug_etd_show, inode->i_private);
 426}
 427
 428static int debug_statistics_open(struct inode *inode, struct file *file)
 429{
 430        return single_open(file, debug_statistics_show, inode->i_private);
 431}
 432
 433static int debug_isoc_open(struct inode *inode, struct file *file)
 434{
 435        return single_open(file, debug_isoc_show, inode->i_private);
 436}
 437
 438static const struct file_operations debug_status_fops = {
 439        .open = debug_status_open,
 440        .read = seq_read,
 441        .llseek = seq_lseek,
 442        .release = single_release,
 443};
 444
 445static const struct file_operations debug_dmem_fops = {
 446        .open = debug_dmem_open,
 447        .read = seq_read,
 448        .llseek = seq_lseek,
 449        .release = single_release,
 450};
 451
 452static const struct file_operations debug_etd_fops = {
 453        .open = debug_etd_open,
 454        .read = seq_read,
 455        .llseek = seq_lseek,
 456        .release = single_release,
 457};
 458
 459static const struct file_operations debug_statistics_fops = {
 460        .open = debug_statistics_open,
 461        .read = seq_read,
 462        .llseek = seq_lseek,
 463        .release = single_release,
 464};
 465
 466static const struct file_operations debug_isoc_fops = {
 467        .open = debug_isoc_open,
 468        .read = seq_read,
 469        .llseek = seq_lseek,
 470        .release = single_release,
 471};
 472
 473static void create_debug_files(struct imx21 *imx21)
 474{
 475        imx21->debug_root = debugfs_create_dir(dev_name(imx21->dev), NULL);
 476        if (!imx21->debug_root)
 477                goto failed_create_rootdir;
 478
 479        if (!debugfs_create_file("status", S_IRUGO,
 480                        imx21->debug_root, imx21, &debug_status_fops))
 481                goto failed_create;
 482
 483        if (!debugfs_create_file("dmem", S_IRUGO,
 484                        imx21->debug_root, imx21, &debug_dmem_fops))
 485                goto failed_create;
 486
 487        if (!debugfs_create_file("etd", S_IRUGO,
 488                        imx21->debug_root, imx21, &debug_etd_fops))
 489                goto failed_create;
 490
 491        if (!debugfs_create_file("statistics", S_IRUGO,
 492                        imx21->debug_root, imx21, &debug_statistics_fops))
 493                goto failed_create;
 494
 495        if (!debugfs_create_file("isoc", S_IRUGO,
 496                        imx21->debug_root, imx21, &debug_isoc_fops))
 497                goto failed_create;
 498
 499        return;
 500
 501failed_create:
 502        debugfs_remove_recursive(imx21->debug_root);
 503
 504failed_create_rootdir:
 505        imx21->debug_root = NULL;
 506}
 507
 508
 509static void remove_debug_files(struct imx21 *imx21)
 510{
 511        if (imx21->debug_root) {
 512                debugfs_remove_recursive(imx21->debug_root);
 513                imx21->debug_root = NULL;
 514        }
 515}
 516
 517#endif
 518
 519