aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-08-21 13:48:07 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-08-21 13:48:07 -0700
commit691a55998cc2fc645b51d28edb4f4d36b512826e (patch)
tree6674dcece28f885d1063771461a8f5a7fa9f0d1b /drivers
parentcce7496d3d5910a003109207f9737c3be658aa1a (diff)
parenteaea04353e0114a9805fc2cf1ff832cb0ac2570b (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: USB: sisusbvga: add USB ID for 0711:0918 Magic Control Technology Corp. USB: automatically enable RHSC interrupts USB: Don't rebind before "complete" callback USB: Add new PM callback methods for USB USB: Defer Set-Interface for suspended devices USB: Add udev argument to interface suspend/resume functions USB: cdc-acm: don't unlock acm->mutex on error path MUSB: Fix index register corruption seen with g_ether and Windows host usb: musb: get rid of MUSB_LOGLEVEL and use parameter usb: musb: get rid of procfs entry USB: Fix pxa27x_udc usb speed handling. USB: cdc-acm: quirk for Conexant CX93010 USB modem USB: fix bug in usb_unlink_anchored_urbs() usb-serial: option support HSDPA modem A2502 USB: ISP1760: fixed trivial math in comment
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/class/cdc-acm.c5
-rw-r--r--drivers/usb/core/driver.c96
-rw-r--r--drivers/usb/core/hcd.c9
-rw-r--r--drivers/usb/core/hcd.h4
-rw-r--r--drivers/usb/core/hub.c9
-rw-r--r--drivers/usb/core/urb.c9
-rw-r--r--drivers/usb/core/usb.c73
-rw-r--r--drivers/usb/core/usb.h3
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c2
-rw-r--r--drivers/usb/host/isp1760-hcd.c2
-rw-r--r--drivers/usb/host/ohci-at91.c1
-rw-r--r--drivers/usb/host/ohci-au1xxx.c1
-rw-r--r--drivers/usb/host/ohci-ep93xx.c1
-rw-r--r--drivers/usb/host/ohci-hub.c53
-rw-r--r--drivers/usb/host/ohci-lh7a404.c1
-rw-r--r--drivers/usb/host/ohci-omap.c1
-rw-r--r--drivers/usb/host/ohci-pci.c1
-rw-r--r--drivers/usb/host/ohci-pnx4008.c1
-rw-r--r--drivers/usb/host/ohci-pnx8550.c1
-rw-r--r--drivers/usb/host/ohci-ppc-of.c1
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c1
-rw-r--r--drivers/usb/host/ohci-ps3.c1
-rw-r--r--drivers/usb/host/ohci-pxa27x.c1
-rw-r--r--drivers/usb/host/ohci-s3c2410.c1
-rw-r--r--drivers/usb/host/ohci-sa1111.c1
-rw-r--r--drivers/usb/host/ohci-sh.c1
-rw-r--r--drivers/usb/host/ohci-sm501.c1
-rw-r--r--drivers/usb/host/ohci-ssb.c1
-rw-r--r--drivers/usb/host/u132-hcd.c11
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c1
-rw-r--r--drivers/usb/musb/Kconfig13
-rw-r--r--drivers/usb/musb/Makefile21
-rw-r--r--drivers/usb/musb/musb_core.c38
-rw-r--r--drivers/usb/musb/musb_core.h19
-rw-r--r--drivers/usb/musb/musb_debug.h4
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c2
-rw-r--r--drivers/usb/musb/musb_procfs.c830
-rw-r--r--drivers/usb/serial/option.c2
38 files changed, 191 insertions, 1032 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index efc4373eded..c257453fa9d 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -589,8 +589,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
tasklet_schedule(&acm->urb_task);
done:
-err_out:
mutex_unlock(&acm->mutex);
+err_out:
mutex_unlock(&open_mutex);
return rv;
@@ -1362,6 +1362,9 @@ static struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
+ { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
/* control interfaces with various AT-command sets */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 2be37fe466f..5a7fa6f0995 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -230,6 +230,13 @@ static int usb_probe_interface(struct device *dev)
*/
intf->pm_usage_cnt = !(driver->supports_autosuspend);
+ /* Carry out a deferred switch to altsetting 0 */
+ if (intf->needs_altsetting0) {
+ usb_set_interface(udev, intf->altsetting[0].
+ desc.bInterfaceNumber, 0);
+ intf->needs_altsetting0 = 0;
+ }
+
error = driver->probe(intf, id);
if (error) {
mark_quiesced(intf);
@@ -266,8 +273,17 @@ static int usb_unbind_interface(struct device *dev)
driver->disconnect(intf);
- /* reset other interface state */
- usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
+ /* Reset other interface state.
+ * We cannot do a Set-Interface if the device is suspended or
+ * if it is prepared for a system sleep (since installing a new
+ * altsetting means creating new endpoint device entries).
+ * When either of these happens, defer the Set-Interface.
+ */
+ if (!error && intf->dev.power.status == DPM_ON)
+ usb_set_interface(udev, intf->altsetting[0].
+ desc.bInterfaceNumber, 0);
+ else
+ intf->needs_altsetting0 = 1;
usb_set_intfdata(intf, NULL);
intf->condition = USB_INTERFACE_UNBOUND;
@@ -798,7 +814,8 @@ void usb_forced_unbind_intf(struct usb_interface *intf)
* The caller must hold @intf's device's lock, but not its pm_mutex
* and not @intf->dev.sem.
*
- * FIXME: The caller must block system sleep transitions.
+ * Note: Rebinds will be skipped if a system sleep transition is in
+ * progress and the PM "complete" callback hasn't occurred yet.
*/
void usb_rebind_intf(struct usb_interface *intf)
{
@@ -814,10 +831,12 @@ void usb_rebind_intf(struct usb_interface *intf)
}
/* Try to rebind the interface */
- intf->needs_binding = 0;
- rc = device_attach(&intf->dev);
- if (rc < 0)
- dev_warn(&intf->dev, "rebind failed: %d\n", rc);
+ if (intf->dev.power.status == DPM_ON) {
+ intf->needs_binding = 0;
+ rc = device_attach(&intf->dev);
+ if (rc < 0)
+ dev_warn(&intf->dev, "rebind failed: %d\n", rc);
+ }
}
#ifdef CONFIG_PM
@@ -829,7 +848,6 @@ void usb_rebind_intf(struct usb_interface *intf)
* or rebind interfaces that have been unbound, according to @action.
*
* The caller must hold @udev's device lock.
- * FIXME: For rebinds, the caller must block system sleep transitions.
*/
static void do_unbind_rebind(struct usb_device *udev, int action)
{
@@ -851,22 +869,8 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
}
break;
case DO_REBIND:
- if (intf->needs_binding) {
-
- /* FIXME: The next line is needed because we are going to probe
- * the interface, but as far as the PM core is concerned the
- * interface is still suspended. The problem wouldn't exist
- * if we could rebind the interface during the interface's own
- * resume() call, but at the time the usb_device isn't locked!
- *
- * The real solution will be to carry this out during the device's
- * complete() callback. Until that is implemented, we have to
- * use this hack.
- */
-// intf->dev.power.sleeping = 0;
-
+ if (intf->needs_binding)
usb_rebind_intf(intf);
- }
break;
}
}
@@ -926,14 +930,14 @@ static int usb_resume_device(struct usb_device *udev)
}
/* Caller has locked intf's usb_device's pm mutex */
-static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
+static int usb_suspend_interface(struct usb_device *udev,
+ struct usb_interface *intf, pm_message_t msg)
{
struct usb_driver *driver;
int status = 0;
/* with no hardware, USB interfaces only use FREEZE and ON states */
- if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
- !is_active(intf))
+ if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf))
goto done;
if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */
@@ -944,7 +948,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
- else if (!interface_to_usbdev(intf)->auto_pm)
+ else if (!udev->auto_pm)
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
@@ -961,13 +965,13 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
}
/* Caller has locked intf's usb_device's pm_mutex */
-static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
+static int usb_resume_interface(struct usb_device *udev,
+ struct usb_interface *intf, int reset_resume)
{
struct usb_driver *driver;
int status = 0;
- if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
- is_active(intf))
+ if (udev->state == USB_STATE_NOTATTACHED || is_active(intf))
goto done;
/* Don't let autoresume interfere with unbinding */
@@ -975,8 +979,17 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
goto done;
/* Can't resume it if it doesn't have a driver. */
- if (intf->condition == USB_INTERFACE_UNBOUND)
+ if (intf->condition == USB_INTERFACE_UNBOUND) {
+
+ /* Carry out a deferred switch to altsetting 0 */
+ if (intf->needs_altsetting0 &&
+ intf->dev.power.status == DPM_ON) {
+ usb_set_interface(udev, intf->altsetting[0].
+ desc.bInterfaceNumber, 0);
+ intf->needs_altsetting0 = 0;
+ }
goto done;
+ }
/* Don't resume if the interface is marked for rebinding */
if (intf->needs_binding)
@@ -1151,7 +1164,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
if (udev->actconfig) {
for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
- status = usb_suspend_interface(intf, msg);
+ status = usb_suspend_interface(udev, intf, msg);
if (status != 0)
break;
}
@@ -1163,7 +1176,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
if (status != 0) {
while (--i >= 0) {
intf = udev->actconfig->interface[i];
- usb_resume_interface(intf, 0);
+ usb_resume_interface(udev, intf, 0);
}
/* Try another autosuspend when the interfaces aren't busy */
@@ -1276,7 +1289,7 @@ static int usb_resume_both(struct usb_device *udev)
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
- usb_resume_interface(intf, udev->reset_resume);
+ usb_resume_interface(udev, intf, udev->reset_resume);
}
}
@@ -1605,12 +1618,10 @@ int usb_external_resume_device(struct usb_device *udev)
return status;
}
-static int usb_suspend(struct device *dev, pm_message_t message)
+int usb_suspend(struct device *dev, pm_message_t message)
{
struct usb_device *udev;
- if (!is_usb_device(dev)) /* Ignore PM for interfaces */
- return 0;
udev = to_usb_device(dev);
/* If udev is already suspended, we can skip this suspend and
@@ -1629,12 +1640,10 @@ static int usb_suspend(struct device *dev, pm_message_t message)
return usb_external_suspend_device(udev, message);
}
-static int usb_resume(struct device *dev)
+int usb_resume(struct device *dev)
{
struct usb_device *udev;
- if (!is_usb_device(dev)) /* Ignore PM for interfaces */
- return 0;
udev = to_usb_device(dev);
/* If udev->skip_sys_resume is set then udev was already suspended
@@ -1646,17 +1655,10 @@ static int usb_resume(struct device *dev)
return usb_external_resume_device(udev);
}
-#else
-
-#define usb_suspend NULL
-#define usb_resume NULL
-
#endif /* CONFIG_PM */
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
- .suspend = usb_suspend,
- .resume = usb_resume,
};
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index f7bfd72ef11..8abd4e59bf4 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -924,15 +924,6 @@ static int register_root_hub(struct usb_hcd *hcd)
return retval;
}
-void usb_enable_root_hub_irq (struct usb_bus *bus)
-{
- struct usb_hcd *hcd;
-
- hcd = container_of (bus, struct usb_hcd, self);
- if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
- hcd->driver->hub_irq_enable (hcd);
-}
-
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 5b0b59b0d89..e710ce04e22 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -212,8 +212,6 @@ struct hc_driver {
int (*bus_suspend)(struct usb_hcd *);
int (*bus_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
- void (*hub_irq_enable)(struct usb_hcd *);
- /* Needed only if port-change IRQs are level-triggered */
/* force handover of high-speed port to full-speed companion */
void (*relinquish_port)(struct usb_hcd *, int);
@@ -379,8 +377,6 @@ extern struct list_head usb_bus_list;
extern struct mutex usb_bus_list_lock;
extern wait_queue_head_t usb_kill_urb_queue;
-extern void usb_enable_root_hub_irq(struct usb_bus *bus);
-
extern int usb_find_interface_driver(struct usb_device *dev,
struct usb_interface *interface);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 107e1d25dde..6a5cb018383 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2102,8 +2102,6 @@ int usb_port_resume(struct usb_device *udev)
}
clear_bit(port1, hub->busy_bits);
- if (!hub->hdev->parent && !hub->busy_bits[0])
- usb_enable_root_hub_irq(hub->hdev->bus);
status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus);
@@ -3081,11 +3079,6 @@ static void hub_events(void)
}
}
- /* If this is a root hub, tell the HCD it's okay to
- * re-enable port-change interrupts now. */
- if (!hdev->parent && !hub->busy_bits[0])
- usb_enable_root_hub_irq(hdev->bus);
-
loop_autopm:
/* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
@@ -3311,8 +3304,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
break;
}
clear_bit(port1, parent_hub->busy_bits);
- if (!parent_hdev->parent && !parent_hub->busy_bits[0])
- usb_enable_root_hub_irq(parent_hdev->bus);
if (ret < 0)
goto re_enumerate;
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index c0b1ae25ae2..47111e88f79 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -601,15 +601,20 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
+ unsigned long flags;
- spin_lock_irq(&anchor->lock);
+ spin_lock_irqsave(&anchor->lock, flags);
while (!list_empty(&anchor->urb_list)) {
victim = list_entry(anchor->urb_list.prev, struct urb,
anchor_list);
+ usb_get_urb(victim);
+ spin_unlock_irqrestore(&anchor->lock, flags);
/* this will unanchor the URB */
usb_unlink_urb(victim);
+ usb_put_urb(victim);
+ spin_lock_irqsave(&anchor->lock, flags);
}
- spin_unlock_irq(&anchor->lock);
+ spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 84fcaa6a21e..be1fa0723f2 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -219,12 +219,6 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
}
#endif /* CONFIG_HOTPLUG */
-struct device_type usb_device_type = {
- .name = "usb_device",
- .release = usb_release_dev,
- .uevent = usb_dev_uevent,
-};
-
#ifdef CONFIG_PM
static int ksuspend_usb_init(void)
@@ -244,13 +238,80 @@ static void ksuspend_usb_cleanup(void)
destroy_workqueue(ksuspend_usb_wq);
}
+/* USB device Power-Management thunks.
+ * There's no need to distinguish here between quiescing a USB device
+ * and powering it down; the generic_suspend() routine takes care of
+ * it by skipping the usb_port_suspend() call for a quiesce. And for
+ * USB interfaces there's no difference at all.
+ */
+
+static int usb_dev_prepare(struct device *dev)
+{
+ return 0; /* Implement eventually? */
+}
+
+static void usb_dev_complete(struct device *dev)
+{
+ /* Currently used only for rebinding interfaces */
+ usb_resume(dev); /* Implement eventually? */
+}
+
+static int usb_dev_suspend(struct device *dev)
+{
+ return usb_suspend(dev, PMSG_SUSPEND);
+}
+
+static int usb_dev_resume(struct device *dev)
+{
+ return usb_resume(dev);
+}
+
+static int usb_dev_freeze(struct device *dev)
+{
+ return usb_suspend(dev, PMSG_FREEZE);
+}
+
+static int usb_dev_thaw(struct device *dev)
+{
+ return usb_resume(dev);
+}
+
+static int usb_dev_poweroff(struct device *dev)
+{
+ return usb_suspend(dev, PMSG_HIBERNATE);
+}
+
+static int usb_dev_restore(struct device *dev)
+{
+ return usb_resume(dev);
+}
+
+static struct pm_ops usb_device_pm_ops = {
+ .prepare = usb_dev_prepare,
+ .complete = usb_dev_complete,
+ .suspend = usb_dev_suspend,
+ .resume = usb_dev_resume,
+ .freeze = usb_dev_freeze,
+ .thaw = usb_dev_thaw,
+ .poweroff = usb_dev_poweroff,
+ .restore = usb_dev_restore,
+};
+
#else
#define ksuspend_usb_init() 0
#define ksuspend_usb_cleanup() do {} while (0)
+#define usb_device_pm_ops (*(struct pm_ops *)0)
#endif /* CONFIG_PM */
+struct device_type usb_device_type = {
+ .name = "usb_device",
+ .release = usb_release_dev,
+ .uevent = usb_dev_uevent,
+ .pm = &usb_device_pm_ops,
+};
+
/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
static unsigned usb_bus_is_wusb(struct usb_bus *bus)
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index d9a6e16dbf8..9a1a45ac3ad 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -41,6 +41,9 @@ extern void usb_host_cleanup(void);
#ifdef CONFIG_PM
+extern int usb_suspend(struct device *dev, pm_message_t msg);
+extern int usb_resume(struct device *dev);
+
extern void usb_autosuspend_work(struct work_struct *work);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index a28513ecbe5..7cbc78a6853 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1622,7 +1622,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
struct pxa_udc *udc = the_controller;
int retval;
- if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind
+ if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind
|| !driver->disconnect || !driver->setup)
return -EINVAL;
if (!udc)
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index d22a84f86a3..8017f1cf78e 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -988,7 +988,7 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
/*
* write bank1 address twice to ensure the 90ns delay (time
* between BANK0 write and the priv_read_copy() call is at
- * least 3*t_WHWL + 2*t_w11 = 3*25ns + 2*17ns = 92ns)
+ * least 3*t_WHWL + 2*t_w11 = 3*25ns + 2*17ns = 109ns)
*/
isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
HC_MEMORY_REG);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 6db7a2889e6..4ed228a8994 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -260,7 +260,6 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index c0948008fe3..2ac4e022a13 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -163,7 +163,6 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index cb0b506f825..fb3055f084b 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -134,7 +134,6 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 439beb784f3..7ea9a7b3115 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -36,18 +36,6 @@
/*-------------------------------------------------------------------------*/
-/* hcd->hub_irq_enable() */
-static void ohci_rhsc_enable (struct usb_hcd *hcd)
-{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-
- spin_lock_irq(&ohci->lock);
- if (!ohci->autostop)
- del_timer(&hcd->rh_timer); /* Prevent next poll */
- ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
- spin_unlock_irq(&ohci->lock);
-}
-
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
@@ -374,18 +362,28 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int any_connected)
{
int poll_rh = 1;
+ int rhsc;
+ rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC;
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:
- /* keep on polling until we know a device is connected
- * and RHSC is enabled */
+ /* If no status changes are pending, enable status-change
+ * interrupts.
+ */
+ if (!rhsc && !changed) {
+ rhsc = OHCI_INTR_RHSC;
+ ohci_writel(ohci, rhsc, &ohci->regs->intrenable);
+ }
+
+ /* Keep on polling until we know a device is connected
+ * and RHSC is enabled, or until we autostop.
+ */
if (!ohci->autostop) {
if (any_connected ||
!device_may_wakeup(&ohci_to_hcd(ohci)
->self.root_hub->dev)) {
- if (ohci_readl(ohci, &ohci->regs->intrenable) &
- OHCI_INTR_RHSC)
+ if (rhsc)
poll_rh = 0;
} else {
ohci->autostop = 1;
@@ -398,12 +396,13 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
ohci->autostop = 0;
ohci->next_statechange = jiffies +
STATECHANGE_DELAY;
- } else if (time_after_eq(jiffies,
+ } else if (rhsc && time_after_eq(jiffies,
ohci->next_statechange)
&& !ohci->ed_rm_list
&& !(ohci->hc_control &
OHCI_SCHED_ENABLES)) {
ohci_rh_suspend(ohci, 1);
+ poll_rh = 0;
}
}
break;
@@ -417,6 +416,12 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
else
usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
} else {
+ if (!rhsc && (ohci->autostop ||
+ ohci_to_hcd(ohci)->self.root_hub->
+ do_remote_wakeup))
+ ohci_writel(ohci, OHCI_INTR_RHSC,
+ &ohci->regs->intrenable);
+
/* everything is idle, no need for polling */
poll_rh = 0;
}
@@ -438,12 +443,16 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci)
static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int any_connected)
{
- int poll_rh = 1;
-
- /* keep on polling until RHSC is enabled */
+ /* If RHSC is enabled, don't poll */
if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
- poll_rh = 0;
- return poll_rh;
+ return 0;
+
+ /* If no status changes are pending, enable status-change interrupts */
+ if (!changed) {
+ ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+ return 0;
+ }
+ return 1;
}
#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 9e31d440d11..de42283149c 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -193,7 +193,6 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 3d532b70967..1eb64d08b60 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -470,7 +470,6 @@ static const struct hc_driver ohci_omap_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 083e8df0a81..a9c2ae36c7a 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -459,7 +459,6 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index b02cd076197..658a2a978c3 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -277,7 +277,6 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
- .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c
index 605d59cba28..28467e288a9 100644
--- a/drivers/usb/host/ohci-pnx8550.c
+++ b/drivers/usb/host/ohci-pnx8550.c
@@ -201,7 +201,6 @@ static const struct hc_driver ohci_pnx8550_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,