summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ohci-hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r--drivers/usb/host/ohci-hub.c103
1 files changed, 65 insertions, 38 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 2347ab83f046..b3d734ab6201 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-1.0+
/*
* OHCI HCD (Host Controller Driver) for USB.
*
@@ -17,21 +18,21 @@
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
- label, num, temp, \
- (temp & RH_PS_PRSC) ? " PRSC" : "", \
- (temp & RH_PS_OCIC) ? " OCIC" : "", \
- (temp & RH_PS_PSSC) ? " PSSC" : "", \
- (temp & RH_PS_PESC) ? " PESC" : "", \
- (temp & RH_PS_CSC) ? " CSC" : "", \
+ label, num, value, \
+ (value & RH_PS_PRSC) ? " PRSC" : "", \
+ (value & RH_PS_OCIC) ? " OCIC" : "", \
+ (value & RH_PS_PSSC) ? " PSSC" : "", \
+ (value & RH_PS_PESC) ? " PESC" : "", \
+ (value & RH_PS_CSC) ? " CSC" : "", \
\
- (temp & RH_PS_LSDA) ? " LSDA" : "", \
- (temp & RH_PS_PPS) ? " PPS" : "", \
- (temp & RH_PS_PRS) ? " PRS" : "", \
- (temp & RH_PS_POCI) ? " POCI" : "", \
- (temp & RH_PS_PSS) ? " PSS" : "", \
+ (value & RH_PS_LSDA) ? " LSDA" : "", \
+ (value & RH_PS_PPS) ? " PPS" : "", \
+ (value & RH_PS_PRS) ? " PRS" : "", \
+ (value & RH_PS_POCI) ? " POCI" : "", \
+ (value & RH_PS_PSS) ? " PSS" : "", \
\
- (temp & RH_PS_PES) ? " PES" : "", \
- (temp & RH_PS_CCS) ? " CCS" : "" \
+ (value & RH_PS_PES) ? " PES" : "", \
+ (value & RH_PS_CCS) ? " CCS" : "" \
);
/*-------------------------------------------------------------------------*/
@@ -39,8 +40,8 @@
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
-static void dl_done_list (struct ohci_hcd *);
-static void finish_unlinks (struct ohci_hcd *, u16);
+static void update_done_list(struct ohci_hcd *);
+static void ohci_work(struct ohci_hcd *);
#ifdef CONFIG_PM
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
@@ -57,7 +58,7 @@ __acquires(ohci->lock)
ohci->hc_control |= OHCI_USB_RESET;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
- /* FALL THROUGH */
+ fallthrough;
case OHCI_USB_RESET:
status = -EBUSY;
ohci_dbg (ohci, "needs reinit!\n");
@@ -87,8 +88,29 @@ __acquires(ohci->lock)
msleep (8);
spin_lock_irq (&ohci->lock);
}
- dl_done_list (ohci);
- finish_unlinks (ohci, ohci_frame_no(ohci));
+ update_done_list(ohci);
+ ohci_work(ohci);
+
+ /* All ED unlinks should be finished, no need for SOF interrupts */
+ ohci_writel(ohci, OHCI_INTR_SF, &ohci->regs->intrdisable);
+
+ /*
+ * Some controllers don't handle "global" suspend properly if
+ * there are unsuspended ports. For these controllers, put all
+ * the enabled ports into suspend before suspending the root hub.
+ */
+ if (ohci->flags & OHCI_QUIRK_GLOBAL_SUSPEND) {
+ __hc32 __iomem *portstat = ohci->regs->roothub.portstatus;
+ int i;
+ unsigned temp;
+
+ for (i = 0; i < ohci->num_ports; (++i, ++portstat)) {
+ temp = ohci_readl(ohci, portstat);
+ if ((temp & (RH_PS_PES | RH_PS_PSS)) ==
+ RH_PS_PES)
+ ohci_writel(ohci, RH_PS_PSS, portstat);
+ }
+ }
/* maybe resume can wake root hub */
if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
@@ -212,10 +234,11 @@ __acquires(ohci->lock)
/* Sometimes PCI D3 suspend trashes frame timings ... */
periodic_reinit (ohci);
- /* the following code is executed with ohci->lock held and
- * irqs disabled if and only if autostopped is true
+ /*
+ * The following code is executed with ohci->lock held and
+ * irqs disabled if and only if autostopped is true. This
+ * will cause sparse to warn about a "context imbalance".
*/
-
skip_resume:
/* interrupts might have been disabled */
ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
@@ -290,6 +313,11 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
else
rc = ohci_rh_suspend (ohci, 0);
spin_unlock_irq (&ohci->lock);
+
+ if (rc == 0) {
+ timer_delete_sync(&ohci->io_watchdog);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
+ }
return rc;
}
@@ -437,8 +465,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
/* build "status change" packet (one or two bytes) from HC registers */
-static int
-ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
+int ohci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
@@ -503,6 +530,7 @@ done:
return changed ? length : 0;
}
+EXPORT_SYMBOL_GPL(ohci_hub_status_data);
/*-------------------------------------------------------------------------*/
@@ -514,7 +542,7 @@ ohci_hub_descriptor (
u32 rh = roothub_a (ohci);
u16 temp;
- desc->bDescriptorType = 0x29;
+ desc->bDescriptorType = USB_DT_HUB;
desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
desc->bHubContrCurrent = 0;
@@ -522,16 +550,16 @@ ohci_hub_descriptor (
temp = 1 + (ohci->num_ports / 8);
desc->bDescLength = 7 + 2 * temp;
- temp = 0;
+ temp = HUB_CHAR_COMMON_LPSM | HUB_CHAR_COMMON_OCPM;
if (rh & RH_A_NPS) /* no power switching? */
- temp |= 0x0002;
+ temp |= HUB_CHAR_NO_LPSM;
if (rh & RH_A_PSM) /* per-port power switching? */
- temp |= 0x0001;
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
if (rh & RH_A_NOCP) /* no overcurrent reporting? */
- temp |= 0x0010;
+ temp |= HUB_CHAR_NO_OCPM;
else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
- temp |= 0x0008;
- desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
+ temp |= HUB_CHAR_INDV_PORT_OCPM;
+ desc->wHubCharacteristics = cpu_to_le16(temp);
/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
rh = roothub_b (ohci);
@@ -563,7 +591,7 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
if (!(status & RH_PS_CCS))
return -ENODEV;
- /* khubd will finish the reset later */
+ /* hub_wq will finish the reset later */
ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
return 0;
}
@@ -588,7 +616,7 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
/* wrap-aware logic morphed from <linux/jiffies.h> */
#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
-/* called from some task, normally khubd */
+/* called from some task, normally hub_wq */
static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
{
__hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
@@ -645,7 +673,7 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
return 0;
}
-static int ohci_hub_control (
+int ohci_hub_control(
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
@@ -667,6 +695,7 @@ static int ohci_hub_control (
case C_HUB_OVER_CURRENT:
ohci_writel (ohci, RH_HS_OCIC,
&ohci->regs->roothub.status);
+ break;
case C_HUB_LOCAL_POWER:
break;
default:
@@ -724,10 +753,8 @@ static int ohci_hub_control (
temp = roothub_portstatus (ohci, wIndex);
put_unaligned_le32(temp, buf);
-#ifndef OHCI_VERBOSE_DEBUG
- if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
-#endif
- dbg_port (ohci, "GetStatus", wIndex, temp);
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+ dbg_port(ohci, "GetStatus", wIndex, temp);
break;
case SetHubFeature:
switch (wValue) {
@@ -773,4 +800,4 @@ error:
}
return retval;
}
-
+EXPORT_SYMBOL_GPL(ohci_hub_control);