Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: automatically enable RHSC interrupts

This patch (as1069c) changes the way OHCI root-hub status-change
interrupts are enabled. Currently a special HCD method,
hub_irq_enable(), is called when the hub driver is finished using a
root hub. This approach turns out to be subject to races, resulting
in unnecessary polling.

The patch does away with the method entirely. Instead, the driver
automatically enables the RHSC interrupt when no more status changes
are present. This scheme is safe with controllers using
level-triggered semantics for their interrupt flags.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
b5fb454f 5096aedc

+31 -72
-9
drivers/usb/core/hcd.c
··· 924 924 return retval; 925 925 } 926 926 927 - void usb_enable_root_hub_irq (struct usb_bus *bus) 928 - { 929 - struct usb_hcd *hcd; 930 - 931 - hcd = container_of (bus, struct usb_hcd, self); 932 - if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT) 933 - hcd->driver->hub_irq_enable (hcd); 934 - } 935 - 936 927 937 928 /*-------------------------------------------------------------------------*/ 938 929
-4
drivers/usb/core/hcd.h
··· 212 212 int (*bus_suspend)(struct usb_hcd *); 213 213 int (*bus_resume)(struct usb_hcd *); 214 214 int (*start_port_reset)(struct usb_hcd *, unsigned port_num); 215 - void (*hub_irq_enable)(struct usb_hcd *); 216 - /* Needed only if port-change IRQs are level-triggered */ 217 215 218 216 /* force handover of high-speed port to full-speed companion */ 219 217 void (*relinquish_port)(struct usb_hcd *, int); ··· 376 378 extern struct list_head usb_bus_list; 377 379 extern struct mutex usb_bus_list_lock; 378 380 extern wait_queue_head_t usb_kill_urb_queue; 379 - 380 - extern void usb_enable_root_hub_irq(struct usb_bus *bus); 381 381 382 382 extern int usb_find_interface_driver(struct usb_device *dev, 383 383 struct usb_interface *interface);
-9
drivers/usb/core/hub.c
··· 2102 2102 } 2103 2103 2104 2104 clear_bit(port1, hub->busy_bits); 2105 - if (!hub->hdev->parent && !hub->busy_bits[0]) 2106 - usb_enable_root_hub_irq(hub->hdev->bus); 2107 2105 2108 2106 status = check_port_resume_type(udev, 2109 2107 hub, port1, status, portchange, portstatus); ··· 3079 3081 } 3080 3082 } 3081 3083 3082 - /* If this is a root hub, tell the HCD it's okay to 3083 - * re-enable port-change interrupts now. */ 3084 - if (!hdev->parent && !hub->busy_bits[0]) 3085 - usb_enable_root_hub_irq(hdev->bus); 3086 - 3087 3084 loop_autopm: 3088 3085 /* Allow autosuspend if we're not going to run again */ 3089 3086 if (list_empty(&hub->event_list)) ··· 3304 3311 break; 3305 3312 } 3306 3313 clear_bit(port1, parent_hub->busy_bits); 3307 - if (!parent_hdev->parent && !parent_hub->busy_bits[0]) 3308 - usb_enable_root_hub_irq(parent_hdev->bus); 3309 3314 3310 3315 if (ret < 0) 3311 3316 goto re_enumerate;
-1
drivers/usb/host/ohci-at91.c
··· 260 260 */ 261 261 .hub_status_data = ohci_hub_status_data, 262 262 .hub_control = ohci_hub_control, 263 - .hub_irq_enable = ohci_rhsc_enable, 264 263 #ifdef CONFIG_PM 265 264 .bus_suspend = ohci_bus_suspend, 266 265 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-au1xxx.c
··· 163 163 */ 164 164 .hub_status_data = ohci_hub_status_data, 165 165 .hub_control = ohci_hub_control, 166 - .hub_irq_enable = ohci_rhsc_enable, 167 166 #ifdef CONFIG_PM 168 167 .bus_suspend = ohci_bus_suspend, 169 168 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-ep93xx.c
··· 134 134 .get_frame_number = ohci_get_frame, 135 135 .hub_status_data = ohci_hub_status_data, 136 136 .hub_control = ohci_hub_control, 137 - .hub_irq_enable = ohci_rhsc_enable, 138 137 #ifdef CONFIG_PM 139 138 .bus_suspend = ohci_bus_suspend, 140 139 .bus_resume = ohci_bus_resume,
+31 -22
drivers/usb/host/ohci-hub.c
··· 36 36 37 37 /*-------------------------------------------------------------------------*/ 38 38 39 - /* hcd->hub_irq_enable() */ 40 - static void ohci_rhsc_enable (struct usb_hcd *hcd) 41 - { 42 - struct ohci_hcd *ohci = hcd_to_ohci (hcd); 43 - 44 - spin_lock_irq(&ohci->lock); 45 - if (!ohci->autostop) 46 - del_timer(&hcd->rh_timer); /* Prevent next poll */ 47 - ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); 48 - spin_unlock_irq(&ohci->lock); 49 - } 50 - 51 39 #define OHCI_SCHED_ENABLES \ 52 40 (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) 53 41 ··· 362 374 int any_connected) 363 375 { 364 376 int poll_rh = 1; 377 + int rhsc; 365 378 379 + rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC; 366 380 switch (ohci->hc_control & OHCI_CTRL_HCFS) { 367 381 368 382 case OHCI_USB_OPER: 369 - /* keep on polling until we know a device is connected 370 - * and RHSC is enabled */ 383 + /* If no status changes are pending, enable status-change 384 + * interrupts. 385 + */ 386 + if (!rhsc && !changed) { 387 + rhsc = OHCI_INTR_RHSC; 388 + ohci_writel(ohci, rhsc, &ohci->regs->intrenable); 389 + } 390 + 391 + /* Keep on polling until we know a device is connected 392 + * and RHSC is enabled, or until we autostop. 393 + */ 371 394 if (!ohci->autostop) { 372 395 if (any_connected || 373 396 !device_may_wakeup(&ohci_to_hcd(ohci) 374 397 ->self.root_hub->dev)) { 375 - if (ohci_readl(ohci, &ohci->regs->intrenable) & 376 - OHCI_INTR_RHSC) 398 + if (rhsc) 377 399 poll_rh = 0; 378 400 } else { 379 401 ohci->autostop = 1; ··· 396 398 ohci->autostop = 0; 397 399 ohci->next_statechange = jiffies + 398 400 STATECHANGE_DELAY; 399 - } else if (time_after_eq(jiffies, 401 + } else if (rhsc && time_after_eq(jiffies, 400 402 ohci->next_statechange) 401 403 && !ohci->ed_rm_list 402 404 && !(ohci->hc_control & 403 405 OHCI_SCHED_ENABLES)) { 404 406 ohci_rh_suspend(ohci, 1); 407 + poll_rh = 0; 405 408 } 406 409 } 407 410 break; ··· 416 417 else 417 418 usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); 418 419 } else { 420 + if (!rhsc && (ohci->autostop || 421 + ohci_to_hcd(ohci)->self.root_hub-> 422 + do_remote_wakeup)) 423 + ohci_writel(ohci, OHCI_INTR_RHSC, 424 + &ohci->regs->intrenable); 425 + 419 426 /* everything is idle, no need for polling */ 420 427 poll_rh = 0; 421 428 } ··· 443 438 static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, 444 439 int any_connected) 445 440 { 446 - int poll_rh = 1; 447 - 448 - /* keep on polling until RHSC is enabled */ 441 + /* If RHSC is enabled, don't poll */ 449 442 if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) 450 - poll_rh = 0; 451 - return poll_rh; 443 + return 0; 444 + 445 + /* If no status changes are pending, enable status-change interrupts */ 446 + if (!changed) { 447 + ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); 448 + return 0; 449 + } 450 + return 1; 452 451 } 453 452 454 453 #endif /* CONFIG_PM */
-1
drivers/usb/host/ohci-lh7a404.c
··· 193 193 */ 194 194 .hub_status_data = ohci_hub_status_data, 195 195 .hub_control = ohci_hub_control, 196 - .hub_irq_enable = ohci_rhsc_enable, 197 196 #ifdef CONFIG_PM 198 197 .bus_suspend = ohci_bus_suspend, 199 198 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-omap.c
··· 470 470 */ 471 471 .hub_status_data = ohci_hub_status_data, 472 472 .hub_control = ohci_hub_control, 473 - .hub_irq_enable = ohci_rhsc_enable, 474 473 #ifdef CONFIG_PM 475 474 .bus_suspend = ohci_bus_suspend, 476 475 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-pci.c
··· 459 459 */ 460 460 .hub_status_data = ohci_hub_status_data, 461 461 .hub_control = ohci_hub_control, 462 - .hub_irq_enable = ohci_rhsc_enable, 463 462 #ifdef CONFIG_PM 464 463 .bus_suspend = ohci_bus_suspend, 465 464 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-pnx4008.c
··· 277 277 */ 278 278 .hub_status_data = ohci_hub_status_data, 279 279 .hub_control = ohci_hub_control, 280 - .hub_irq_enable = ohci_rhsc_enable, 281 280 #ifdef CONFIG_PM 282 281 .bus_suspend = ohci_bus_suspend, 283 282 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-pnx8550.c
··· 201 201 */ 202 202 .hub_status_data = ohci_hub_status_data, 203 203 .hub_control = ohci_hub_control, 204 - .hub_irq_enable = ohci_rhsc_enable, 205 204 #ifdef CONFIG_PM 206 205 .bus_suspend = ohci_bus_suspend, 207 206 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-ppc-of.c
··· 72 72 */ 73 73 .hub_status_data = ohci_hub_status_data, 74 74 .hub_control = ohci_hub_control, 75 - .hub_irq_enable = ohci_rhsc_enable, 76 75 #ifdef CONFIG_PM 77 76 .bus_suspend = ohci_bus_suspend, 78 77 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-ppc-soc.c
··· 172 172 */ 173 173 .hub_status_data = ohci_hub_status_data, 174 174 .hub_control = ohci_hub_control, 175 - .hub_irq_enable = ohci_rhsc_enable, 176 175 #ifdef CONFIG_PM 177 176 .bus_suspend = ohci_bus_suspend, 178 177 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-ps3.c
··· 68 68 .get_frame_number = ohci_get_frame, 69 69 .hub_status_data = ohci_hub_status_data, 70 70 .hub_control = ohci_hub_control, 71 - .hub_irq_enable = ohci_rhsc_enable, 72 71 .start_port_reset = ohci_start_port_reset, 73 72 #if defined(CONFIG_PM) 74 73 .bus_suspend = ohci_bus_suspend,
-1
drivers/usb/host/ohci-pxa27x.c
··· 298 298 */ 299 299 .hub_status_data = ohci_hub_status_data, 300 300 .hub_control = ohci_hub_control, 301 - .hub_irq_enable = ohci_rhsc_enable, 302 301 #ifdef CONFIG_PM 303 302 .bus_suspend = ohci_bus_suspend, 304 303 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-s3c2410.c
··· 466 466 */ 467 467 .hub_status_data = ohci_s3c2410_hub_status_data, 468 468 .hub_control = ohci_s3c2410_hub_control, 469 - .hub_irq_enable = ohci_rhsc_enable, 470 469 #ifdef CONFIG_PM 471 470 .bus_suspend = ohci_bus_suspend, 472 471 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-sa1111.c
··· 231 231 */ 232 232 .hub_status_data = ohci_hub_status_data, 233 233 .hub_control = ohci_hub_control, 234 - .hub_irq_enable = ohci_rhsc_enable, 235 234 #ifdef CONFIG_PM 236 235 .bus_suspend = ohci_bus_suspend, 237 236 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-sh.c
··· 68 68 */ 69 69 .hub_status_data = ohci_hub_status_data, 70 70 .hub_control = ohci_hub_control, 71 - .hub_irq_enable = ohci_rhsc_enable, 72 71 #ifdef CONFIG_PM 73 72 .bus_suspend = ohci_bus_suspend, 74 73 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-sm501.c
··· 75 75 */ 76 76 .hub_status_data = ohci_hub_status_data, 77 77 .hub_control = ohci_hub_control, 78 - .hub_irq_enable = ohci_rhsc_enable, 79 78 #ifdef CONFIG_PM 80 79 .bus_suspend = ohci_bus_suspend, 81 80 .bus_resume = ohci_bus_resume,
-1
drivers/usb/host/ohci-ssb.c
··· 81 81 82 82 .hub_status_data = ohci_hub_status_data, 83 83 .hub_control = ohci_hub_control, 84 - .hub_irq_enable = ohci_rhsc_enable, 85 84 #ifdef CONFIG_PM 86 85 .bus_suspend = ohci_bus_suspend, 87 86 .bus_resume = ohci_bus_resume,
-11
drivers/usb/host/u132-hcd.c
··· 2934 2934 return 0; 2935 2935 } 2936 2936 2937 - static void u132_hub_irq_enable(struct usb_hcd *hcd) 2938 - { 2939 - struct u132 *u132 = hcd_to_u132(hcd); 2940 - if (u132->going > 1) { 2941 - dev_err(&u132->platform_dev->dev, "device has been removed %d\n" 2942 - , u132->going); 2943 - } else if (u132->going > 0) 2944 - dev_err(&u132->platform_dev->dev, "device is being removed\n"); 2945 - } 2946 - 2947 2937 2948 2938 #ifdef CONFIG_PM 2949 2939 static int u132_bus_suspend(struct usb_hcd *hcd) ··· 2985 2995 .bus_suspend = u132_bus_suspend, 2986 2996 .bus_resume = u132_bus_resume, 2987 2997 .start_port_reset = u132_start_port_reset, 2988 - .hub_irq_enable = u132_hub_irq_enable, 2989 2998 }; 2990 2999 2991 3000 /*