@@ -77,6 +77,7 @@ struct usb_hub {
7777 unsigned has_indicators :1 ;
7878 u8 indicator [USB_MAXCHILDREN ];
7979 struct delayed_work leds ;
80+ struct delayed_work init_work ;
8081};
8182
8283
@@ -515,10 +516,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
515516}
516517EXPORT_SYMBOL_GPL (usb_hub_tt_clear_buffer );
517518
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 )
519523{
520524 int port1 ;
521525 unsigned pgood_delay = hub -> descriptor -> bPwrOn2PwrGood * 2 ;
526+ unsigned delay ;
522527 u16 wHubCharacteristics =
523528 le16_to_cpu (hub -> descriptor -> wHubCharacteristics );
524529
@@ -537,7 +542,10 @@ static void hub_power_on(struct usb_hub *hub)
537542 set_port_feature (hub -> hdev , port1 , USB_PORT_FEAT_POWER );
538543
539544 /* 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 ;
541549}
542550
543551static int hub_hub_status (struct usb_hub * hub ,
@@ -599,21 +607,55 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
599607}
600608
601609enum 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 ,
603612};
604613
614+ static void hub_init_func2 (struct work_struct * ws );
615+ static void hub_init_func3 (struct work_struct * ws );
616+
605617static void hub_activate (struct usb_hub * hub , enum hub_activation_type type )
606618{
607619 struct usb_device * hdev = hub -> hdev ;
608620 int port1 ;
609621 int status ;
610622 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 ;
611630
612631 /* After a resume, port power should still be on.
613632 * For any other type of activation, turn it on.
614633 */
615- if (type != HUB_RESUME )
616- hub_power_on (hub );
634+ if (type != HUB_RESUME ) {
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 :
617659
618660 /* Check each port and set hub->change_bits to let khubd know
619661 * which ports need attention.
@@ -692,9 +734,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
692734 * If any port-status changes do occur during this delay, khubd
693735 * will see them later and handle them normally.
694736 */
695- if (need_debounce_delay )
696- msleep (HUB_DEBOUNCE_STABLE );
697-
737+ if (need_debounce_delay ) {
738+ delay = HUB_DEBOUNCE_STABLE ;
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 :
698751 hub -> quiescing = 0 ;
699752
700753 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)
707760 kick_khubd (hub );
708761}
709762
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+
710778enum hub_quiescing_type {
711779 HUB_DISCONNECT , HUB_PRE_RESET , HUB_SUSPEND
712780};
@@ -716,6 +784,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
716784 struct usb_device * hdev = hub -> hdev ;
717785 int i ;
718786
787+ cancel_delayed_work_sync (& hub -> init_work );
788+
719789 /* khubd and related activity won't re-trigger */
720790 hub -> quiescing = 1 ;
721791
@@ -1099,6 +1169,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
10991169 hub -> intfdev = & intf -> dev ;
11001170 hub -> hdev = hdev ;
11011171 INIT_DELAYED_WORK (& hub -> leds , led_work );
1172+ INIT_DELAYED_WORK (& hub -> init_work , NULL );
11021173 usb_get_intf (intf );
11031174
11041175 usb_set_intfdata (intf , hub );
@@ -3035,7 +3106,7 @@ static void hub_events(void)
30353106 i );
30363107 clear_port_feature (hdev , i ,
30373108 USB_PORT_FEAT_C_OVER_CURRENT );
3038- hub_power_on (hub );
3109+ hub_power_on (hub , true );
30393110 }
30403111
30413112 if (portchange & USB_PORT_STAT_C_RESET ) {
@@ -3070,7 +3141,7 @@ static void hub_events(void)
30703141 dev_dbg (hub_dev , "overcurrent change\n" );
30713142 msleep (500 ); /* Cool down */
30723143 clear_hub_feature (hdev , C_HUB_OVER_CURRENT );
3073- hub_power_on (hub );
3144+ hub_power_on (hub , true );
30743145 }
30753146 }
30763147
0 commit comments