Commit 2a339670 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: OHCI resume/reset stops deadlocking in PM code

System-wide PM resume now happily deadlocks if one of the
resuming devices tries to remove devices which vanished
during the suspend(*).  IMO that's unreasonable both
because devices can/do vanish, and because 2.4 didn't
deadlock in those cases; but no patch to fix that has been
merged.  The result is that ever since merging the "new" PM
code, some OHCI-based systems deadlock on resume.

So this patch handles the "lost power during resume" case
differently:  it doesn't disconnect the root hub (or its
children) directly.  Instead, it does part of that work
immediately, and defers the rest to khubd:

  - add a "pending" list for live urbs, and use it after reset
    to abort pending URBs (and reclaim "live" EDs/TDs)
  - immediately mark all devices NOTATTACHED, so any operations
    on the devices before khubd handles the disconnects, including
    resume() callbacks, will fail
  - kick root hub so it can do the cleanup

It also handles "fminterval" init/reinit a bit better, mostly
to work better in some remote wakeup scenarios addressed in
later patches:

   - save any initial value the boot firmware provided
   - use it during initialization (and eventually, remote wakeup)

Other changes:

  - use better jiffies calculation for scheduled delays
  - the allocator does more of the one-time initialization
  - initialize hcd.can_wakeup according to boot firmware
  - move some inlines to the header
  - minor cleanups


(*) http://marc.theaimsgroup.com/?l=linux-kernel&m=106606272103414&w=2
     reported against 2.6.0-test7.
parent ec0e3558
...@@ -118,19 +118,12 @@ ...@@ -118,19 +118,12 @@
/* For initializing controller (mask in an HCFS mode too) */ /* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_UNLINK_TIMEOUT (HZ / 10)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ohci_hcd"; static const char hcd_name [] = "ohci_hcd";
#include "ohci.h" #include "ohci.h"
static inline void disable (struct ohci_hcd *ohci)
{
ohci->hcd.state = USB_STATE_HALT;
}
#include "ohci-hub.c" #include "ohci-hub.c"
#include "ohci-dbg.c" #include "ohci-dbg.c"
#include "ohci-mem.c" #include "ohci-mem.c"
...@@ -206,8 +199,7 @@ static int ohci_urb_enqueue ( ...@@ -206,8 +199,7 @@ static int ohci_urb_enqueue (
if (!urb_priv) if (!urb_priv)
return -ENOMEM; return -ENOMEM;
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
INIT_LIST_HEAD (&urb_priv->pending);
/* fill the private part of the URB */
urb_priv->length = size; urb_priv->length = size;
urb_priv->ed = ed; urb_priv->ed = ed;
...@@ -397,6 +389,16 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -397,6 +389,16 @@ static int hc_reset (struct ohci_hcd *ohci)
{ {
u32 temp; u32 temp;
/* boot firmware should have set this up (5.1.1.3.1) */
if (!ohci->fminterval) {
temp = readl (&ohci->regs->fminterval);
if (temp & 0x3fff0000)
ohci->fminterval = temp;
else
ohci->fminterval = DEFAULT_FMINTERVAL;
/* also: power/overcurrent flags in roothub.a */
}
/* SMM owns the HC? not for long! /* SMM owns the HC? not for long!
* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. * On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
*/ */
...@@ -413,7 +415,7 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -413,7 +415,7 @@ static int hc_reset (struct ohci_hcd *ohci)
writel (OHCI_INTR_OC, &ohci->regs->intrenable); writel (OHCI_INTR_OC, &ohci->regs->intrenable);
writel (OHCI_OCR, &ohci->regs->cmdstatus); writel (OHCI_OCR, &ohci->regs->cmdstatus);
while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
wait_ms (10); msec_delay (10);
if (--temp == 0) { if (--temp == 0) {
ohci_err (ohci, "USB HC TakeOver failed!\n"); ohci_err (ohci, "USB HC TakeOver failed!\n");
return -1; return -1;
...@@ -430,9 +432,12 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -430,9 +432,12 @@ static int hc_reset (struct ohci_hcd *ohci)
/* Reset USB (needed by some controllers); RemoteWakeupConnected /* Reset USB (needed by some controllers); RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected * saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/ */
ohci->hc_control = readl (&ohci->regs->control); ohci->hc_control = readl (&ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
if (ohci->hc_control)
ohci->hcd.can_wakeup = 1;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
if (power_switching) { if (power_switching) {
unsigned ports = roothub_a (ohci) & RH_A_NDP; unsigned ports = roothub_a (ohci) & RH_A_NDP;
...@@ -444,7 +449,7 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -444,7 +449,7 @@ static int hc_reset (struct ohci_hcd *ohci)
} }
// flush those pci writes // flush those pci writes
(void) readl (&ohci->regs->control); (void) readl (&ohci->regs->control);
wait_ms (50); msec_delay (50);
/* HC Reset requires max 10 us delay */ /* HC Reset requires max 10 us delay */
writel (OHCI_HCR, &ohci->regs->cmdstatus); writel (OHCI_HCR, &ohci->regs->cmdstatus);
...@@ -473,9 +478,6 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -473,9 +478,6 @@ static int hc_reset (struct ohci_hcd *ohci)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#define FI 0x2edf /* 12000 bits per frame (-1) */
#define LSTHRESH 0x628 /* lowspeed bit threshold */
/* Start an OHCI controller, set the BUS operational /* Start an OHCI controller, set the BUS operational
* enable interrupts * enable interrupts
* connect the virtual root hub * connect the virtual root hub
...@@ -486,7 +488,6 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -486,7 +488,6 @@ static int hc_start (struct ohci_hcd *ohci)
struct usb_device *udev; struct usb_device *udev;
struct usb_bus *bus; struct usb_bus *bus;
spin_lock_init (&ohci->lock);
disable (ohci); disable (ohci);
/* Tell the controller where the control and bulk lists are /* Tell the controller where the control and bulk lists are
...@@ -497,12 +498,7 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -497,12 +498,7 @@ static int hc_start (struct ohci_hcd *ohci)
/* a reset clears this */ /* a reset clears this */
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
/* force default fmInterval (we won't adjust it); init thresholds periodic_reinit (ohci);
* for last FS and LS packets, reserve 90% for periodic.
*/
writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval);
writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
writel (LSTHRESH, &ohci->regs->lsthresh);
/* some OHCI implementations are finicky about how they init. /* some OHCI implementations are finicky about how they init.
* bogus values here mean not even enumeration could work. * bogus values here mean not even enumeration could work.
...@@ -551,9 +547,14 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -551,9 +547,14 @@ static int hc_start (struct ohci_hcd *ohci)
// POTPGT delay is bits 24-31, in 2 ms units. // POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe); mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
bus = hcd_to_bus (&ohci->hcd);
if (bus->root_hub) {
ohci->hcd.state = USB_STATE_RUNNING;
return 0;
}
/* connect the virtual root hub */ /* connect the virtual root hub */
bus = hcd_to_bus (&ohci->hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0);
ohci->hcd.state = USB_STATE_RUNNING; ohci->hcd.state = USB_STATE_RUNNING;
if (!udev) { if (!udev) {
...@@ -670,21 +671,67 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -670,21 +671,67 @@ static void ohci_stop (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
// FIXME: this restart logic should be generic,
// and handle full hcd state cleanup
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */ /* must not be called from interrupt context */
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void mark_children_gone (struct usb_device *dev)
{
unsigned i;
for (i = 0; i < dev->maxchild; i++) {
if (dev->children [i] == 0)
continue;
dev->children [i]->state = USB_STATE_NOTATTACHED;
mark_children_gone (dev->children [i]);
}
}
static int hc_restart (struct ohci_hcd *ohci) static int hc_restart (struct ohci_hcd *ohci)
{ {
int temp; int temp;
int i; int i;
struct urb_priv *priv;
/* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away.
* later, khubd disconnect processing will recycle the other state,
* (either as disconnect/reconnect, or maybe someday as a reset).
*/
spin_lock_irq(&ohci->lock);
disable (ohci); disable (ohci);
if (hcd_to_bus (&ohci->hcd)->root_hub) mark_children_gone (ohci->hcd.self.root_hub);
usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub); if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
struct urb *urb = priv->td[0]->urb;
struct ed *ed = priv->ed;
switch (ed->state) {
case ED_OPER:
ed->state = ED_UNLINK;
ed->hwINFO |= ED_DEQUEUE;
ed_deschedule (ohci, ed);
ed->ed_next = ohci->ed_rm_list;
ed->ed_prev = 0;
ohci->ed_rm_list = ed;
/* FALLTHROUGH */
case ED_UNLINK:
break;
default:
ohci_dbg(ohci, "bogus ed %p state %d\n",
ed, ed->state);
}
spin_lock (&urb->lock);
urb->status = -ESHUTDOWN;
spin_unlock (&urb->lock);
}
finish_unlinks (ohci, 0, 0);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
/* empty the interrupt branches */ /* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
...@@ -700,8 +747,20 @@ static int hc_restart (struct ohci_hcd *ohci) ...@@ -700,8 +747,20 @@ static int hc_restart (struct ohci_hcd *ohci)
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp); ohci_err (ohci, "can't restart, %d\n", temp);
return temp; return temp;
} else } else {
/* here we "know" root ports should always stay powered,
* and that if we try to turn them back on the root hub
* will respond to CSC processing.
*/
i = roothub_a (ohci) & RH_A_NDP;
while (i--)
writel (RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
ohci->hcd.self.root_hub->dev.power.power_state = 0;
ohci->hcd.state = USB_STATE_RUNNING;
ohci_dbg (ohci, "restart complete\n"); ohci_dbg (ohci, "restart complete\n");
ohci_dump (ohci, 1);
}
return 0; return 0;
} }
#endif #endif
......
...@@ -31,6 +31,8 @@ static struct usb_hcd *ohci_hcd_alloc (void) ...@@ -31,6 +31,8 @@ static struct usb_hcd *ohci_hcd_alloc (void)
if (ohci != 0) { if (ohci != 0) {
memset (ohci, 0, sizeof (struct ohci_hcd)); memset (ohci, 0, sizeof (struct ohci_hcd));
ohci->hcd.product_desc = "OHCI Host Controller"; ohci->hcd.product_desc = "OHCI Host Controller";
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
return &ohci->hcd; return &ohci->hcd;
} }
return 0; return 0;
......
...@@ -22,6 +22,7 @@ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv) ...@@ -22,6 +22,7 @@ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
} }
} }
list_del (&urb_priv->pending);
kfree (urb_priv); kfree (urb_priv);
} }
...@@ -419,7 +420,7 @@ static struct ed *ed_get ( ...@@ -419,7 +420,7 @@ static struct ed *ed_get (
} }
/* NOTE: only ep0 currently needs this "re"init logic, during /* NOTE: only ep0 currently needs this "re"init logic, during
* enumeration (after set_address, or if ep0 maxpacket >8). * enumeration (after set_address).
*/ */
if (ed->state == ED_IDLE) { if (ed->state == ED_IDLE) {
u32 info; u32 info;
...@@ -593,6 +594,7 @@ static void td_submit_urb ( ...@@ -593,6 +594,7 @@ static void td_submit_urb (
} }
urb_priv->td_cnt = 0; urb_priv->td_cnt = 0;
list_add (&urb_priv->pending, &ohci->pending);
if (data_len) if (data_len)
data = urb->transfer_dma; data = urb->transfer_dma;
......
...@@ -318,8 +318,9 @@ struct ohci_regs { ...@@ -318,8 +318,9 @@ struct ohci_regs {
/* hcd-private per-urb state */ /* hcd-private per-urb state */
typedef struct urb_priv { typedef struct urb_priv {
struct ed *ed; struct ed *ed;
__u16 length; // # tds in this request u16 length; // # tds in this request
__u16 td_cnt; // tds already serviced u16 td_cnt; // tds already serviced
struct list_head pending;
struct td *td [0]; // all TDs in this request struct td *td [0]; // all TDs in this request
} urb_priv_t; } urb_priv_t;
...@@ -364,12 +365,14 @@ struct ohci_hcd { ...@@ -364,12 +365,14 @@ struct ohci_hcd {
struct dma_pool *td_cache; struct dma_pool *td_cache;
struct dma_pool *ed_cache; struct dma_pool *ed_cache;
struct td *td_hash [TD_HASH_SIZE]; struct td *td_hash [TD_HASH_SIZE];
struct list_head pending;
/* /*
* driver state * driver state
*/ */
int load [NUM_INTS]; int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */ u32 hc_control; /* copy of hc control reg */
u32 fminterval; /* saved register */
unsigned long flags; /* for HC bugs */ unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
...@@ -386,6 +389,32 @@ struct ohci_hcd { ...@@ -386,6 +389,32 @@ struct ohci_hcd {
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static inline void disable (struct ohci_hcd *ohci)
{
ohci->hcd.state = USB_STATE_HALT;
}
#define MSEC_TO_JIFFIES(msec) ((HZ * (msec) + 999) / 1000)
static inline void msec_delay(int msec)
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MSEC_TO_JIFFIES(msec));
}
#define FI 0x2edf /* 12000 bits per frame (-1) */
#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI)
#define LSTHRESH 0x628 /* lowspeed bit threshold */
static inline void periodic_reinit (struct ohci_hcd *ohci)
{
writel (ohci->fminterval, &ohci->regs->fminterval);
writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
writel (LSTHRESH, &ohci->regs->lsthresh);
}
/*-------------------------------------------------------------------------*/
#ifndef DEBUG #ifndef DEBUG
#define STUB_DEBUG_FILES #define STUB_DEBUG_FILES
#endif /* DEBUG */ #endif /* DEBUG */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment