/*-------------------------------------------------------------------------*/
-static void hcd_pci_release(struct usb_bus *bus)
-{
- struct usb_hcd *hcd = bus->hcpriv;
-
- if (hcd)
- hcd->driver->hcd_free(hcd);
-}
-
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
if (pci_enable_device (dev) < 0)
return -ENODEV;
+ dev->current_state = 0;
+ dev->dev.power.power_state = 0;
if (!dev->irq) {
dev_err (&dev->dev,
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
pci_name(dev));
- return -ENODEV;
+ retval = -ENODEV;
+ goto done;
}
if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
len = pci_resource_len (dev, 0);
if (!request_mem_region (resource, len, driver->description)) {
dev_dbg (&dev->dev, "controller already in use\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto done;
}
base = ioremap_nocache (resource, len);
if (base == NULL) {
release_mem_region (resource, len);
dev_err (&dev->dev, "init %s fail, %d\n",
pci_name(dev), retval);
- return retval;
+ goto done;
}
} else { // UHCI
}
if (region == PCI_ROM_RESOURCE) {
dev_dbg (&dev->dev, "no i/o regions available\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto done;
}
base = (void __iomem *) resource;
}
// driver->reset(), later on, will transfer device from
// control by SMM/BIOS to control by Linux (if needed)
- hcd = driver->hcd_alloc ();
+ hcd = usb_create_hcd (driver);
if (hcd == NULL){
dev_dbg (&dev->dev, "hcd alloc fail\n");
retval = -ENOMEM;
release_region (resource, len);
dev_err (&dev->dev, "init %s fail, %d\n",
pci_name(dev), retval);
- return retval;
+ goto done;
}
}
// hcd zeroed everything
hcd->region = region;
pci_set_drvdata (dev, hcd);
- hcd->driver = driver;
- hcd->description = driver->description;
hcd->self.bus_name = pci_name(dev);
#ifdef CONFIG_PCI_NAMES
hcd->product_desc = dev->pretty_name;
-#else
- if (hcd->product_desc == NULL)
- hcd->product_desc = "USB Host Controller";
#endif
hcd->self.controller = &dev->dev;
if ((retval = hcd_buffer_create (hcd)) != 0) {
clean_3:
- driver->hcd_free (hcd);
+ pci_set_drvdata (dev, NULL);
+ usb_put_hcd (hcd);
goto clean_2;
}
dev_err (hcd->self.controller, "can't reset\n");
goto clean_3;
}
- hcd->state = USB_STATE_HALT;
pci_set_master (dev);
#ifndef __sparc__
bufp = __irq_itoa(dev->irq);
#endif
retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
- hcd->description, hcd);
+ hcd->driver->description, hcd);
if (retval != 0) {
dev_err (hcd->self.controller,
"request interrupt %s failed\n", bufp);
}
hcd->irq = dev->irq;
- dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp,
+ dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp,
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
- base);
-
- usb_bus_init (&hcd->self);
- hcd->self.op = &usb_hcd_operations;
- hcd->self.hcpriv = (void *) hcd;
- hcd->self.release = &hcd_pci_release;
- init_timer (&hcd->rh_timer);
-
- INIT_LIST_HEAD (&hcd->dev_list);
+ resource);
usb_register_bus (&hcd->self);
usb_hcd_pci_remove (dev);
}
+done:
+ if (retval != 0)
+ pci_disable_device (dev);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_probe);
}
usb_deregister_bus (&hcd->self);
+
+ pci_disable_device(dev);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
#ifdef CONFIG_PM
+static char __attribute_used__ *pci_state(u32 state)
+{
+ switch (state) {
+ case 0: return "D0";
+ case 1: return "D1";
+ case 2: return "D2";
+ case 3: return "D3hot";
+ case 4: return "D3cold";
+ }
+ return NULL;
+}
+
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
* PM-sensitive HCDs may already have done this.
*/
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- if (has_pci_pm)
- dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n",
- dev->current_state, state);
+ if (state > 4)
+ state = 4;
switch (hcd->state) {
- case USB_STATE_HALT:
- dev_dbg (hcd->self.controller, "halted; hcd not suspended\n");
- break;
- case HCD_STATE_SUSPENDED:
- dev_dbg (hcd->self.controller, "hcd already suspended\n");
- break;
- default:
+
+ /* entry if root hub wasn't yet suspended ... from sysfs,
+ * without autosuspend, or if USB_SUSPEND isn't configured.
+ */
+ case USB_STATE_RUNNING:
+ hcd->state = USB_STATE_QUIESCING;
retval = hcd->driver->suspend (hcd, state);
- if (retval)
+ if (retval) {
dev_dbg (hcd->self.controller,
"suspend fail, retval %d\n",
retval);
- else {
- hcd->state = HCD_STATE_SUSPENDED;
- pci_save_state (dev, hcd->pci_state);
+ break;
+ }
+ hcd->state = HCD_STATE_SUSPENDED;
+ /* FALLTHROUGH */
+
+ /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the
+ * controller and/or root hub will already have been suspended,
+ * but it won't be ready for a PCI resume call.
+ *
+ * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will
+ * have been called, otherwise root hub timers still run ...
+ */
+ case HCD_STATE_SUSPENDED:
+ if (state <= dev->current_state)
+ break;
+
+ /* no DMA or IRQs except in D0 */
+ if (!dev->current_state) {
+ pci_save_state (dev);
+ pci_disable_device (dev);
+ free_irq (hcd->irq, hcd);
+ }
+
+ if (!has_pci_pm) {
+ dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
+ break;
+ }
+
+ /* POLICY: ignore D1/D2/D3hot differences;
+ * we know D3hot will always work.
+ */
+ retval = pci_set_power_state (dev, state);
+ if (retval < 0 && state < 3) {
+ retval = pci_set_power_state (dev, 3);
+ if (retval == 0)
+ state = 3;
+ }
+ if (retval == 0) {
+ dev_dbg (hcd->self.controller, "--> PCI %s\n",
+ pci_state(dev->current_state));
#ifdef CONFIG_USB_SUSPEND
pci_enable_wake (dev, state, hcd->remote_wakeup);
pci_enable_wake (dev, 4, hcd->remote_wakeup);
#endif
- /* no DMA or IRQs except in D0 */
- pci_disable_device (dev);
- free_irq (hcd->irq, hcd);
-
- if (has_pci_pm)
- retval = pci_set_power_state (dev, state);
- dev->dev.power.power_state = state;
- if (retval < 0) {
- dev_dbg (&dev->dev,
- "PCI suspend fail, %d\n",
- retval);
- (void) usb_hcd_pci_resume (dev);
- }
+ } else if (retval < 0) {
+ dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n",
+ pci_state(state), retval);
+ (void) usb_hcd_pci_resume (dev);
+ break;
}
+ break;
+ default:
+ dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
+ hcd->state);
+ retval = -EINVAL;
+ break;
}
+
+ /* update power_state **ONLY** to make sysfs happier */
+ if (retval == 0)
+ dev->dev.power.power_state = state;
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_suspend);
int has_pci_pm;
hcd = pci_get_drvdata(dev);
- has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- if (has_pci_pm)
- dev_dbg(hcd->self.controller, "resume from state D%d\n",
- dev->current_state);
-
if (hcd->state != HCD_STATE_SUSPENDED) {
dev_dbg (hcd->self.controller,
"can't resume, not suspended!\n");
- return -EL3HLT;
+ return 0;
}
+ has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+ /* D3cold resume isn't usually reported this way... */
+ dev_dbg(hcd->self.controller, "resume from PCI %s%s\n",
+ pci_state(dev->current_state),
+ has_pci_pm ? "" : " (legacy)");
+
hcd->state = USB_STATE_RESUMING;
if (has_pci_pm)
pci_set_power_state (dev, 0);
dev->dev.power.power_state = 0;
retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
- hcd->description, hcd);
+ hcd->driver->description, hcd);
if (retval < 0) {
dev_err (hcd->self.controller,
"can't restore IRQ after resume!\n");
return retval;
}
- pci_set_master (dev);
- pci_restore_state (dev, hcd->pci_state);
+ hcd->saw_irq = 0;
+ pci_restore_state (dev);
#ifdef CONFIG_USB_SUSPEND
pci_enable_wake (dev, dev->current_state, 0);
pci_enable_wake (dev, 4, 0);