vserver 1.9.5.x5
[linux-2.6.git] / drivers / usb / core / hcd-pci.c
index 5fdaae0..7b836ae 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
-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 */
 
@@ -78,12 +70,15 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
 
        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
@@ -92,7 +87,8 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
                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) {
@@ -102,7 +98,7 @@ clean_1:
                        release_mem_region (resource, len);
                        dev_err (&dev->dev, "init %s fail, %d\n",
                                pci_name(dev), retval);
-                       return retval;
+                       goto done;
                }
 
        } else {                                // UHCI
@@ -119,7 +115,8 @@ clean_1:
                }
                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;
        }
@@ -127,7 +124,7 @@ clean_1:
        // 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;
@@ -139,7 +136,7 @@ clean_2:
                        release_region (resource, len);
                        dev_err (&dev->dev, "init %s fail, %d\n",
                                pci_name(dev), retval);
-                       return retval;
+                       goto done;
                }
        }
        // hcd zeroed everything
@@ -147,20 +144,16 @@ clean_2:
        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;
        }
 
@@ -171,7 +164,6 @@ clean_3:
                dev_err (hcd->self.controller, "can't reset\n");
                goto clean_3;
        }
-       hcd->state = USB_STATE_HALT;
 
        pci_set_master (dev);
 #ifndef __sparc__
@@ -180,7 +172,7 @@ clean_3:
        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);
@@ -188,17 +180,9 @@ clean_3:
        }
        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);
 
@@ -207,6 +191,9 @@ clean_3:
                usb_hcd_pci_remove (dev);
        }
 
+done:
+       if (retval != 0)
+               pci_disable_device (dev);
        return retval;
 } 
 EXPORT_SYMBOL (usb_hcd_pci_probe);
@@ -260,12 +247,26 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
        }
 
        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
@@ -286,45 +287,82 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
         * 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);
@@ -342,30 +380,32 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
        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);