Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[powerpc.git] / drivers / usb / core / hub.c
index 65720f2..ba165af 100644 (file)
@@ -291,9 +291,9 @@ void usb_kick_khubd(struct usb_device *hdev)
 
 
 /* completion function, fires on port status changes and various faults */
-static void hub_irq(struct urb *urb, struct pt_regs *regs)
+static void hub_irq(struct urb *urb)
 {
-       struct usb_hub *hub = (struct usb_hub *)urb->context;
+       struct usb_hub *hub = urb->context;
        int status;
        int i;
        unsigned long bits;
@@ -311,7 +311,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
                        goto resubmit;
                hub->error = urb->status;
                /* FALL THROUGH */
-       
+
        /* let khubd handle things */
        case 0:                 /* we got data:  port status changed */
                bits = 0;
@@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub)
        msleep(max(pgood_delay, (unsigned) 100));
 }
 
-static inline void __hub_quiesce(struct usb_hub *hub)
+static void hub_quiesce(struct usb_hub *hub)
 {
        /* (nonblocking) khubd and related activity won't re-trigger */
        hub->quiescing = 1;
        hub->activating = 0;
        hub->resume_root_hub = 0;
-}
 
-static void hub_quiesce(struct usb_hub *hub)
-{
        /* (blocking) stop khubd and related activity */
-       __hub_quiesce(hub);
        usb_kill_urb(hub->urb);
        if (hub->has_indicators)
                cancel_delayed_work(&hub->leds);
@@ -1017,19 +1013,22 @@ void usb_set_device_state(struct usb_device *udev,
        if (udev->state == USB_STATE_NOTATTACHED)
                ;       /* do nothing */
        else if (new_state != USB_STATE_NOTATTACHED) {
-               udev->state = new_state;
 
                /* root hub wakeup capabilities are managed out-of-band
                 * and may involve silicon errata ... ignore them here.
                 */
                if (udev->parent) {
-                       if (new_state == USB_STATE_CONFIGURED)
+                       if (udev->state == USB_STATE_SUSPENDED
+                                       || new_state == USB_STATE_SUSPENDED)
+                               ;       /* No change to wakeup settings */
+                       else if (new_state == USB_STATE_CONFIGURED)
                                device_init_wakeup(&udev->dev,
                                        (udev->actconfig->desc.bmAttributes
                                         & USB_CONFIG_ATT_WAKEUP));
-                       else if (new_state != USB_STATE_SUSPENDED)
+                       else
                                device_init_wakeup(&udev->dev, 0);
                }
+               udev->state = new_state;
        } else
                recursively_mark_NOTATTACHED(udev);
        spin_unlock_irqrestore(&device_state_lock, flags);
@@ -1054,6 +1053,12 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
        unsigned long flags;
 
        dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+
+       /* Make sure no potential wakeup events get lost,
+        * by forcing the root hub to be resumed.
+        */
+       rhdev->dev.power.prev_state.event = PM_EVENT_ON;
+
        spin_lock_irqsave(&device_state_lock, flags);
        hub = hdev_to_hub(rhdev);
        for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
@@ -1183,6 +1188,7 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
 
 #ifdef CONFIG_USB_OTG
 #include "otg_whitelist.h"
+static int __usb_port_suspend(struct usb_device *, int port1);
 #endif
 
 /**
@@ -1249,8 +1255,7 @@ int usb_new_device(struct usb_device *udev)
                                        USB_DT_OTG, (void **) &desc) == 0) {
                        if (desc->bmAttributes & USB_OTG_HNP) {
                                unsigned                port1 = udev->portnum;
-                               struct usb_device       *root = udev->parent;
-                               
+
                                dev_info(&udev->dev,
                                        "Dual-Role OTG device on %sHNP port\n",
                                        (port1 == bus->otg_port)
@@ -1285,8 +1290,6 @@ int usb_new_device(struct usb_device *udev)
                 * (Includes HNP test device.)
                 */
                if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-                       static int __usb_port_suspend(struct usb_device *,
-                                               int port1);
                        err = __usb_port_suspend(udev, udev->bus->otg_port);
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
@@ -1507,7 +1510,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
         * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
         * we don't explicitly enable it here.
         */
-       if (device_may_wakeup(&udev->dev)) {
+       if (udev->do_remote_wakeup) {
                status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
                                USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1533,7 +1536,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
                                USB_CTRL_SET_TIMEOUT);
        } else {
                /* device has up to 10 msec to fully suspend */
-               dev_dbg(&udev->dev, "usb suspend\n");
+               dev_dbg(&udev->dev, "usb %ssuspend\n",
+                               udev->auto_pm ? "auto-" : "");
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
                msleep(10);
        }
@@ -1573,7 +1577,8 @@ static int __usb_port_suspend (struct usb_device *udev, int port1)
                status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
                                udev);
        else {
-               dev_dbg(&udev->dev, "usb suspend\n");
+               dev_dbg(&udev->dev, "usb %ssuspend\n",
+                               udev->auto_pm ? "auto-" : "");
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
        }
        return status;
@@ -1687,7 +1692,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 
                /* drive resume for at least 20 msec */
                if (udev)
-                       dev_dbg(&udev->dev, "RESUME\n");
+                       dev_dbg(&udev->dev, "usb %sresume\n",
+                                       udev->auto_pm ? "auto-" : "");
                msleep(25);
 
 #define LIVE_FLAGS     ( USB_PORT_STAT_POWER \
@@ -1754,8 +1760,11 @@ int usb_port_resume(struct usb_device *udev)
                // NOTE this fails if parent is also suspended...
                status = hub_port_resume(hdev_to_hub(udev->parent),
                                udev->portnum, udev);
-       } else
+       } else {
+               dev_dbg(&udev->dev, "usb %sresume\n",
+                               udev->auto_pm ? "auto-" : "");
                status = finish_port_resume(udev);
+       }
        if (status < 0)
                dev_dbg(&udev->dev, "can't resume, status %d\n", status);
        return status;
@@ -1765,19 +1774,23 @@ static int remote_wakeup(struct usb_device *udev)
 {
        int     status = 0;
 
-       /* don't repeat RESUME sequence if this device
-        * was already woken up by some other task
-        */
+       /* All this just to avoid sending a port-resume message
+        * to the parent hub! */
+
        usb_lock_device(udev);
+       usb_pm_lock(udev);
        if (udev->state == USB_STATE_SUSPENDED) {
-               dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+               dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
                /* TRSMRCY = 10 msec */
                msleep(10);
                status = finish_port_resume(udev);
+               if (status == 0)
+                       udev->dev.power.power_state.event = PM_EVENT_ON;
        }
+       usb_pm_unlock(udev);
 
        if (status == 0)
-               usb_resume_both(udev);
+               usb_autoresume_device(udev, 0);
        usb_unlock_device(udev);
        return status;
 }
@@ -1834,7 +1847,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
                                        == PM_EVENT_ON
 #endif
                                ) {
-                       dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+                       if (!hdev->auto_pm)
+                               dev_dbg(&intf->dev, "port %d nyet suspended\n",
+                                               port1);
                        return -EBUSY;
                }
        }
@@ -1895,20 +1910,10 @@ static inline int remote_wakeup(struct usb_device *udev)
        return 0;
 }
 
+#define hub_suspend NULL
+#define hub_resume NULL
 #endif
 
-void usb_suspend_root_hub(struct usb_device *hdev)
-{
-       struct usb_hub *hub = hdev_to_hub(hdev);
-
-       /* This also makes any led blinker stop retriggering.  We're called
-        * from irq, so the blinker might still be scheduled.  Caller promises
-        * that the root hub status URB will be canceled.
-        */
-       __hub_quiesce(hub);
-       mark_quiesced(to_usb_interface(hub->intfdev));
-}
-
 void usb_resume_root_hub(struct usb_device *hdev)
 {
        struct usb_hub *hub = hdev_to_hub(hdev);
@@ -2412,6 +2417,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                usb_set_device_state(udev, USB_STATE_POWERED);
                udev->speed = USB_SPEED_UNKNOWN;
                udev->bus_mA = hub->mA_per_port;
+               udev->level = hdev->level + 1;
 
                /* set the address */
                choose_address(udev);
@@ -2584,7 +2590,7 @@ static void hub_events(void)
                 * stub "device" node was never suspended.
                 */
                if (i)
-                       usb_resume_both(hdev);
+                       usb_autoresume_device(hdev, 0);
 
                /* If this is an inactive or suspended hub, do nothing */
                if (hub->quiescing)
@@ -2990,6 +2996,9 @@ int usb_reset_composite_device(struct usb_device *udev,
                return -EINVAL;
        }
 
+       /* Prevent autosuspend during the reset */
+       usb_autoresume_device(udev, 1);
+
        if (iface && iface->condition != USB_INTERFACE_BINDING)
                iface = NULL;
 
@@ -3031,6 +3040,7 @@ int usb_reset_composite_device(struct usb_device *udev,
                }
        }
 
+       usb_autosuspend_device(udev, 1);
        return ret;
 }
 EXPORT_SYMBOL(usb_reset_composite_device);