Commit 95ec612c authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: usb hcd states

AFAICT this is ready for your next merge with Linus.

Ben's ohci stuff was not cooked yet, seemed like the
pm stuff wasn't yet supporting the hook(s) he needed.
RMK had similar issues w.r.t. PM on ARM too.


This patch includes:

   - updates from Benjamin Herrenschmidt to make usbcore
     behave a bit better during PM suspend (setting and
     checking hcd state).

   - related updates from me, making more paths into hcds
     fail when the driver is suspended.

   - updates based on some feedback from Alan Stern,
     notably including getting rid of a state we don't
     really need (most of the patch, by volume).

   - an experiment that tries to give a warning in the
     sadly common case of ACPI or APIC (etc) settings
     that need to change before USB works.

Net effect is that some of the PM issues start to get
resolved, maybe IRQ problems will be diagnosed quicker,
and some overdue cleanup gets started.
parent 83744de4
...@@ -139,6 +139,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ...@@ -139,6 +139,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
return retval; return retval;
} }
} }
// hcd zeroed everything
hcd->regs = base; hcd->regs = base;
hcd->region = region; hcd->region = region;
...@@ -165,6 +166,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ...@@ -165,6 +166,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
dev_err (hcd->controller, "can't reset\n"); dev_err (hcd->controller, "can't reset\n");
goto clean_3; goto clean_3;
} }
hcd->state = USB_STATE_HALT;
pci_set_master (dev); pci_set_master (dev);
#ifndef __sparc__ #ifndef __sparc__
...@@ -230,7 +232,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev) ...@@ -230,7 +232,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
BUG (); BUG ();
hub = hcd->self.root_hub; hub = hcd->self.root_hub;
hcd->state = USB_STATE_QUIESCING; if (HCD_IS_RUNNING (hcd->state))
hcd->state = USB_STATE_QUIESCING;
dev_dbg (hcd->controller, "roothub graceful disconnect\n"); dev_dbg (hcd->controller, "roothub graceful disconnect\n");
usb_disconnect (&hub); usb_disconnect (&hub);
...@@ -287,8 +290,8 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) ...@@ -287,8 +290,8 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
pci_save_state (dev, hcd->pci_state); pci_save_state (dev, hcd->pci_state);
/* driver may want to disable DMA etc */ /* driver may want to disable DMA etc */
hcd->state = USB_STATE_QUIESCING;
retval = hcd->driver->suspend (hcd, state); retval = hcd->driver->suspend (hcd, state);
hcd->state = USB_STATE_SUSPENDED;
} }
pci_set_power_state (dev, state); pci_set_power_state (dev, state);
......
...@@ -1099,6 +1099,8 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1099,6 +1099,8 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
static int hcd_get_frame_number (struct usb_device *udev) static int hcd_get_frame_number (struct usb_device *udev)
{ {
struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv; struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
if (!HCD_IS_RUNNING (hcd->state))
return -ESHUTDOWN;
return hcd->driver->get_frame_number (hcd); return hcd->driver->get_frame_number (hcd);
} }
...@@ -1195,6 +1197,12 @@ static int hcd_unlink_urb (struct urb *urb) ...@@ -1195,6 +1197,12 @@ static int hcd_unlink_urb (struct urb *urb)
goto done; goto done;
} }
/* running ~= hc unlink handshake works (irq, timer, etc)
* halted ~= no unlink handshake is needed
* suspended, resuming == should never happen
*/
WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
if (!urb->hcpriv) { if (!urb->hcpriv) {
retval = -EINVAL; retval = -EINVAL;
goto done; goto done;
...@@ -1210,6 +1218,17 @@ static int hcd_unlink_urb (struct urb *urb) ...@@ -1210,6 +1218,17 @@ static int hcd_unlink_urb (struct urb *urb)
goto done; goto done;
} }
/* PCI IRQ setup can easily be broken so that USB controllers
* never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address().
*/
if (!hcd->saw_irq) {
dev_warn (hcd->controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help."
"\n");
hcd->saw_irq = 1;
}
/* maybe set up to block until the urb's completion fires. the /* maybe set up to block until the urb's completion fires. the
* lower level hcd code is always async, locking on urb->status * lower level hcd code is always async, locking on urb->status
* updates; an intercepted completion unblocks us. * updates; an intercepted completion unblocks us.
...@@ -1289,6 +1308,8 @@ static void hcd_endpoint_disable (struct usb_device *udev, int endpoint) ...@@ -1289,6 +1308,8 @@ static void hcd_endpoint_disable (struct usb_device *udev, int endpoint)
dev = udev->hcpriv; dev = udev->hcpriv;
hcd = udev->bus->hcpriv; hcd = udev->bus->hcpriv;
WARN_ON (!HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_HALT);
local_irq_disable (); local_irq_disable ();
rescan: rescan:
...@@ -1485,6 +1506,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) ...@@ -1485,6 +1506,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
return IRQ_NONE; return IRQ_NONE;
hcd->saw_irq = 1;
hcd->driver->irq (hcd, r); hcd->driver->irq (hcd, r);
if (hcd->state != start && hcd->state == USB_STATE_HALT) if (hcd->state != start && hcd->state == USB_STATE_HALT)
usb_hc_died (hcd); usb_hc_died (hcd);
......
...@@ -73,6 +73,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ ...@@ -73,6 +73,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
* hardware info/state * hardware info/state
*/ */
struct hc_driver *driver; /* hw-specific hooks */ struct hc_driver *driver; /* hw-specific hooks */
unsigned saw_irq : 1;
int irq; /* irq allocated */ int irq; /* irq allocated */
void *regs; /* device memory/io */ void *regs; /* device memory/io */
struct device *controller; /* handle to hardware */ struct device *controller; /* handle to hardware */
...@@ -89,13 +90,11 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ ...@@ -89,13 +90,11 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
int state; int state;
# define __ACTIVE 0x01 # define __ACTIVE 0x01
# define __SLEEPY 0x02
# define __SUSPEND 0x04 # define __SUSPEND 0x04
# define __TRANSIENT 0x80 # define __TRANSIENT 0x80
# define USB_STATE_HALT 0 # define USB_STATE_HALT 0
# define USB_STATE_RUNNING (__ACTIVE) # define USB_STATE_RUNNING (__ACTIVE)
# define USB_STATE_READY (__ACTIVE|__SLEEPY)
# define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE) # define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
# define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT) # define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT)
# define USB_STATE_SUSPENDED (__SUSPEND) # define USB_STATE_SUSPENDED (__SUSPEND)
......
...@@ -231,7 +231,6 @@ static void ehci_ready (struct ehci_hcd *ehci) ...@@ -231,7 +231,6 @@ static void ehci_ready (struct ehci_hcd *ehci)
ehci->hcd.state = USB_STATE_HALT; ehci->hcd.state = USB_STATE_HALT;
return; return;
} }
ehci->hcd.state = USB_STATE_READY;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -481,7 +480,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -481,7 +480,7 @@ static int ehci_start (struct usb_hcd *hcd)
ehci->reboot_notifier.notifier_call = ehci_reboot; ehci->reboot_notifier.notifier_call = ehci_reboot;
register_reboot_notifier (&ehci->reboot_notifier); register_reboot_notifier (&ehci->reboot_notifier);
ehci->hcd.state = USB_STATE_READY; ehci->hcd.state = USB_STATE_RUNNING;
writel (FLAG_CF, &ehci->regs->configured_flag); writel (FLAG_CF, &ehci->regs->configured_flag);
readl (&ehci->regs->command); /* unblock posted write */ readl (&ehci->regs->command); /* unblock posted write */
...@@ -625,7 +624,7 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -625,7 +624,7 @@ static int ehci_resume (struct usb_hcd *hcd)
/* resume HC and each port */ /* resume HC and each port */
// restore pci FLADJ value // restore pci FLADJ value
// khubd and drivers will set HC running, if needed; // khubd and drivers will set HC running, if needed;
hcd->state = USB_STATE_READY; hcd->state = USB_STATE_RUNNING;
// FIXME Philips/Intel/... etc don't really have a "READY" // FIXME Philips/Intel/... etc don't really have a "READY"
// state ... turn on CMD_RUN too // state ... turn on CMD_RUN too
for (i = 0; i < ports; i++) { for (i = 0; i < ports; i++) {
......
...@@ -529,7 +529,7 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -529,7 +529,7 @@ static int hc_start (struct ohci_hcd *ohci)
/* connect the virtual root hub */ /* connect the virtual root hub */
bus = hcd_to_bus (&ohci->hcd); bus = hcd_to_bus (&ohci->hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus); bus->root_hub = udev = usb_alloc_dev (NULL, bus);
ohci->hcd.state = USB_STATE_READY; ohci->hcd.state = USB_STATE_RUNNING;
if (!udev) { if (!udev) {
disable (ohci); disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci->hc_control &= ~OHCI_CTRL_HCFS;
......
...@@ -267,7 +267,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd) ...@@ -267,7 +267,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
if (ohci->ed_bulktail) if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE; ohci->hc_control |= OHCI_CTRL_BLE;
} }
hcd->state = USB_STATE_READY; hcd->state = USB_STATE_RUNNING;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
/* trigger a start-frame interrupt (why?) */ /* trigger a start-frame interrupt (why?) */
......
...@@ -2099,7 +2099,7 @@ static void start_hc(struct uhci_hcd *uhci) ...@@ -2099,7 +2099,7 @@ static void start_hc(struct uhci_hcd *uhci)
uhci->state_end = jiffies + HZ; uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
uhci->hcd.state = USB_STATE_READY; uhci->hcd.state = USB_STATE_RUNNING;
} }
/* /*
...@@ -2452,7 +2452,7 @@ static int uhci_resume(struct usb_hcd *hcd) ...@@ -2452,7 +2452,7 @@ static int uhci_resume(struct usb_hcd *hcd)
reset_hc(uhci); reset_hc(uhci);
start_hc(uhci); start_hc(uhci);
} }
uhci->hcd.state = USB_STATE_READY; uhci->hcd.state = USB_STATE_RUNNING;
return 0; return 0;
} }
#endif #endif
......
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