Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/usb/fotg210/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 155 kB image not shown  

Quelle  fotg210-hcd.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/* Faraday FOTG210 EHCI-like driver
 *
 * Copyright (c) 2013 Faraday Technology Corporation
 *
 * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com>
 *    Feng-Hsin Chiang <john453@faraday-tech.com>
 *    Po-Yu Chuang <ratbert.chuang@gmail.com>
 *
 * Most of code borrowed from the Linux-3.7 EHCI driver
 */

#include <linux/module.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/dmapool.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/iopoll.h>

#include <asm/byteorder.h>
#include <asm/irq.h>
#include <linux/unaligned.h>

#include "fotg210.h"

static const char hcd_name[] = "fotg210_hcd";

#undef FOTG210_URB_TRACE
#define FOTG210_STATS

/* magic numbers that can affect system performance */
#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
#define FOTG210_TUNE_RL_TT 0
#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define FOTG210_TUNE_MULT_TT 1

/* Some drivers think it's safe to schedule isochronous transfers more than 256
 * ms into the future (partly as a result of an old bug in the scheduling
 * code).  In an attempt to avoid trouble, we will use a minimum scheduling
 * length of 512 frames instead of 256.
 */

#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */

/* Initial IRQ latency:  faster than hw default */
static int log2_irq_thresh; /* 0 to 6 */
module_param(log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");

/* initial park setting:  slower than hw default */
static unsigned park;
module_param(park, uint, S_IRUGO);
MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets");

/* for link power management(LPM) feature */
static unsigned int hird;
module_param(hird, int, S_IRUGO);
MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");

#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)

#include "fotg210-hcd.h"

#define fotg210_dbg(fotg210, fmt, args...) \
 dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args)
#define fotg210_err(fotg210, fmt, args...) \
 dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args)
#define fotg210_info(fotg210, fmt, args...) \
 dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args)
#define fotg210_warn(fotg210, fmt, args...) \
 dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args)

/* check the values in the HCSPARAMS register (host controller _Structural_
 * parameters) see EHCI spec, Table 2-4 for each value
 */

static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label)
{
 u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params);

 fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params,
   HCS_N_PORTS(params));
}

/* check the values in the HCCPARAMS register (host controller _Capability_
 * parameters) see EHCI Spec, Table 2-5 for each value
 */

static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label)
{
 u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);

 fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label,
   params,
   HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
   HCC_CANPARK(params) ? " park" : "");
}

static void __maybe_unused
dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd)
{
 fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
   hc32_to_cpup(fotg210, &qtd->hw_next),
   hc32_to_cpup(fotg210, &qtd->hw_alt_next),
   hc32_to_cpup(fotg210, &qtd->hw_token),
   hc32_to_cpup(fotg210, &qtd->hw_buf[0]));
 if (qtd->hw_buf[1])
  fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
    hc32_to_cpup(fotg210, &qtd->hw_buf[1]),
    hc32_to_cpup(fotg210, &qtd->hw_buf[2]),
    hc32_to_cpup(fotg210, &qtd->hw_buf[3]),
    hc32_to_cpup(fotg210, &qtd->hw_buf[4]));
}

static void __maybe_unused
dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
{
 struct fotg210_qh_hw *hw = qh->hw;

 fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh,
   hw->hw_next, hw->hw_info1, hw->hw_info2,
   hw->hw_current);

 dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next);
}

static void __maybe_unused
dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd)
{
 fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label,
   itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next),
   itd->urb);

 fotg210_dbg(fotg210,
   " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
   hc32_to_cpu(fotg210, itd->hw_transaction[0]),
   hc32_to_cpu(fotg210, itd->hw_transaction[1]),
   hc32_to_cpu(fotg210, itd->hw_transaction[2]),
   hc32_to_cpu(fotg210, itd->hw_transaction[3]),
   hc32_to_cpu(fotg210, itd->hw_transaction[4]),
   hc32_to_cpu(fotg210, itd->hw_transaction[5]),
   hc32_to_cpu(fotg210, itd->hw_transaction[6]),
   hc32_to_cpu(fotg210, itd->hw_transaction[7]));

 fotg210_dbg(fotg210,
   " buf: %08x %08x %08x %08x %08x %08x %08x\n",
   hc32_to_cpu(fotg210, itd->hw_bufp[0]),
   hc32_to_cpu(fotg210, itd->hw_bufp[1]),
   hc32_to_cpu(fotg210, itd->hw_bufp[2]),
   hc32_to_cpu(fotg210, itd->hw_bufp[3]),
   hc32_to_cpu(fotg210, itd->hw_bufp[4]),
   hc32_to_cpu(fotg210, itd->hw_bufp[5]),
   hc32_to_cpu(fotg210, itd->hw_bufp[6]));

 fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n",
   itd->index[0], itd->index[1], itd->index[2],
   itd->index[3], itd->index[4], itd->index[5],
   itd->index[6], itd->index[7]);
}

static int __maybe_unused
dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
{
 return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
   label, label[0] ? " " : "", status,
   (status & STS_ASS) ? " Async" : "",
   (status & STS_PSS) ? " Periodic" : "",
   (status & STS_RECL) ? " Recl" : "",
   (status & STS_HALT) ? " Halt" : "",
   (status & STS_IAA) ? " IAA" : "",
   (status & STS_FATAL) ? " FATAL" : "",
   (status & STS_FLR) ? " FLR" : "",
   (status & STS_PCD) ? " PCD" : "",
   (status & STS_ERR) ? " ERR" : "",
   (status & STS_INT) ? " INT" : "");
}

static int __maybe_unused
dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
{
 return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s",
   label, label[0] ? " " : "", enable,
   (enable & STS_IAA) ? " IAA" : "",
   (enable & STS_FATAL) ? " FATAL" : "",
   (enable & STS_FLR) ? " FLR" : "",
   (enable & STS_PCD) ? " PCD" : "",
   (enable & STS_ERR) ? " ERR" : "",
   (enable & STS_INT) ? " INT" : "");
}

static const char *const fls_strings[] = { "1024""512""256""??" };

static int dbg_command_buf(char *buf, unsigned len, const char *label,
  u32 command)
{
 return scnprintf(buf, len,
   "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s",
   label, label[0] ? " " : "", command,
   (command & CMD_PARK) ? " park" : "(park)",
   CMD_PARK_CNT(command),
   (command >> 16) & 0x3f,
   (command & CMD_IAAD) ? " IAAD" : "",
   (command & CMD_ASE) ? " Async" : "",
   (command & CMD_PSE) ? " Periodic" : "",
   fls_strings[(command >> 2) & 0x3],
   (command & CMD_RESET) ? " Reset" : "",
   (command & CMD_RUN) ? "RUN" : "HALT");
}

static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port,
  u32 status)
{
 char *sig;

 /* signaling state */
 switch (status & (3 << 10)) {
 case 0 << 10:
  sig = "se0";
  break;
 case 1 << 10:
  sig = "k";
  break/* low speed */
 case 2 << 10:
  sig = "j";
  break;
 default:
  sig = "?";
  break;
 }

 scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s",
   label, label[0] ? " " : "", port, status,
   status >> 25, /*device address */
   sig,
   (status & PORT_RESET) ? " RESET" : "",
   (status & PORT_SUSPEND) ? " SUSPEND" : "",
   (status & PORT_RESUME) ? " RESUME" : "",
   (status & PORT_PEC) ? " PEC" : "",
   (status & PORT_PE) ? " PE" : "",
   (status & PORT_CSC) ? " CSC" : "",
   (status & PORT_CONNECT) ? " CONNECT" : "");

 return buf;
}

/* functions have the "wrong" filename when they're output... */
#define dbg_status(fotg210, label, status) {   \
 char _buf[80];      \
 dbg_status_buf(_buf, sizeof(_buf), label, status); \
 fotg210_dbg(fotg210, "%s\n", _buf);   \
}

#define dbg_cmd(fotg210, label, command) {   \
 char _buf[80];      \
 dbg_command_buf(_buf, sizeof(_buf), label, command); \
 fotg210_dbg(fotg210, "%s\n", _buf);   \
}

#define dbg_port(fotg210, label, port, status) {          \
 char _buf[80];              \
 fotg210_dbg(fotg210, "%s\n",            \
   dbg_port_buf(_buf, sizeof(_buf), label, port, status));\
}

/* troubleshooting help: expose state in debugfs */
static int debug_async_open(struct inode *, struct file *);
static int debug_periodic_open(struct inode *, struct file *);
static int debug_registers_open(struct inode *, struct file *);
static int debug_async_open(struct inode *, struct file *);

static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
static int debug_close(struct inode *, struct file *);

static const struct file_operations debug_async_fops = {
 .owner  = THIS_MODULE,
 .open  = debug_async_open,
 .read  = debug_output,
 .release = debug_close,
 .llseek  = default_llseek,
};
static const struct file_operations debug_periodic_fops = {
 .owner  = THIS_MODULE,
 .open  = debug_periodic_open,
 .read  = debug_output,
 .release = debug_close,
 .llseek  = default_llseek,
};
static const struct file_operations debug_registers_fops = {
 .owner  = THIS_MODULE,
 .open  = debug_registers_open,
 .read  = debug_output,
 .release = debug_close,
 .llseek  = default_llseek,
};

static struct dentry *fotg210_debug_root;

struct debug_buffer {
 ssize_t (*fill_func)(struct debug_buffer *); /* fill method */
 struct usb_bus *bus;
 struct mutex mutex; /* protect filling of buffer */
 size_t count;  /* number of characters filled into buffer */
 char *output_buf;
 size_t alloc_size;
};

static inline char speed_char(u32 scratch)
{
 switch (scratch & (3 << 12)) {
 case QH_FULL_SPEED:
  return 'f';

 case QH_LOW_SPEED:
  return 'l';

 case QH_HIGH_SPEED:
  return 'h';

 default:
  return '?';
 }
}

static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token)
{
 __u32 v = hc32_to_cpu(fotg210, token);

 if (v & QTD_STS_ACTIVE)
  return '*';
 if (v & QTD_STS_HALT)
  return '-';
 if (!IS_SHORT_READ(v))
  return ' ';
 /* tries to advance through hw_alt_next */
 return '/';
}

static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
  char **nextp, unsigned *sizep)
{
 u32 scratch;
 u32 hw_curr;
 struct fotg210_qtd *td;
 unsigned temp;
 unsigned size = *sizep;
 char *next = *nextp;
 char mark;
 __le32 list_end = FOTG210_LIST_END(fotg210);
 struct fotg210_qh_hw *hw = qh->hw;

 if (hw->hw_qtd_next == list_end) /* NEC does this */
  mark = '@';
 else
  mark = token_mark(fotg210, hw->hw_token);
 if (mark == '/') { /* qh_alt_next controls qh advance? */
  if ((hw->hw_alt_next & QTD_MASK(fotg210)) ==
      fotg210->async->hw->hw_alt_next)
   mark = '#'/* blocked */
  else if (hw->hw_alt_next == list_end)
   mark = '.'/* use hw_qtd_next */
  /* else alt_next points to some other qtd */
 }
 scratch = hc32_to_cpup(fotg210, &hw->hw_info1);
 hw_curr = (mark == '*') ? hc32_to_cpup(fotg210, &hw->hw_current) : 0;
 temp = scnprintf(next, size,
   "qh/%p dev%d %cs ep%d %08x %08x(%08x%c %s nak%d)",
   qh, scratch & 0x007f,
   speed_char(scratch),
   (scratch >> 8) & 0x000f,
   scratch, hc32_to_cpup(fotg210, &hw->hw_info2),
   hc32_to_cpup(fotg210, &hw->hw_token), mark,
   (cpu_to_hc32(fotg210, QTD_TOGGLE) & hw->hw_token)
    ? "data1" : "data0",
   (hc32_to_cpup(fotg210, &hw->hw_alt_next) >> 1) & 0x0f);
 size -= temp;
 next += temp;

 /* hc may be modifying the list as we read it ... */
 list_for_each_entry(td, &qh->qtd_list, qtd_list) {
  scratch = hc32_to_cpup(fotg210, &td->hw_token);
  mark = ' ';
  if (hw_curr == td->qtd_dma)
   mark = '*';
  else if (hw->hw_qtd_next == cpu_to_hc32(fotg210, td->qtd_dma))
   mark = '+';
  else if (QTD_LENGTH(scratch)) {
   if (td->hw_alt_next == fotg210->async->hw->hw_alt_next)
    mark = '#';
   else if (td->hw_alt_next != list_end)
    mark = '/';
  }
  temp = scnprintf(next, size,
     "\n\t%p%c%s len=%d %08x urb %p",
     td, mark, ({ char *tmp;
    switch ((scratch>>8)&0x03) {
    case 0:
     tmp = "out";
     break;
    case 1:
     tmp = "in";
     break;
    case 2:
     tmp = "setup";
     break;
    default:
     tmp = "?";
     break;
     } tmp; }),
    (scratch >> 16) & 0x7fff,
    scratch,
    td->urb);
  size -= temp;
  next += temp;
 }

 temp = scnprintf(next, size, "\n");

 size -= temp;
 next += temp;

 *sizep = size;
 *nextp = next;
}

static ssize_t fill_async_buffer(struct debug_buffer *buf)
{
 struct usb_hcd *hcd;
 struct fotg210_hcd *fotg210;
 unsigned long flags;
 unsigned temp, size;
 char *next;
 struct fotg210_qh *qh;

 hcd = bus_to_hcd(buf->bus);
 fotg210 = hcd_to_fotg210(hcd);
 next = buf->output_buf;
 size = buf->alloc_size;

 *next = 0;

 /* dumps a snapshot of the async schedule.
 * usually empty except for long-term bulk reads, or head.
 * one QH per line, and TDs we know about
 */

 spin_lock_irqsave(&fotg210->lock, flags);
 for (qh = fotg210->async->qh_next.qh; size > 0 && qh;
   qh = qh->qh_next.qh)
  qh_lines(fotg210, qh, &next, &size);
 if (fotg210->async_unlink && size > 0) {
  temp = scnprintf(next, size, "\nunlink =\n");
  size -= temp;
  next += temp;

  for (qh = fotg210->async_unlink; size > 0 && qh;
    qh = qh->unlink_next)
   qh_lines(fotg210, qh, &next, &size);
 }
 spin_unlock_irqrestore(&fotg210->lock, flags);

 return strlen(buf->output_buf);
}

/* count tds, get ep direction */
static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210,
  struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size)
{
 u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1);
 struct fotg210_qtd *qtd;
 char *type = "";
 unsigned temp = 0;

 /* count tds, get ep direction */
 list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
  temp++;
  switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) {
  case 0:
   type = "out";
   continue;
  case 1:
   type = "in";
   continue;
  }
 }

 return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)",
   speed_char(scratch), scratch & 0x007f,
   (scratch >> 8) & 0x000f, type, qh->usecs,
   qh->c_usecs, temp, (scratch >> 16) & 0x7ff);
}

#define DBG_SCHED_LIMIT 64
static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
{
 struct usb_hcd *hcd;
 struct fotg210_hcd *fotg210;
 unsigned long flags;
 union fotg210_shadow p, *seen;
 unsigned temp, size, seen_count;
 char *next;
 unsigned i;
 __hc32 tag;

 seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
 if (!seen)
  return 0;

 seen_count = 0;

 hcd = bus_to_hcd(buf->bus);
 fotg210 = hcd_to_fotg210(hcd);
 next = buf->output_buf;
 size = buf->alloc_size;

 temp = scnprintf(next, size, "size = %d\n", fotg210->periodic_size);
 size -= temp;
 next += temp;

 /* dump a snapshot of the periodic schedule.
 * iso changes, interrupt usually doesn't.
 */

 spin_lock_irqsave(&fotg210->lock, flags);
 for (i = 0; i < fotg210->periodic_size; i++) {
  p = fotg210->pshadow[i];
  if (likely(!p.ptr))
   continue;

  tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]);

  temp = scnprintf(next, size, "%4d: ", i);
  size -= temp;
  next += temp;

  do {
   struct fotg210_qh_hw *hw;

   switch (hc32_to_cpu(fotg210, tag)) {
   case Q_TYPE_QH:
    hw = p.qh->hw;
    temp = scnprintf(next, size, " qh%d-%04x/%p",
      p.qh->period,
      hc32_to_cpup(fotg210,
       &hw->hw_info2)
       /* uframe masks */
       & (QH_CMASK | QH_SMASK),
      p.qh);
    size -= temp;
    next += temp;
    /* don't repeat what follows this qh */
    for (temp = 0; temp < seen_count; temp++) {
     if (seen[temp].ptr != p.ptr)
      continue;
     if (p.qh->qh_next.ptr) {
      temp = scnprintf(next, size,
        " ...");
      size -= temp;
      next += temp;
     }
     break;
    }
    /* show more info the first time around */
    if (temp == seen_count) {
     temp = output_buf_tds_dir(next,
       fotg210, hw,
       p.qh, size);

     if (seen_count < DBG_SCHED_LIMIT)
      seen[seen_count++].qh = p.qh;
    } else
     temp = 0;
    tag = Q_NEXT_TYPE(fotg210, hw->hw_next);
    p = p.qh->qh_next;
    break;
   case Q_TYPE_FSTN:
    temp = scnprintf(next, size,
      " fstn-%8x/%p",
      p.fstn->hw_prev, p.fstn);
    tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next);
    p = p.fstn->fstn_next;
    break;
   case Q_TYPE_ITD:
    temp = scnprintf(next, size,
      " itd/%p", p.itd);
    tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next);
    p = p.itd->itd_next;
    break;
   }
   size -= temp;
   next += temp;
  } while (p.ptr);

  temp = scnprintf(next, size, "\n");
  size -= temp;
  next += temp;
 }
 spin_unlock_irqrestore(&fotg210->lock, flags);
 kfree(seen);

 return buf->alloc_size - size;
}
#undef DBG_SCHED_LIMIT

static const char *rh_state_string(struct fotg210_hcd *fotg210)
{
 switch (fotg210->rh_state) {
 case FOTG210_RH_HALTED:
  return "halted";
 case FOTG210_RH_SUSPENDED:
  return "suspended";
 case FOTG210_RH_RUNNING:
  return "running";
 case FOTG210_RH_STOPPING:
  return "stopping";
 }
 return "?";
}

static ssize_t fill_registers_buffer(struct debug_buffer *buf)
{
 struct usb_hcd *hcd;
 struct fotg210_hcd *fotg210;
 unsigned long flags;
 unsigned temp, size, i;
 char *next, scratch[80];
 static const char fmt[] = "%*s\n";
 static const char label[] = "";

 hcd = bus_to_hcd(buf->bus);
 fotg210 = hcd_to_fotg210(hcd);
 next = buf->output_buf;
 size = buf->alloc_size;

 spin_lock_irqsave(&fotg210->lock, flags);

 if (!HCD_HW_ACCESSIBLE(hcd)) {
  size = scnprintf(next, size,
    "bus %s, device %s\n"
    "%s\n"
    "SUSPENDED(no register access)\n",
    hcd->self.controller->bus->name,
    dev_name(hcd->self.controller),
    hcd->product_desc);
  goto done;
 }

 /* Capability Registers */
 i = HC_VERSION(fotg210, fotg210_readl(fotg210,
   &fotg210->caps->hc_capbase));
 temp = scnprintf(next, size,
   "bus %s, device %s\n"
   "%s\n"
   "EHCI %x.%02x, rh state %s\n",
   hcd->self.controller->bus->name,
   dev_name(hcd->self.controller),
   hcd->product_desc,
   i >> 8, i & 0x0ff, rh_state_string(fotg210));
 size -= temp;
 next += temp;

 /* FIXME interpret both types of params */
 i = fotg210_readl(fotg210, &fotg210->caps->hcs_params);
 temp = scnprintf(next, size, "structural params 0x%08x\n", i);
 size -= temp;
 next += temp;

 i = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
 temp = scnprintf(next, size, "capability params 0x%08x\n", i);
 size -= temp;
 next += temp;

 /* Operational Registers */
 temp = dbg_status_buf(scratch, sizeof(scratch), label,
   fotg210_readl(fotg210, &fotg210->regs->status));
 temp = scnprintf(next, size, fmt, temp, scratch);
 size -= temp;
 next += temp;

 temp = dbg_command_buf(scratch, sizeof(scratch), label,
   fotg210_readl(fotg210, &fotg210->regs->command));
 temp = scnprintf(next, size, fmt, temp, scratch);
 size -= temp;
 next += temp;

 temp = dbg_intr_buf(scratch, sizeof(scratch), label,
   fotg210_readl(fotg210, &fotg210->regs->intr_enable));
 temp = scnprintf(next, size, fmt, temp, scratch);
 size -= temp;
 next += temp;

 temp = scnprintf(next, size, "uframe %04x\n",
   fotg210_read_frame_index(fotg210));
 size -= temp;
 next += temp;

 if (fotg210->async_unlink) {
  temp = scnprintf(next, size, "async unlink qh %p\n",
    fotg210->async_unlink);
  size -= temp;
  next += temp;
 }

#ifdef FOTG210_STATS
 temp = scnprintf(next, size,
   "irq normal %ld err %ld iaa %ld(lost %ld)\n",
   fotg210->stats.normal, fotg210->stats.error,
   fotg210->stats.iaa, fotg210->stats.lost_iaa);
 size -= temp;
 next += temp;

 temp = scnprintf(next, size, "complete %ld unlink %ld\n",
   fotg210->stats.complete, fotg210->stats.unlink);
 size -= temp;
 next += temp;
#endif

done:
 spin_unlock_irqrestore(&fotg210->lock, flags);

 return buf->alloc_size - size;
}

static struct debug_buffer
*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *))
{
 struct debug_buffer *buf;

 buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);

 if (buf) {
  buf->bus = bus;
  buf->fill_func = fill_func;
  mutex_init(&buf->mutex);
  buf->alloc_size = PAGE_SIZE;
 }

 return buf;
}

static int fill_buffer(struct debug_buffer *buf)
{
 int ret = 0;

 if (!buf->output_buf)
  buf->output_buf = vmalloc(buf->alloc_size);

 if (!buf->output_buf) {
  ret = -ENOMEM;
  goto out;
 }

 ret = buf->fill_func(buf);

 if (ret >= 0) {
  buf->count = ret;
  ret = 0;
 }

out:
 return ret;
}

static ssize_t debug_output(struct file *file, char __user *user_buf,
  size_t len, loff_t *offset)
{
 struct debug_buffer *buf = file->private_data;
 int ret = 0;

 mutex_lock(&buf->mutex);
 if (buf->count == 0) {
  ret = fill_buffer(buf);
  if (ret != 0) {
   mutex_unlock(&buf->mutex);
   goto out;
  }
 }
 mutex_unlock(&buf->mutex);

 ret = simple_read_from_buffer(user_buf, len, offset,
   buf->output_buf, buf->count);

out:
 return ret;

}

static int debug_close(struct inode *inode, struct file *file)
{
 struct debug_buffer *buf = file->private_data;

 if (buf) {
  vfree(buf->output_buf);
  kfree(buf);
 }

 return 0;
}
static int debug_async_open(struct inode *inode, struct file *file)
{
 file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);

 return file->private_data ? 0 : -ENOMEM;
}

static int debug_periodic_open(struct inode *inode, struct file *file)
{
 struct debug_buffer *buf;

 buf = alloc_buffer(inode->i_private, fill_periodic_buffer);
 if (!buf)
  return -ENOMEM;

 buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
 file->private_data = buf;
 return 0;
}

static int debug_registers_open(struct inode *inode, struct file *file)
{
 file->private_data = alloc_buffer(inode->i_private,
   fill_registers_buffer);

 return file->private_data ? 0 : -ENOMEM;
}

static inline void create_debug_files(struct fotg210_hcd *fotg210)
{
 struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self;
 struct dentry *root;

 root = debugfs_create_dir(bus->bus_name, fotg210_debug_root);

 debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops);
 debugfs_create_file("periodic", S_IRUGO, root, bus,
       &debug_periodic_fops);
 debugfs_create_file("registers", S_IRUGO, root, bus,
       &debug_registers_fops);
}

static inline void remove_debug_files(struct fotg210_hcd *fotg210)
{
 struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self;

 debugfs_lookup_and_remove(bus->bus_name, fotg210_debug_root);
}

/* handshake - spin reading hc until handshake completes or fails
 * @ptr: address of hc register to be read
 * @mask: bits to look at in result of read
 * @done: value of those bits when handshake succeeds
 * @usec: timeout in microseconds
 *
 * Returns negative errno, or zero on success
 *
 * Success happens when the "mask" bits have the specified value (hardware
 * handshake done).  There are two failure modes:  "usec" have passed (major
 * hardware flakeout), or the register reads as all-ones (hardware removed).
 *
 * That last failure should_only happen in cases like physical cardbus eject
 * before driver shutdown. But it also seems to be caused by bugs in cardbus
 * bridge shutdown:  shutting down the bridge before the devices using it.
 */

static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr,
  u32 mask, u32 done, int usec)
{
 u32 result;
 int ret;

 ret = readl_poll_timeout_atomic(ptr, result,
     ((result & mask) == done ||
      result == U32_MAX), 1, usec);
 if (result == U32_MAX)  /* card removed */
  return -ENODEV;

 return ret;
}

/* Force HC to halt state from unknown (EHCI spec section 2.3).
 * Must be called with interrupts enabled and the lock not held.
 */

static int fotg210_halt(struct fotg210_hcd *fotg210)
{
 u32 temp;

 spin_lock_irq(&fotg210->lock);

 /* disable any irqs left enabled by previous code */
 fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable);

 /*
 * This routine gets called during probe before fotg210->command
 * has been initialized, so we can't rely on its value.
 */

 fotg210->command &= ~CMD_RUN;
 temp = fotg210_readl(fotg210, &fotg210->regs->command);
 temp &= ~(CMD_RUN | CMD_IAAD);
 fotg210_writel(fotg210, temp, &fotg210->regs->command);

 spin_unlock_irq(&fotg210->lock);
 synchronize_irq(fotg210_to_hcd(fotg210)->irq);

 return handshake(fotg210, &fotg210->regs->status,
   STS_HALT, STS_HALT, 16 * 125);
}

/* Reset a non-running (STS_HALT == 1) controller.
 * Must be called with interrupts enabled and the lock not held.
 */

static int fotg210_reset(struct fotg210_hcd *fotg210)
{
 int retval;
 u32 command = fotg210_readl(fotg210, &fotg210->regs->command);

 /* If the EHCI debug controller is active, special care must be
 * taken before and after a host controller reset
 */

 if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210)))
  fotg210->debug = NULL;

 command |= CMD_RESET;
 dbg_cmd(fotg210, "reset", command);
 fotg210_writel(fotg210, command, &fotg210->regs->command);
 fotg210->rh_state = FOTG210_RH_HALTED;
 fotg210->next_statechange = jiffies;
 retval = handshake(fotg210, &fotg210->regs->command,
   CMD_RESET, 0, 250 * 1000);

 if (retval)
  return retval;

 if (fotg210->debug)
  dbgp_external_startup(fotg210_to_hcd(fotg210));

 fotg210->port_c_suspend = fotg210->suspended_ports =
   fotg210->resuming_ports = 0;
 return retval;
}

/* Idle the controller (turn off the schedules).
 * Must be called with interrupts enabled and the lock not held.
 */

static void fotg210_quiesce(struct fotg210_hcd *fotg210)
{
 u32 temp;

 if (fotg210->rh_state != FOTG210_RH_RUNNING)
  return;

 /* wait for any schedule enables/disables to take effect */
 temp = (fotg210->command << 10) & (STS_ASS | STS_PSS);
 handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp,
   16 * 125);

 /* then disable anything that's still active */
 spin_lock_irq(&fotg210->lock);
 fotg210->command &= ~(CMD_ASE | CMD_PSE);
 fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);
 spin_unlock_irq(&fotg210->lock);

 /* hardware can take 16 microframes to turn off ... */
 handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0,
   16 * 125);
}

static void end_unlink_async(struct fotg210_hcd *fotg210);
static void unlink_empty_async(struct fotg210_hcd *fotg210);
static void fotg210_work(struct fotg210_hcd *fotg210);
static void start_unlink_intr(struct fotg210_hcd *fotg210,
         struct fotg210_qh *qh);
static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh);

/* Set a bit in the USBCMD register */
static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit)
{
 fotg210->command |= bit;
 fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);

 /* unblock posted write */
 fotg210_readl(fotg210, &fotg210->regs->command);
}

/* Clear a bit in the USBCMD register */
static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit)
{
 fotg210->command &= ~bit;
 fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);

 /* unblock posted write */
 fotg210_readl(fotg210, &fotg210->regs->command);
}

/* EHCI timer support...  Now using hrtimers.
 *
 * Lots of different events are triggered from fotg210->hrtimer.  Whenever
 * the timer routine runs, it checks each possible event; events that are
 * currently enabled and whose expiration time has passed get handled.
 * The set of enabled events is stored as a collection of bitflags in
 * fotg210->enabled_hrtimer_events, and they are numbered in order of
 * increasing delay values (ranging between 1 ms and 100 ms).
 *
 * Rather than implementing a sorted list or tree of all pending events,
 * we keep track only of the lowest-numbered pending event, in
 * fotg210->next_hrtimer_event.  Whenever fotg210->hrtimer gets restarted, its
 * expiration time is set to the timeout value for this event.
 *
 * As a result, events might not get handled right away; the actual delay
 * could be anywhere up to twice the requested delay.  This doesn't
 * matter, because none of the events are especially time-critical.  The
 * ones that matter most all have a delay of 1 ms, so they will be
 * handled after 2 ms at most, which is okay.  In addition to this, we
 * allow for an expiration range of 1 ms.
 */


/* Delay lengths for the hrtimer event types.
 * Keep this list sorted by delay length, in the same order as
 * the event types indexed by enum fotg210_hrtimer_event in fotg210.h.
 */

static unsigned event_delays_ns[] = {
 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_ASS */
 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_PSS */
 1 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_POLL_DEAD */
 1125 * NSEC_PER_USEC, /* FOTG210_HRTIMER_UNLINK_INTR */
 2 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_FREE_ITDS */
 6 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_ASYNC_UNLINKS */
 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IAA_WATCHDOG */
 10 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_PERIODIC */
 15 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_DISABLE_ASYNC */
 100 * NSEC_PER_MSEC, /* FOTG210_HRTIMER_IO_WATCHDOG */
};

/* Enable a pending hrtimer event */
static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event,
  bool resched)
{
 ktime_t *timeout = &fotg210->hr_timeouts[event];

 if (resched)
  *timeout = ktime_add(ktime_get(), event_delays_ns[event]);
 fotg210->enabled_hrtimer_events |= (1 << event);

 /* Track only the lowest-numbered pending event */
 if (event < fotg210->next_hrtimer_event) {
  fotg210->next_hrtimer_event = event;
  hrtimer_start_range_ns(&fotg210->hrtimer, *timeout,
    NSEC_PER_MSEC, HRTIMER_MODE_ABS);
 }
}


/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
static void fotg210_poll_ASS(struct fotg210_hcd *fotg210)
{
 unsigned actual, want;

 /* Don't enable anything if the controller isn't running (e.g., died) */
 if (fotg210->rh_state != FOTG210_RH_RUNNING)
  return;

 want = (fotg210->command & CMD_ASE) ? STS_ASS : 0;
 actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_ASS;

 if (want != actual) {

  /* Poll again later, but give up after about 20 ms */
  if (fotg210->ASS_poll_count++ < 20) {
   fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS,
     true);
   return;
  }
  fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n",
    want, actual);
 }
 fotg210->ASS_poll_count = 0;

 /* The status is up-to-date; restart or stop the schedule as needed */
 if (want == 0) { /* Stopped */
  if (fotg210->async_count > 0)
   fotg210_set_command_bit(fotg210, CMD_ASE);

 } else {  /* Running */
  if (fotg210->async_count == 0) {

   /* Turn off the schedule after a while */
   fotg210_enable_event(fotg210,
     FOTG210_HRTIMER_DISABLE_ASYNC,
     true);
  }
 }
}

/* Turn off the async schedule after a brief delay */
static void fotg210_disable_ASE(struct fotg210_hcd *fotg210)
{
 fotg210_clear_command_bit(fotg210, CMD_ASE);
}


/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
static void fotg210_poll_PSS(struct fotg210_hcd *fotg210)
{
 unsigned actual, want;

 /* Don't do anything if the controller isn't running (e.g., died) */
 if (fotg210->rh_state != FOTG210_RH_RUNNING)
  return;

 want = (fotg210->command & CMD_PSE) ? STS_PSS : 0;
 actual = fotg210_readl(fotg210, &fotg210->regs->status) & STS_PSS;

 if (want != actual) {

  /* Poll again later, but give up after about 20 ms */
  if (fotg210->PSS_poll_count++ < 20) {
   fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS,
     true);
   return;
  }
  fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
    want, actual);
 }
 fotg210->PSS_poll_count = 0;

 /* The status is up-to-date; restart or stop the schedule as needed */
 if (want == 0) { /* Stopped */
  if (fotg210->periodic_count > 0)
   fotg210_set_command_bit(fotg210, CMD_PSE);

 } else {  /* Running */
  if (fotg210->periodic_count == 0) {

   /* Turn off the schedule after a while */
   fotg210_enable_event(fotg210,
     FOTG210_HRTIMER_DISABLE_PERIODIC,
     true);
  }
 }
}

/* Turn off the periodic schedule after a brief delay */
static void fotg210_disable_PSE(struct fotg210_hcd *fotg210)
{
 fotg210_clear_command_bit(fotg210, CMD_PSE);
}


/* Poll the STS_HALT status bit; see when a dead controller stops */
static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210)
{
 if (!(fotg210_readl(fotg210, &fotg210->regs->status) & STS_HALT)) {

  /* Give up after a few milliseconds */
  if (fotg210->died_poll_count++ < 5) {
   /* Try again later */
   fotg210_enable_event(fotg210,
     FOTG210_HRTIMER_POLL_DEAD, true);
   return;
  }
  fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n");
 }

 /* Clean up the mess */
 fotg210->rh_state = FOTG210_RH_HALTED;
 fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable);
 fotg210_work(fotg210);
 end_unlink_async(fotg210);

 /* Not in process context, so don't try to reset the controller */
}


/* Handle unlinked interrupt QHs once they are gone from the hardware */
static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210)
{
 bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING);

 /*
 * Process all the QHs on the intr_unlink list that were added
 * before the current unlink cycle began.  The list is in
 * temporal order, so stop when we reach the first entry in the
 * current cycle.  But if the root hub isn't running then
 * process all the QHs on the list.
 */

 fotg210->intr_unlinking = true;
 while (fotg210->intr_unlink) {
  struct fotg210_qh *qh = fotg210->intr_unlink;

  if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle)
   break;
  fotg210->intr_unlink = qh->unlink_next;
  qh->unlink_next = NULL;
  end_unlink_intr(fotg210, qh);
 }

 /* Handle remaining entries later */
 if (fotg210->intr_unlink) {
  fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR,
    true);
  ++fotg210->intr_unlink_cycle;
 }
 fotg210->intr_unlinking = false;
}


/* Start another free-iTDs/siTDs cycle */
static void start_free_itds(struct fotg210_hcd *fotg210)
{
 if (!(fotg210->enabled_hrtimer_events &
   BIT(FOTG210_HRTIMER_FREE_ITDS))) {
  fotg210->last_itd_to_free = list_entry(
    fotg210->cached_itd_list.prev,
    struct fotg210_itd, itd_list);
  fotg210_enable_event(fotg210, FOTG210_HRTIMER_FREE_ITDS, true);
 }
}

/* Wait for controller to stop using old iTDs and siTDs */
static void end_free_itds(struct fotg210_hcd *fotg210)
{
 struct fotg210_itd *itd, *n;

 if (fotg210->rh_state < FOTG210_RH_RUNNING)
  fotg210->last_itd_to_free = NULL;

 list_for_each_entry_safe(itd, n, &fotg210->cached_itd_list, itd_list) {
  list_del(&itd->itd_list);
  dma_pool_free(fotg210->itd_pool, itd, itd->itd_dma);
  if (itd == fotg210->last_itd_to_free)
   break;
 }

 if (!list_empty(&fotg210->cached_itd_list))
  start_free_itds(fotg210);
}


/* Handle lost (or very late) IAA interrupts */
static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210)
{
 if (fotg210->rh_state != FOTG210_RH_RUNNING)
  return;

 /*
 * Lost IAA irqs wedge things badly; seen first with a vt8235.
 * So we need this watchdog, but must protect it against both
 * (a) SMP races against real IAA firing and retriggering, and
 * (b) clean HC shutdown, when IAA watchdog was pending.
 */

 if (fotg210->async_iaa) {
  u32 cmd, status;

  /* If we get here, IAA is *REALLY* late.  It's barely
 * conceivable that the system is so busy that CMD_IAAD
 * is still legitimately set, so let's be sure it's
 * clear before we read STS_IAA.  (The HC should clear
 * CMD_IAAD when it sets STS_IAA.)
 */

  cmd = fotg210_readl(fotg210, &fotg210->regs->command);

  /*
 * If IAA is set here it either legitimately triggered
 * after the watchdog timer expired (_way_ late, so we'll
 * still count it as lost) ... or a silicon erratum:
 * - VIA seems to set IAA without triggering the IRQ;
 * - IAAD potentially cleared without setting IAA.
 */

  status = fotg210_readl(fotg210, &fotg210->regs->status);
  if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
   INCR(fotg210->stats.lost_iaa);
   fotg210_writel(fotg210, STS_IAA,
     &fotg210->regs->status);
  }

  fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n",
    status, cmd);
  end_unlink_async(fotg210);
 }
}


/* Enable the I/O watchdog, if appropriate */
static void turn_on_io_watchdog(struct fotg210_hcd *fotg210)
{
 /* Not needed if the controller isn't running or it's already enabled */
 if (fotg210->rh_state != FOTG210_RH_RUNNING ||
   (fotg210->enabled_hrtimer_events &
   BIT(FOTG210_HRTIMER_IO_WATCHDOG)))
  return;

 /*
 * Isochronous transfers always need the watchdog.
 * For other sorts we use it only if the flag is set.
 */

 if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog &&
   fotg210->async_count + fotg210->intr_count > 0))
  fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG,
    true);
}


/* Handler functions for the hrtimer event types.
 * Keep this array in the same order as the event types indexed by
 * enum fotg210_hrtimer_event in fotg210.h.
 */

static void (*event_handlers[])(struct fotg210_hcd *) = {
 fotg210_poll_ASS,   /* FOTG210_HRTIMER_POLL_ASS */
 fotg210_poll_PSS,   /* FOTG210_HRTIMER_POLL_PSS */
 fotg210_handle_controller_death, /* FOTG210_HRTIMER_POLL_DEAD */
 fotg210_handle_intr_unlinks, /* FOTG210_HRTIMER_UNLINK_INTR */
 end_free_itds,   /* FOTG210_HRTIMER_FREE_ITDS */
 unlink_empty_async,  /* FOTG210_HRTIMER_ASYNC_UNLINKS */
 fotg210_iaa_watchdog,  /* FOTG210_HRTIMER_IAA_WATCHDOG */
 fotg210_disable_PSE,  /* FOTG210_HRTIMER_DISABLE_PERIODIC */
 fotg210_disable_ASE,  /* FOTG210_HRTIMER_DISABLE_ASYNC */
 fotg210_work,   /* FOTG210_HRTIMER_IO_WATCHDOG */
};

static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t)
{
 struct fotg210_hcd *fotg210 =
   container_of(t, struct fotg210_hcd, hrtimer);
 ktime_t now;
 unsigned long events;
 unsigned long flags;
 unsigned e;

 spin_lock_irqsave(&fotg210->lock, flags);

 events = fotg210->enabled_hrtimer_events;
 fotg210->enabled_hrtimer_events = 0;
 fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT;

 /*
 * Check each pending event.  If its time has expired, handle
 * the event; otherwise re-enable it.
 */

 now = ktime_get();
 for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) {
  if (ktime_compare(now, fotg210->hr_timeouts[e]) >= 0)
   event_handlers[e](fotg210);
  else
   fotg210_enable_event(fotg210, e, false);
 }

 spin_unlock_irqrestore(&fotg210->lock, flags);
 return HRTIMER_NORESTART;
}

#define fotg210_bus_suspend NULL
#define fotg210_bus_resume NULL

static int check_reset_complete(struct fotg210_hcd *fotg210, int index,
  u32 __iomem *status_reg, int port_status)
{
 if (!(port_status & PORT_CONNECT))
  return port_status;

 /* if reset finished and it's still not enabled -- handoff */
 if (!(port_status & PORT_PE))
  /* with integrated TT, there's nobody to hand it to! */
  fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n",
    index + 1);
 else
  fotg210_dbg(fotg210, "port %d reset complete, port enabled\n",
    index + 1);

 return port_status;
}


/* build "status change" packet (one or two bytes) from HC registers */

static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf)
{
 struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
 u32 temp, status;
 u32 mask;
 int retval = 1;
 unsigned long flags;

 /* init status to no-changes */
 buf[0] = 0;

 /* Inform the core about resumes-in-progress by returning
 * a non-zero value even if there are no status changes.
 */

 status = fotg210->resuming_ports;

 mask = PORT_CSC | PORT_PEC;
 /* PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND */

 /* no hub change reports (bit 0) for now (power, ...) */

 /* port N changes (bit N)? */
 spin_lock_irqsave(&fotg210->lock, flags);

 temp = fotg210_readl(fotg210, &fotg210->regs->port_status);

 /*
 * Return status information even for ports with OWNER set.
 * Otherwise hub_wq wouldn't see the disconnect event when a
 * high-speed device is switched over to the companion
 * controller by the user.
 */


 if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) ||
   (fotg210->reset_done[0] &&
   time_after_eq(jiffies, fotg210->reset_done[0]))) {
  buf[0] |= 1 << 1;
  status = STS_PCD;
 }
 /* FIXME autosuspend idle root hubs */
 spin_unlock_irqrestore(&fotg210->lock, flags);
 return status ? retval : 0;
}

static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210,
  struct usb_hub_descriptor *desc)
{
 int ports = HCS_N_PORTS(fotg210->hcs_params);
 u16 temp;

 desc->bDescriptorType = USB_DT_HUB;
 desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */
 desc->bHubContrCurrent = 0;

 desc->bNbrPorts = ports;
 temp = 1 + (ports / 8);
 desc->bDescLength = 7 + 2 * temp;

 /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
 memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
 memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);

 temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
 temp |= HUB_CHAR_NO_LPSM; /* no power switching */
 desc->wHubCharacteristics = cpu_to_le16(temp);
}

static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
  u16 wIndex, char *buf, u16 wLength)
{
 struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
 int ports = HCS_N_PORTS(fotg210->hcs_params);
 u32 __iomem *status_reg = &fotg210->regs->port_status;
 u32 temp, temp1, status;
 unsigned long flags;
 int retval = 0;
 unsigned selector;

 /*
 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
 * (track current state ourselves) ... blink for diagnostics,
 * power, "this is the one", etc.  EHCI spec supports this.
 */


 spin_lock_irqsave(&fotg210->lock, flags);
 switch (typeReq) {
 case ClearHubFeature:
  switch (wValue) {
  case C_HUB_LOCAL_POWER:
  case C_HUB_OVER_CURRENT:
   /* no hub-wide feature/status flags */
   break;
  default:
   goto error;
  }
  break;
 case ClearPortFeature:
  if (!wIndex || wIndex > ports)
   goto error;
  wIndex--;
  temp = fotg210_readl(fotg210, status_reg);
  temp &= ~PORT_RWC_BITS;

  /*
 * Even if OWNER is set, so the port is owned by the
 * companion controller, hub_wq needs to be able to clear
 * the port-change status bits (especially
 * USB_PORT_STAT_C_CONNECTION).
 */


  switch (wValue) {
  case USB_PORT_FEAT_ENABLE:
   fotg210_writel(fotg210, temp & ~PORT_PE, status_reg);
   break;
  case USB_PORT_FEAT_C_ENABLE:
   fotg210_writel(fotg210, temp | PORT_PEC, status_reg);
   break;
  case USB_PORT_FEAT_SUSPEND:
   if (temp & PORT_RESET)
    goto error;
   if (!(temp & PORT_SUSPEND))
    break;
   if ((temp & PORT_PE) == 0)
    goto error;

   /* resume signaling for 20 msec */
   fotg210_writel(fotg210, temp | PORT_RESUME, status_reg);
   fotg210->reset_done[wIndex] = jiffies
     + msecs_to_jiffies(USB_RESUME_TIMEOUT);
   break;
  case USB_PORT_FEAT_C_SUSPEND:
   clear_bit(wIndex, &fotg210->port_c_suspend);
   break;
  case USB_PORT_FEAT_C_CONNECTION:
   fotg210_writel(fotg210, temp | PORT_CSC, status_reg);
   break;
  case USB_PORT_FEAT_C_OVER_CURRENT:
   fotg210_writel(fotg210, temp | OTGISR_OVC,
     &fotg210->regs->otgisr);
   break;
  case USB_PORT_FEAT_C_RESET:
   /* GetPortStatus clears reset */
   break;
  default:
   goto error;
  }
  fotg210_readl(fotg210, &fotg210->regs->command);
  break;
 case GetHubDescriptor:
  fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *)
    buf);
  break;
 case GetHubStatus:
  /* no hub-wide feature/status flags */
  memset(buf, 0, 4);
  /*cpu_to_le32s ((u32 *) buf); */
  break;
 case GetPortStatus:
  if (!wIndex || wIndex > ports)
   goto error;
  wIndex--;
  status = 0;
  temp = fotg210_readl(fotg210, status_reg);

  /* wPortChange bits */
  if (temp & PORT_CSC)
   status |= USB_PORT_STAT_C_CONNECTION << 16;
  if (temp & PORT_PEC)
   status |= USB_PORT_STAT_C_ENABLE << 16;

  temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr);
  if (temp1 & OTGISR_OVC)
   status |= USB_PORT_STAT_C_OVERCURRENT << 16;

  /* whoever resumes must GetPortStatus to complete it!! */
  if (temp & PORT_RESUME) {

   /* Remote Wakeup received? */
   if (!fotg210->reset_done[wIndex]) {
    /* resume signaling for 20 msec */
    fotg210->reset_done[wIndex] = jiffies
      + msecs_to_jiffies(20);
    /* check the port again */
    mod_timer(&fotg210_to_hcd(fotg210)->rh_timer,
      fotg210->reset_done[wIndex]);
   }

   /* resume completed? */
   else if (time_after_eq(jiffies,
     fotg210->reset_done[wIndex])) {
    clear_bit(wIndex, &fotg210->suspended_ports);
    set_bit(wIndex, &fotg210->port_c_suspend);
    fotg210->reset_done[wIndex] = 0;

    /* stop resume signaling */
    temp = fotg210_readl(fotg210, status_reg);
    fotg210_writel(fotg210, temp &
      ~(PORT_RWC_BITS | PORT_RESUME),
      status_reg);
    clear_bit(wIndex, &fotg210->resuming_ports);
    retval = handshake(fotg210, status_reg,
      PORT_RESUME, 0, 2000);/* 2ms */
    if (retval != 0) {
     fotg210_err(fotg210,
       "port %d resume error %d\n",
       wIndex + 1, retval);
     goto error;
    }
    temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
   }
  }

  /* whoever resets must GetPortStatus to complete it!! */
  if ((temp & PORT_RESET) && time_after_eq(jiffies,
    fotg210->reset_done[wIndex])) {
   status |= USB_PORT_STAT_C_RESET << 16;
   fotg210->reset_done[wIndex] = 0;
   clear_bit(wIndex, &fotg210->resuming_ports);

   /* force reset to complete */
   fotg210_writel(fotg210,
     temp & ~(PORT_RWC_BITS | PORT_RESET),
     status_reg);
   /* REVISIT:  some hardware needs 550+ usec to clear
 * this bit; seems too long to spin routinely...
 */

   retval = handshake(fotg210, status_reg,
     PORT_RESET, 0, 1000);
   if (retval != 0) {
    fotg210_err(fotg210, "port %d reset error %d\n",
      wIndex + 1, retval);
    goto error;
   }

   /* see what we found out */
   temp = check_reset_complete(fotg210, wIndex, status_reg,
     fotg210_readl(fotg210, status_reg));

   /* restart schedule */
   fotg210->command |= CMD_RUN;
   fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command);
  }

  if (!(temp & (PORT_RESUME|PORT_RESET))) {
   fotg210->reset_done[wIndex] = 0;
   clear_bit(wIndex, &fotg210->resuming_ports);
  }

  /* transfer dedicated ports to the companion hc */
  if ((temp & PORT_CONNECT) &&
    test_bit(wIndex, &fotg210->companion_ports)) {
   temp &= ~PORT_RWC_BITS;
   fotg210_writel(fotg210, temp, status_reg);
   fotg210_dbg(fotg210, "port %d --> companion\n",
     wIndex + 1);
   temp = fotg210_readl(fotg210, status_reg);
  }

  /*
 * Even if OWNER is set, there's no harm letting hub_wq
 * see the wPortStatus values (they should all be 0 except
 * for PORT_POWER anyway).
 */


  if (temp & PORT_CONNECT) {
   status |= USB_PORT_STAT_CONNECTION;
   status |= fotg210_port_speed(fotg210, temp);
  }
  if (temp & PORT_PE)
   status |= USB_PORT_STAT_ENABLE;

  /* maybe the port was unsuspended without our knowledge */
  if (temp & (PORT_SUSPEND|PORT_RESUME)) {
   status |= USB_PORT_STAT_SUSPEND;
  } else if (test_bit(wIndex, &fotg210->suspended_ports)) {
   clear_bit(wIndex, &fotg210->suspended_ports);
   clear_bit(wIndex, &fotg210->resuming_ports);
   fotg210->reset_done[wIndex] = 0;
   if (temp & PORT_PE)
    set_bit(wIndex, &fotg210->port_c_suspend);
  }

  temp1 = fotg210_readl(fotg210, &fotg210->regs->otgisr);
  if (temp1 & OTGISR_OVC)
   status |= USB_PORT_STAT_OVERCURRENT;
  if (temp & PORT_RESET)
   status |= USB_PORT_STAT_RESET;
  if (test_bit(wIndex, &fotg210->port_c_suspend))
   status |= USB_PORT_STAT_C_SUSPEND << 16;

  if (status & ~0xffff) /* only if wPortChange is interesting */
   dbg_port(fotg210, "GetStatus", wIndex + 1, temp);
  put_unaligned_le32(status, buf);
  break;
 case SetHubFeature:
  switch (wValue) {
  case C_HUB_LOCAL_POWER:
  case C_HUB_OVER_CURRENT:
   /* no hub-wide feature/status flags */
   break;
  default:
   goto error;
  }
  break;
 case SetPortFeature:
  selector = wIndex >> 8;
  wIndex &= 0xff;

  if (!wIndex || wIndex > ports)
   goto error;
  wIndex--;
  temp = fotg210_readl(fotg210, status_reg);
  temp &= ~PORT_RWC_BITS;
  switch (wValue) {
  case USB_PORT_FEAT_SUSPEND:
   if ((temp & PORT_PE) == 0
     || (temp & PORT_RESET) != 0)
    goto error;

   /* After above check the port must be connected.
 * Set appropriate bit thus could put phy into low power
 * mode if we have hostpc feature
 */

   fotg210_writel(fotg210, temp | PORT_SUSPEND,
     status_reg);
   set_bit(wIndex, &fotg210->suspended_ports);
   break;
  case USB_PORT_FEAT_RESET:
   if (temp & PORT_RESUME)
    goto error;
   /* line status bits may report this as low speed,
 * which can be fine if this root hub has a
 * transaction translator built in.
 */

   fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1);
   temp |= PORT_RESET;
   temp &= ~PORT_PE;

   /*
 * caller must wait, then call GetPortStatus
 * usb 2.0 spec says 50 ms resets on root
 */

   fotg210->reset_done[wIndex] = jiffies
     + msecs_to_jiffies(50);
   fotg210_writel(fotg210, temp, status_reg);
   break;

  /* For downstream facing ports (these):  one hub port is put
 * into test mode according to USB2 11.24.2.13, then the hub
 * must be reset (which for root hub now means rmmod+modprobe,
 * or else system reboot).  See EHCI 2.3.9 and 4.14 for info
 * about the EHCI-specific stuff.
 */

  case USB_PORT_FEAT_TEST:
   if (!selector || selector > 5)
    goto error;
   spin_unlock_irqrestore(&fotg210->lock, flags);
   fotg210_quiesce(fotg210);
   spin_lock_irqsave(&fotg210->lock, flags);

   /* Put all enabled ports into suspend */
   temp = fotg210_readl(fotg210, status_reg) &
    ~PORT_RWC_BITS;
   if (temp & PORT_PE)
    fotg210_writel(fotg210, temp | PORT_SUSPEND,
      status_reg);

   spin_unlock_irqrestore(&fotg210->lock, flags);
   fotg210_halt(fotg210);
   spin_lock_irqsave(&fotg210->lock, flags);

   temp = fotg210_readl(fotg210, status_reg);
   temp |= selector << 16;
   fotg210_writel(fotg210, temp, status_reg);
   break;

  default:
   goto error;
  }
  fotg210_readl(fotg210, &fotg210->regs->command);
  break;

 default:
error:
  /* "stall" on error */
  retval = -EPIPE;
 }
 spin_unlock_irqrestore(&fotg210->lock, flags);
 return retval;
}

static void __maybe_unused fotg210_relinquish_port(struct usb_hcd *hcd,
  int portnum)
{
 return;
}

static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd,
  int portnum)
{
 return 0;
}

/* There's basically three types of memory:
 * - data used only by the HCD ... kmalloc is fine
 * - async and periodic schedules, shared by HC and HCD ... these
 *   need to use dma_pool or dma_alloc_coherent
 * - driver buffers, read/written by HC ... single shot DMA mapped
 *
 * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
 * No memory seen by this driver is pageable.
 */


/* Allocate the key transfer structures from the previously allocated pool */
static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210,
  struct fotg210_qtd *qtd, dma_addr_t dma)
{
 memset(qtd, 0, sizeof(*qtd));
 qtd->qtd_dma = dma;
 qtd->hw_token = cpu_to_hc32(fotg210, QTD_STS_HALT);
 qtd->hw_next = FOTG210_LIST_END(fotg210);
 qtd->hw_alt_next = FOTG210_LIST_END(fotg210);
 INIT_LIST_HEAD(&qtd->qtd_list);
}

static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210,
  gfp_t flags)
{
 struct fotg210_qtd *qtd;
 dma_addr_t dma;

 qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma);
 if (qtd != NULL)
  fotg210_qtd_init(fotg210, qtd, dma);

 return qtd;
}

static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210,
  struct fotg210_qtd *qtd)
{
 dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma);
}


static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
{
 /* clean qtds first, and know this is not linked */
 if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) {
  fotg210_dbg(fotg210, "unused qh not empty!\n");
  BUG();
 }
 if (qh->dummy)
  fotg210_qtd_free(fotg210, qh->dummy);
 dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma);
 kfree(qh);
}

static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
  gfp_t flags)
{
 struct fotg210_qh *qh;
 dma_addr_t dma;

 qh = kzalloc(sizeof(*qh), GFP_ATOMIC);
 if (!qh)
  goto done;
 qh->hw = (struct fotg210_qh_hw *)
  dma_pool_zalloc(fotg210->qh_pool, flags, &dma);
 if (!qh->hw)
  goto fail;
 qh->qh_dma = dma;
 INIT_LIST_HEAD(&qh->qtd_list);

 /* dummy td enables safe urb queuing */
 qh->dummy = fotg210_qtd_alloc(fotg210, flags);
 if (qh->dummy == NULL) {
  fotg210_dbg(fotg210, "no dummy td\n");
  goto fail1;
 }
done:
 return qh;
fail1:
 dma_pool_free(fotg210->qh_pool, qh->hw, qh->qh_dma);
fail:
 kfree(qh);
 return NULL;
}

/* The queue heads and transfer descriptors are managed from pools tied
 * to each of the "per device" structures.
 * This is the initialisation and cleanup code.
 */


static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210)
{
 if (fotg210->async)
  qh_destroy(fotg210, fotg210->async);
 fotg210->async = NULL;

 if (fotg210->dummy)
  qh_destroy(fotg210, fotg210->dummy);
 fotg210->dummy = NULL;

 /* DMA consistent memory and pools */
 dma_pool_destroy(fotg210->qtd_pool);
 fotg210->qtd_pool = NULL;

 dma_pool_destroy(fotg210->qh_pool);
 fotg210->qh_pool = NULL;

 dma_pool_destroy(fotg210->itd_pool);
 fotg210->itd_pool = NULL;

 if (fotg210->periodic)
  dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller,
    fotg210->periodic_size * sizeof(u32),
    fotg210->periodic, fotg210->periodic_dma);
 fotg210->periodic = NULL;

 /* shadow periodic table */
 kfree(fotg210->pshadow);
 fotg210->pshadow = NULL;
}

/* remember to add cleanup code (above) if you add anything here */
static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags)
{
 int i;

 /* QTDs for control/bulk/intr transfers */
 fotg210->qtd_pool = dma_pool_create("fotg210_qtd",
   fotg210_to_hcd(fotg210)->self.controller,
   sizeof(struct fotg210_qtd),
   32 /* byte alignment (for hw parts) */,
   4096 /* can't cross 4K */);
 if (!fotg210->qtd_pool)
  goto fail;

 /* QHs for control/bulk/intr transfers */
 fotg210->qh_pool = dma_pool_create("fotg210_qh",
   fotg210_to_hcd(fotg210)->self.controller,
   sizeof(struct fotg210_qh_hw),
   32 /* byte alignment (for hw parts) */,
   4096 /* can't cross 4K */);
 if (!fotg210->qh_pool)
  goto fail;

 fotg210->async = fotg210_qh_alloc(fotg210, flags);
 if (!fotg210->async)
  goto fail;

 /* ITD for high speed ISO transfers */
 fotg210->itd_pool = dma_pool_create("fotg210_itd",
   fotg210_to_hcd(fotg210)->self.controller,
   sizeof(struct fotg210_itd),
   64 /* byte alignment (for hw parts) */,
   4096 /* can't cross 4K */);
 if (!fotg210->itd_pool)
  goto fail;

 /* Hardware periodic table */
 fotg210->periodic =
  dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller,
    fotg210->periodic_size * sizeof(__le32),
    &fotg210->periodic_dma, 0);
 if (fotg210->periodic == NULL)
  goto fail;

 for (i = 0; i < fotg210->periodic_size; i++)
  fotg210->periodic[i] = FOTG210_LIST_END(fotg210);

 /* software shadow of hardware table */
 fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *),
   flags);
 if (fotg210->pshadow != NULL)
  return 0;

fail:
 fotg210_dbg(fotg210, "couldn't init memory\n");
 fotg210_mem_cleanup(fotg210);
 return -ENOMEM;
}
/* EHCI hardware queue manipulation ... the core.  QH/QTD manipulation.
 *
 * Control, bulk, and interrupt traffic all use "qh" lists.  They list "qtd"
 * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
 * buffers needed for the larger number).  We use one QH per endpoint, queue
 * multiple urbs (all three types) per endpoint.  URBs may need several qtds.
 *
 * ISO traffic uses "ISO TD" (itd) records, and (along with
 * interrupts) needs careful scheduling.  Performance improvements can be
 * an ongoing challenge.  That's in "ehci-sched.c".
 *
 * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
 * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
 * (b) special fields in qh entries or (c) split iso entries.  TTs will
 * buffer low/full speed data so the host collects it at high speed.
 */


/* fill a qtd, returning how much of the buffer we were able to queue up */
static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd,
  dma_addr_t buf, size_t len, int token, int maxpacket)
{
 int i, count;
 u64 addr = buf;

 /* one buffer entry per 4K ... first might be short or unaligned */
 qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr);
 qtd->hw_buf_hi[0] = cpu_to_hc32(fotg210, (u32)(addr >> 32));
 count = 0x1000 - (buf & 0x0fff); /* rest of that page */
 if (likely(len < count))  /* ... iff needed */
  count = len;
 else {
  buf +=  0x1000;
  buf &= ~0x0fff;

  /* per-qtd limit: from 16K to 20K (best alignment) */
  for (i = 1; count < len && i < 5; i++) {
   addr = buf;
   qtd->hw_buf[i] = cpu_to_hc32(fotg210, (u32)addr);
   qtd->hw_buf_hi[i] = cpu_to_hc32(fotg210,
     (u32)(addr >> 32));
   buf += 0x1000;
   if ((count + 0x1000) < len)
    count += 0x1000;
   else
    count = len;
  }

  /* short packets may only terminate transfers */
  if (count != len)
   count -= (count % maxpacket);
 }
 qtd->hw_token = cpu_to_hc32(fotg210, (count << 16) | token);
 qtd->length = count;

 return count;
}

static inline void qh_update(struct fotg210_hcd *fotg210,
  struct fotg210_qh *qh, struct fotg210_qtd *qtd)
{
 struct fotg210_qh_hw *hw = qh->hw;

 /* writes to an active overlay are unsafe */
 BUG_ON(qh->qh_state != QH_STATE_IDLE);

 hw->hw_qtd_next = QTD_NEXT(fotg210, qtd->qtd_dma);
 hw->hw_alt_next = FOTG210_LIST_END(fotg210);

 /* Except for control endpoints, we make hardware maintain data
 * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
 * and set the pseudo-toggle in udev. Only usb_clear_halt() will
 * ever clear it.
 */

 if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) {
  unsigned is_out, epnum;

  is_out = qh->is_out;
  epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f;
  if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) {
   hw->hw_token &= ~cpu_to_hc32(fotg210, QTD_TOGGLE);
   usb_settoggle(qh->dev, epnum, is_out, 1);
  }
 }

 hw->hw_token &= cpu_to_hc32(fotg210, QTD_TOGGLE | QTD_STS_PING);
}

/* if it weren't for a common silicon quirk (writing the dummy into the qh
 * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
 * recovery (including urb dequeue) would need software changes to a QH...
 */

static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh)
{
 struct fotg210_qtd *qtd;

 if (list_empty(&qh->qtd_list))
  qtd = qh->dummy;
 else {
  qtd = list_entry(qh->qtd_list.next,
    struct fotg210_qtd, qtd_list);
  /*
 * first qtd may already be partially processed.
 * If we come here during unlink, the QH overlay region
 * might have reference to the just unlinked qtd. The
 * qtd is updated in qh_completions(). Update the QH
 * overlay here.
 */

  if (cpu_to_hc32(fotg210, qtd->qtd_dma) == qh->hw->hw_current) {
   qh->hw->hw_qtd_next = qtd->hw_next;
   qtd = NULL;
  }
 }

 if (qtd)
  qh_update(fotg210, qh, qtd);
}

static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh);

static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd,
  struct usb_host_endpoint *ep)
{
 struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
 struct fotg210_qh *qh = ep->hcpriv;
 unsigned long flags;

 spin_lock_irqsave(&fotg210->lock, flags);
 qh->clearing_tt = 0;
 if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
   && fotg210->rh_state == FOTG210_RH_RUNNING)
  qh_link_async(fotg210, qh);
 spin_unlock_irqrestore(&fotg210->lock, flags);
}

static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210,
  struct fotg210_qh *qh, struct urb *urb, u32 token)
{

 /* If an async split transaction gets an error or is unlinked,
 * the TT buffer may be left in an indeterminate state.  We
 * have to clear the TT buffer.
 *
 * Note: this routine is never called for Isochronous transfers.
 */

 if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
  struct usb_device *tt = urb->dev->tt->hub;

  dev_dbg(&tt->dev,
    "clear tt buffer port %d, a%d ep%d t%08x\n",
    urb->dev->ttport, urb->dev->devnum,
    usb_pipeendpoint(urb->pipe), token);

  if (urb->dev->tt->hub !=
    fotg210_to_hcd(fotg210)->self.root_hub) {
   if (usb_hub_clear_tt_buffer(urb) == 0)
    qh->clearing_tt = 1;
  }
 }
}

static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb,
  size_t length, u32 token)
{
 int status = -EINPROGRESS;

 /* count IN/OUT bytes, not SETUP (even short packets) */
 if (likely(QTD_PID(token) != 2))
  urb->actual_length += length - QTD_LENGTH(token);

 /* don't modify error codes */
 if (unlikely(urb->unlinked))
  return status;

 /* force cleanup after short read; not always an error */
 if (unlikely(IS_SHORT_READ(token)))
  status = -EREMOTEIO;

 /* serious "can't proceed" faults reported by the hardware */
 if (token & QTD_STS_HALT) {
  if (token & QTD_STS_BABBLE) {
   /* FIXME "must" disable babbling device's port too */
   status = -EOVERFLOW;
  /* CERR nonzero + halt --> stall */
  } else if (QTD_CERR(token)) {
   status = -EPIPE;

  /* In theory, more than one of the following bits can be set
 * since they are sticky and the transaction is retried.
 * Which to test first is rather arbitrary.
 */

  } else if (token & QTD_STS_MMF) {
   /* fs/ls interrupt xfer missed the complete-split */
   status = -EPROTO;
  } else if (token & QTD_STS_DBE) {
   status = (QTD_PID(token) == 1) /* IN ? */
    ? -ENOSR  /* hc couldn't read data */
    : -ECOMM; /* hc couldn't write data */
  } else if (token & QTD_STS_XACT) {
   /* timeout, bad CRC, wrong PID, etc */
   fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n",
     urb->dev->devpath,
     usb_pipeendpoint(urb->pipe),
     usb_pipein(urb->pipe) ? "in" : "out");
   status = -EPROTO;
  } else { /* unknown */
   status = -EPROTO;
  }

  fotg210_dbg(fotg210,
    "dev%d ep%d%s qtd token %08x --> status %d\n",
    usb_pipedevice(urb->pipe),
    usb_pipeendpoint(urb->pipe),
    usb_pipein(urb->pipe) ? "in" : "out",
    token, status);
 }

 return status;
}

static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb,
  int status)
__releases(fotg210->lock)
__acquires(fotg210->lock)
{
 if (likely(urb->hcpriv != NULL)) {
  struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv;

  /* S-mask in a QH means it's an interrupt urb */
  if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) {

   /* ... update hc-wide periodic stats (for usbfs) */
   fotg210_to_hcd(fotg210)->self.bandwidth_int_reqs--;
  }
 }

 if (unlikely(urb->unlinked)) {
  INCR(fotg210->stats.unlink);
 } else {
  /* report non-error and short read status as zero */
  if (status == -EINPROGRESS || status == -EREMOTEIO)
   status = 0;
  INCR(fotg210->stats.complete);
 }

#ifdef FOTG210_URB_TRACE
 fotg210_dbg(fotg210,
   "%s %s urb %p ep%d%s status %d len %d/%d\n",
   __func__, urb->dev->devpath, urb,
   usb_pipeendpoint(urb->pipe),
   usb_pipein(urb->pipe) ? "in" : "out",
   status,
   urb->actual_length, urb->transfer_buffer_length);
#endif

 /* complete() can reenter this HCD */
 usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
 spin_unlock(&fotg210->lock);
 usb_hcd_giveback_urb(fotg210_to_hcd(fotg210), urb, status);
 spin_lock(&fotg210->lock);
}

static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh);

/* Process and free completed qtds for a qh, returning URBs to drivers.
 * Chases up to qh->hw_current.  Returns number of completions called,
 * indicating how much "real" work we did.
 */

static unsigned qh_completions(struct fotg210_hcd *fotg210,
  struct fotg210_qh *qh)
{
 struct fotg210_qtd *last, *end = qh->dummy;
 struct fotg210_qtd *qtd, *tmp;
 int last_status;
 int stopped;
 unsigned count = 0;
 u8 state;
 struct fotg210_qh_hw *hw = qh->hw;

 if (unlikely(list_empty(&qh->qtd_list)))
  return count;

 /* completions (or tasks on other cpus) must never clobber HALT
 * till we've gone through and cleaned everything up, even when
 * they add urbs to this qh's queue or mark them for unlinking.
 *
 * NOTE:  unlinking expects to be done in queue order.
 *
 * It's a bug for qh->qh_state to be anything other than
 * QH_STATE_IDLE, unless our caller is scan_async() or
 * scan_intr().
 */

 state = qh->qh_state;
 qh->qh_state = QH_STATE_COMPLETING;
 stopped = (state == QH_STATE_IDLE);

rescan:
 last = NULL;
 last_status = -EINPROGRESS;
 qh->needs_rescan = 0;

 /* remove de-activated QTDs from front of queue.
 * after faults (including short reads), cleanup this urb
 * then let the queue advance.
 * if queue is stopped, handles unlinks.
 */

 list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) {
  struct urb *urb;
  u32 token = 0;

  urb = qtd->urb;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=92 G=92

¤ Dauer der Verarbeitung: 0.51 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.