diff options
-rw-r--r-- | drivers/usb/core/hub.c | 91 |
1 files changed, 81 insertions, 10 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d99963873e37..b97110ca352d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -77,6 +77,7 @@ struct usb_hub { | |||
77 | unsigned has_indicators:1; | 77 | unsigned has_indicators:1; |
78 | u8 indicator[USB_MAXCHILDREN]; | 78 | u8 indicator[USB_MAXCHILDREN]; |
79 | struct delayed_work leds; | 79 | struct delayed_work leds; |
80 | struct delayed_work init_work; | ||
80 | }; | 81 | }; |
81 | 82 | ||
82 | 83 | ||
@@ -515,10 +516,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) | |||
515 | } | 516 | } |
516 | EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); | 517 | EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); |
517 | 518 | ||
518 | static void hub_power_on(struct usb_hub *hub) | 519 | /* If do_delay is false, return the number of milliseconds the caller |
520 | * needs to delay. | ||
521 | */ | ||
522 | static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) | ||
519 | { | 523 | { |
520 | int port1; | 524 | int port1; |
521 | unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; | 525 | unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; |
526 | unsigned delay; | ||
522 | u16 wHubCharacteristics = | 527 | u16 wHubCharacteristics = |
523 | le16_to_cpu(hub->descriptor->wHubCharacteristics); | 528 | le16_to_cpu(hub->descriptor->wHubCharacteristics); |
524 | 529 | ||
@@ -537,7 +542,10 @@ static void hub_power_on(struct usb_hub *hub) | |||
537 | set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); | 542 | set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); |
538 | 543 | ||
539 | /* Wait at least 100 msec for power to become stable */ | 544 | /* Wait at least 100 msec for power to become stable */ |
540 | msleep(max(pgood_delay, (unsigned) 100)); | 545 | delay = max(pgood_delay, (unsigned) 100); |
546 | if (do_delay) | ||
547 | msleep(delay); | ||
548 | return delay; | ||
541 | } | 549 | } |
542 | 550 | ||
543 | static int hub_hub_status(struct usb_hub *hub, | 551 | static int hub_hub_status(struct usb_hub *hub, |
@@ -599,21 +607,55 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) | |||
599 | } | 607 | } |
600 | 608 | ||
601 | enum hub_activation_type { | 609 | enum hub_activation_type { |
602 | HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME | 610 | HUB_INIT, HUB_INIT2, HUB_INIT3, |
611 | HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, | ||
603 | }; | 612 | }; |
604 | 613 | ||
614 | static void hub_init_func2(struct work_struct *ws); | ||
615 | static void hub_init_func3(struct work_struct *ws); | ||
616 | |||
605 | static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) | 617 | static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) |
606 | { | 618 | { |
607 | struct usb_device *hdev = hub->hdev; | 619 | struct usb_device *hdev = hub->hdev; |
608 | int port1; | 620 | int port1; |
609 | int status; | 621 | int status; |
610 | bool need_debounce_delay = false; | 622 | bool need_debounce_delay = false; |
623 | unsigned delay; | ||
624 | |||
625 | /* Continue a partial initialization */ | ||
626 | if (type == HUB_INIT2) | ||
627 | goto init2; | ||
628 | if (type == HUB_INIT3) | ||
629 | goto init3; | ||
611 | 630 | ||
612 | /* After a resume, port power should still be on. | 631 | /* After a resume, port power should still be on. |
613 | * For any other type of activation, turn it on. | 632 | * For any other type of activation, turn it on. |
614 | */ | 633 | */ |
615 | if (type != HUB_RESUME) | 634 | if (type != HUB_RESUME) { |
616 | hub_power_on(hub); | 635 | |
636 | /* Speed up system boot by using a delayed_work for the | ||
637 | * hub's initial power-up delays. This is pretty awkward | ||
638 | * and the implementation looks like a home-brewed sort of | ||
639 | * setjmp/longjmp, but it saves at least 100 ms for each | ||
640 | * root hub (assuming usbcore is compiled into the kernel | ||
641 | * rather than as a module). It adds up. | ||
642 | * | ||
643 | * This can't be done for HUB_RESUME or HUB_RESET_RESUME | ||
644 | * because for those activation types the ports have to be | ||
645 | * operational when we return. In theory this could be done | ||
646 | * for HUB_POST_RESET, but it's easier not to. | ||
647 | */ | ||
648 | if (type == HUB_INIT) { | ||
649 | delay = hub_power_on(hub, false); | ||
650 | PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); | ||
651 | schedule_delayed_work(&hub->init_work, | ||
652 | msecs_to_jiffies(delay)); | ||
653 | return; /* Continues at init2: below */ | ||
654 | } else { | ||
655 | hub_power_on(hub, true); | ||
656 | } | ||
657 | } | ||
658 | init2: | ||
617 | 659 | ||
618 | /* Check each port and set hub->change_bits to let khubd know | 660 | /* Check each port and set hub->change_bits to let khubd know |
619 | * which ports need attention. | 661 | * which ports need attention. |
@@ -692,9 +734,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) | |||
692 | * If any port-status changes do occur during this delay, khubd | 734 | * If any port-status changes do occur during this delay, khubd |
693 | * will see them later and handle them normally. | 735 | * will see them later and handle them normally. |
694 | */ | 736 | */ |
695 | if (need_debounce_delay) | 737 | if (need_debounce_delay) { |
696 | msleep(HUB_DEBOUNCE_STABLE); | 738 | delay = HUB_DEBOUNCE_STABLE; |
697 | 739 | ||
740 | /* Don't do a long sleep inside a workqueue routine */ | ||
741 | if (type == HUB_INIT2) { | ||
742 | PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); | ||
743 | schedule_delayed_work(&hub->init_work, | ||
744 | msecs_to_jiffies(delay)); | ||
745 | return; /* Continues at init3: below */ | ||
746 | } else { | ||
747 | msleep(delay); | ||
748 | } | ||
749 | } | ||
750 | init3: | ||
698 | hub->quiescing = 0; | 751 | hub->quiescing = 0; |
699 | 752 | ||
700 | status = usb_submit_urb(hub->urb, GFP_NOIO); | 753 | status = usb_submit_urb(hub->urb, GFP_NOIO); |
@@ -707,6 +760,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) | |||
707 | kick_khubd(hub); | 760 | kick_khubd(hub); |
708 | } | 761 | } |
709 | 762 | ||
763 | /* Implement the continuations for the delays above */ | ||
764 | static void hub_init_func2(struct work_struct *ws) | ||
765 | { | ||
766 | struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work); | ||
767 | |||
768 | hub_activate(hub, HUB_INIT2); | ||
769 | } | ||
770 | |||
771 | static void hub_init_func3(struct work_struct *ws) | ||
772 | { | ||
773 | struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work); | ||
774 | |||
775 | hub_activate(hub, HUB_INIT3); | ||
776 | } | ||
777 | |||
710 | enum hub_quiescing_type { | 778 | enum hub_quiescing_type { |
711 | HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND | 779 | HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND |
712 | }; | 780 | }; |
@@ -716,6 +784,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) | |||
716 | struct usb_device *hdev = hub->hdev; | 784 | struct usb_device *hdev = hub->hdev; |
717 | int i; | 785 | int i; |
718 | 786 | ||
787 | cancel_delayed_work_sync(&hub->init_work); | ||
788 | |||
719 | /* khubd and related activity won't re-trigger */ | 789 | /* khubd and related activity won't re-trigger */ |
720 | hub->quiescing = 1; | 790 | hub->quiescing = 1; |
721 | 791 | ||
@@ -1099,6 +1169,7 @@ descriptor_error: | |||
1099 | hub->intfdev = &intf->dev; | 1169 | hub->intfdev = &intf->dev; |
1100 | hub->hdev = hdev; | 1170 | hub->hdev = hdev; |
1101 | INIT_DELAYED_WORK(&hub->leds, led_work); | 1171 | INIT_DELAYED_WORK(&hub->leds, led_work); |
1172 | INIT_DELAYED_WORK(&hub->init_work, NULL); | ||
1102 | usb_get_intf(intf); | 1173 | usb_get_intf(intf); |
1103 | 1174 | ||
1104 | usb_set_intfdata (intf, hub); | 1175 | usb_set_intfdata (intf, hub); |
@@ -3035,7 +3106,7 @@ static void hub_events(void) | |||
3035 | i); | 3106 | i); |
3036 | clear_port_feature(hdev, i, | 3107 | clear_port_feature(hdev, i, |
3037 | USB_PORT_FEAT_C_OVER_CURRENT); | 3108 | USB_PORT_FEAT_C_OVER_CURRENT); |
3038 | hub_power_on(hub); | 3109 | hub_power_on(hub, true); |
3039 | } | 3110 | } |
3040 | 3111 | ||
3041 | if (portchange & USB_PORT_STAT_C_RESET) { | 3112 | if (portchange & USB_PORT_STAT_C_RESET) { |
@@ -3070,7 +3141,7 @@ static void hub_events(void) | |||
3070 | dev_dbg (hub_dev, "overcurrent change\n"); | 3141 | dev_dbg (hub_dev, "overcurrent change\n"); |
3071 | msleep(500); /* Cool down */ | 3142 | msleep(500); /* Cool down */ |
3072 | clear_hub_feature(hdev, C_HUB_OVER_CURRENT); | 3143 | clear_hub_feature(hdev, C_HUB_OVER_CURRENT); |
3073 | hub_power_on(hub); | 3144 | hub_power_on(hub, true); |
3074 | } | 3145 | } |
3075 | } | 3146 | } |
3076 | 3147 | ||