Loading drivers/usb/core/hcd.c +44 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ #include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> #include <linux/pm_runtime.h> #include <linux/usb.h> #include <linux/usb/hcd.h> Loading Loading @@ -1029,6 +1030,49 @@ static int register_root_hub(struct usb_hcd *hcd) return retval; } /* * usb_hcd_start_port_resume - a root-hub port is sending a resume signal * @bus: the bus which the root hub belongs to * @portnum: the port which is being resumed * * HCDs should call this function when they know that a resume signal is * being sent to a root-hub port. The root hub will be prevented from * going into autosuspend until usb_hcd_end_port_resume() is called. * * The bus's private lock must be held by the caller. */ void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum) { unsigned bit = 1 << portnum; if (!(bus->resuming_ports & bit)) { bus->resuming_ports |= bit; pm_runtime_get_noresume(&bus->root_hub->dev); } } EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume); /* * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal * @bus: the bus which the root hub belongs to * @portnum: the port which is being resumed * * HCDs should call this function when they know that a resume signal has * stopped being sent to a root-hub port. The root hub will be allowed to * autosuspend again. * * The bus's private lock must be held by the caller. */ void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum) { unsigned bit = 1 << portnum; if (bus->resuming_ports & bit) { bus->resuming_ports &= ~bit; pm_runtime_put_noidle(&bus->root_hub->dev); } } EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume); /*-------------------------------------------------------------------------*/ Loading drivers/usb/core/hub.c +52 −18 Original line number Diff line number Diff line Loading @@ -2822,6 +2822,23 @@ void usb_enable_ltm(struct usb_device *udev) EXPORT_SYMBOL_GPL(usb_enable_ltm); #ifdef CONFIG_USB_SUSPEND /* * usb_disable_function_remotewakeup - disable usb3.0 * device's function remote wakeup * @udev: target device * * Assume there's only one function on the USB 3.0 * device and disable remote wake for the first * interface. FIXME if the interface association * descriptor shows there's more than one function. */ static int usb_disable_function_remotewakeup(struct usb_device *udev) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } /* * usb_port_suspend - suspend a usb device's upstream port Loading Loading @@ -2939,12 +2956,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ if (udev->do_remote_wakeup) (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, if (udev->do_remote_wakeup) { if (!hub_is_superspeed(hub->hdev)) { (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } else (void) usb_disable_function_remotewakeup(udev); } /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) Loading Loading @@ -3051,8 +3075,9 @@ static int finish_port_resume(struct usb_device *udev) * udev->reset_resume */ } else if (udev->actconfig && !udev->reset_resume) { if (!hub_is_superspeed(udev->parent)) { le16_to_cpus(&devstatus); if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, Loading @@ -3060,11 +3085,20 @@ static int finish_port_resume(struct usb_device *udev) USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } else { status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, &devstatus); le16_to_cpus(&devstatus); if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW)) status = usb_disable_function_remotewakeup(udev); } if (status) dev_dbg(&udev->dev, "disable remote wakeup, status %d\n", status); } status = 0; } return status; Loading drivers/usb/host/ehci-hcd.c +1 −0 Original line number Diff line number Diff line Loading @@ -797,6 +797,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); set_bit(i, &ehci->resuming_ports); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); usb_hcd_start_port_resume(&hcd->self, i); mod_timer(&hcd->rh_timer, ehci->reset_done[i]); } } Loading drivers/usb/host/ehci-hub.c +8 −1 Original line number Diff line number Diff line Loading @@ -649,7 +649,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) status = STS_PCD; } } /* FIXME autosuspend idle root hubs */ /* If a resume is in progress, make sure it can finish */ if (ehci->resuming_ports) mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } Loading Loading @@ -851,6 +855,7 @@ static int ehci_hub_control ( /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); usb_hcd_start_port_resume(&hcd->self, wIndex); /* check the port again */ mod_timer(&ehci_to_hcd(ehci)->rh_timer, ehci->reset_done[wIndex]); Loading @@ -862,6 +867,7 @@ static int ehci_hub_control ( clear_bit(wIndex, &ehci->suspended_ports); set_bit(wIndex, &ehci->port_c_suspend); ehci->reset_done[wIndex] = 0; usb_hcd_end_port_resume(&hcd->self, wIndex); /* stop resume signaling */ temp = ehci_readl(ehci, status_reg); Loading Loading @@ -950,6 +956,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); usb_hcd_end_port_resume(&hcd->self, wIndex); } if (temp & PORT_OC) Loading drivers/usb/host/ehci-q.c +30 −20 Original line number Diff line number Diff line Loading @@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) if (ehci->async_iaa || ehci->async_unlinking) return; /* Do all the waiting QHs at once */ /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { /* Do all the waiting QHs */ ehci->async_iaa = ehci->async_unlink; ehci->async_unlink = NULL; /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { if (!nested) /* Avoid recursion */ end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { struct ehci_qh *qh; /* Do only the first waiting QH (nVidia bug?) */ qh = ehci->async_unlink; ehci->async_iaa = qh; ehci->async_unlink = qh->unlink_next; qh->unlink_next = NULL; /* Make sure the unlinks are all visible to the hardware */ wmb(); Loading Loading @@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci) } } static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); static void unlink_empty_async(struct ehci_hcd *ehci) { struct ehci_qh *qh, *next; bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); struct ehci_qh *qh; struct ehci_qh *qh_to_unlink = NULL; bool check_unlinks_later = false; int count = 0; /* Unlink all the async QHs that have been empty for a timer cycle */ next = ehci->async->qh_next.qh; while (next) { qh = next; next = qh->qh_next.qh; /* Find the last async QH which has been empty for a timer cycle */ for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) { if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { if (!stopped && qh->unlink_cycle == ehci->async_unlink_cycle) ++count; if (qh->unlink_cycle == ehci->async_unlink_cycle) check_unlinks_later = true; else single_unlink_async(ehci, qh); qh_to_unlink = qh; } } /* Start a new IAA cycle if any QHs are waiting for it */ if (ehci->async_unlink) start_iaa_cycle(ehci, false); /* If nothing else is being unlinked, unlink the last empty QH */ if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) { start_unlink_async(ehci, qh_to_unlink); --count; } /* QHs that haven't been empty for long enough will be handled later */ if (check_unlinks_later) { /* Other QHs will be handled later */ if (count > 0) { ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); ++ehci->async_unlink_cycle; } Loading Loading
drivers/usb/core/hcd.c +44 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ #include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> #include <linux/pm_runtime.h> #include <linux/usb.h> #include <linux/usb/hcd.h> Loading Loading @@ -1029,6 +1030,49 @@ static int register_root_hub(struct usb_hcd *hcd) return retval; } /* * usb_hcd_start_port_resume - a root-hub port is sending a resume signal * @bus: the bus which the root hub belongs to * @portnum: the port which is being resumed * * HCDs should call this function when they know that a resume signal is * being sent to a root-hub port. The root hub will be prevented from * going into autosuspend until usb_hcd_end_port_resume() is called. * * The bus's private lock must be held by the caller. */ void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum) { unsigned bit = 1 << portnum; if (!(bus->resuming_ports & bit)) { bus->resuming_ports |= bit; pm_runtime_get_noresume(&bus->root_hub->dev); } } EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume); /* * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal * @bus: the bus which the root hub belongs to * @portnum: the port which is being resumed * * HCDs should call this function when they know that a resume signal has * stopped being sent to a root-hub port. The root hub will be allowed to * autosuspend again. * * The bus's private lock must be held by the caller. */ void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum) { unsigned bit = 1 << portnum; if (bus->resuming_ports & bit) { bus->resuming_ports &= ~bit; pm_runtime_put_noidle(&bus->root_hub->dev); } } EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume); /*-------------------------------------------------------------------------*/ Loading
drivers/usb/core/hub.c +52 −18 Original line number Diff line number Diff line Loading @@ -2822,6 +2822,23 @@ void usb_enable_ltm(struct usb_device *udev) EXPORT_SYMBOL_GPL(usb_enable_ltm); #ifdef CONFIG_USB_SUSPEND /* * usb_disable_function_remotewakeup - disable usb3.0 * device's function remote wakeup * @udev: target device * * Assume there's only one function on the USB 3.0 * device and disable remote wake for the first * interface. FIXME if the interface association * descriptor shows there's more than one function. */ static int usb_disable_function_remotewakeup(struct usb_device *udev) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } /* * usb_port_suspend - suspend a usb device's upstream port Loading Loading @@ -2939,12 +2956,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ if (udev->do_remote_wakeup) (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, if (udev->do_remote_wakeup) { if (!hub_is_superspeed(hub->hdev)) { (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } else (void) usb_disable_function_remotewakeup(udev); } /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) Loading Loading @@ -3051,8 +3075,9 @@ static int finish_port_resume(struct usb_device *udev) * udev->reset_resume */ } else if (udev->actconfig && !udev->reset_resume) { if (!hub_is_superspeed(udev->parent)) { le16_to_cpus(&devstatus); if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, Loading @@ -3060,11 +3085,20 @@ static int finish_port_resume(struct usb_device *udev) USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } else { status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, &devstatus); le16_to_cpus(&devstatus); if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW)) status = usb_disable_function_remotewakeup(udev); } if (status) dev_dbg(&udev->dev, "disable remote wakeup, status %d\n", status); } status = 0; } return status; Loading
drivers/usb/host/ehci-hcd.c +1 −0 Original line number Diff line number Diff line Loading @@ -797,6 +797,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); set_bit(i, &ehci->resuming_ports); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); usb_hcd_start_port_resume(&hcd->self, i); mod_timer(&hcd->rh_timer, ehci->reset_done[i]); } } Loading
drivers/usb/host/ehci-hub.c +8 −1 Original line number Diff line number Diff line Loading @@ -649,7 +649,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) status = STS_PCD; } } /* FIXME autosuspend idle root hubs */ /* If a resume is in progress, make sure it can finish */ if (ehci->resuming_ports) mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } Loading Loading @@ -851,6 +855,7 @@ static int ehci_hub_control ( /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); usb_hcd_start_port_resume(&hcd->self, wIndex); /* check the port again */ mod_timer(&ehci_to_hcd(ehci)->rh_timer, ehci->reset_done[wIndex]); Loading @@ -862,6 +867,7 @@ static int ehci_hub_control ( clear_bit(wIndex, &ehci->suspended_ports); set_bit(wIndex, &ehci->port_c_suspend); ehci->reset_done[wIndex] = 0; usb_hcd_end_port_resume(&hcd->self, wIndex); /* stop resume signaling */ temp = ehci_readl(ehci, status_reg); Loading Loading @@ -950,6 +956,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); usb_hcd_end_port_resume(&hcd->self, wIndex); } if (temp & PORT_OC) Loading
drivers/usb/host/ehci-q.c +30 −20 Original line number Diff line number Diff line Loading @@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) if (ehci->async_iaa || ehci->async_unlinking) return; /* Do all the waiting QHs at once */ /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { /* Do all the waiting QHs */ ehci->async_iaa = ehci->async_unlink; ehci->async_unlink = NULL; /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { if (!nested) /* Avoid recursion */ end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { struct ehci_qh *qh; /* Do only the first waiting QH (nVidia bug?) */ qh = ehci->async_unlink; ehci->async_iaa = qh; ehci->async_unlink = qh->unlink_next; qh->unlink_next = NULL; /* Make sure the unlinks are all visible to the hardware */ wmb(); Loading Loading @@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci) } } static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); static void unlink_empty_async(struct ehci_hcd *ehci) { struct ehci_qh *qh, *next; bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); struct ehci_qh *qh; struct ehci_qh *qh_to_unlink = NULL; bool check_unlinks_later = false; int count = 0; /* Unlink all the async QHs that have been empty for a timer cycle */ next = ehci->async->qh_next.qh; while (next) { qh = next; next = qh->qh_next.qh; /* Find the last async QH which has been empty for a timer cycle */ for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) { if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { if (!stopped && qh->unlink_cycle == ehci->async_unlink_cycle) ++count; if (qh->unlink_cycle == ehci->async_unlink_cycle) check_unlinks_later = true; else single_unlink_async(ehci, qh); qh_to_unlink = qh; } } /* Start a new IAA cycle if any QHs are waiting for it */ if (ehci->async_unlink) start_iaa_cycle(ehci, false); /* If nothing else is being unlinked, unlink the last empty QH */ if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) { start_unlink_async(ehci, qh_to_unlink); --count; } /* QHs that haven't been empty for long enough will be handled later */ if (check_unlinks_later) { /* Other QHs will be handled later */ if (count > 0) { ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); ++ehci->async_unlink_cycle; } Loading