/* After a power loss, ports that were owned by the companion must be * reset so that the companion can still own them.
*/ staticvoid ehci_handover_companion_ports(struct ehci_hcd *ehci)
{
u32 __iomem *reg;
u32 status; int port;
__le32 buf; struct usb_hcd *hcd = ehci_to_hcd(ehci);
if (!ehci->owned_ports) return;
/* * USB 1.1 devices are mostly HIDs, which don't need to persist across * suspends. If we ensure that none of our companion's devices have * persist_enabled (by looking through all USB 1.1 buses in the system), * we can skip this and avoid slowing resume down. Devices without * persist will just get reenumerated shortly after resume anyway.
*/ if (!usb_for_each_dev(NULL, persist_enabled_on_companion)) return;
/* Make sure the ports are powered */
port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; if (!(status & PORT_POWER))
ehci_port_power(ehci, port, true);
}
}
/* Give the connections some time to appear */
msleep(20);
spin_lock_irq(&ehci->lock);
port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
/* Port already owned by companion? */ if (status & PORT_OWNER)
clear_bit(port, &ehci->owned_ports); elseif (test_bit(port, &ehci->companion_ports))
ehci_writel(ehci, status & ~PORT_PE, reg); else {
spin_unlock_irq(&ehci->lock);
ehci_hub_control(hcd, SetPortFeature,
USB_PORT_FEAT_RESET, port + 1,
NULL, 0);
spin_lock_irq(&ehci->lock);
}
}
}
spin_unlock_irq(&ehci->lock);
if (!ehci->owned_ports) return;
msleep(90); /* Wait for resets to complete */
spin_lock_irq(&ehci->lock);
port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) {
spin_unlock_irq(&ehci->lock);
ehci_hub_control(hcd, GetPortStatus,
0, port + 1,
(char *) &buf, sizeof(buf));
spin_lock_irq(&ehci->lock);
/* The companion should now own the port, * but if something went wrong the port must not * remain enabled.
*/
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; if (status & PORT_OWNER)
ehci_writel(ehci, status | PORT_CSC, reg); else {
ehci_dbg(ehci, "failed handover port %d: %x\n",
port + 1, status);
ehci_writel(ehci, status & ~PORT_PE, reg);
}
}
}
/* If remote wakeup is enabled for the root hub but disabled * for the controller, we must adjust all the port wakeup flags * when the controller is suspended or resumed. In all other * cases they don't need to be changed.
*/ if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup) return;
spin_lock_irq(&ehci->lock);
/* clear phy low-power mode before changing wakeup flags */ if (ehci->has_tdi_phy_lpm) {
port = HCS_N_PORTS(ehci->hcs_params); while (port--) {
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
/* If we are suspending the controller, clear the flags. * If we are resuming the controller, set the wakeup flags.
*/ if (!suspending) { if (t1 & PORT_CONNECT)
t2 |= PORT_WKOC_E | PORT_WKDISC_E; else
t2 |= PORT_WKOC_E | PORT_WKCONN_E;
}
ehci_writel(ehci, t2, reg);
}
/* enter phy low-power mode again */ if (ehci->has_tdi_phy_lpm) {
port = HCS_N_PORTS(ehci->hcs_params); while (port--) {
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
staticint ehci_bus_suspend (struct usb_hcd *hcd)
{ struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; int mask; int changed; bool fs_idle_delay;
ehci_dbg(ehci, "suspend root hub\n");
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
/* stop the schedules */
ehci_quiesce(ehci);
spin_lock_irq (&ehci->lock); if (ehci->rh_state < EHCI_RH_RUNNING) goto done;
/* Once the controller is stopped, port resumes that are already * in progress won't complete. Hence if remote wakeup is enabled * for the root hub and any ports are in the middle of a resume or * remote wakeup, we must fail the suspend.
*/ if (hcd->self.root_hub->do_remote_wakeup) { if (ehci->resuming_ports) {
spin_unlock_irq(&ehci->lock);
ehci_dbg(ehci, "suspend failed because a port is resuming\n"); return -EBUSY;
}
}
/* Unlike other USB host controller types, EHCI doesn't have * any notion of "global" or bus-wide suspend. The driver has * to manually suspend all the active unsuspended ports, and * then manually resume them in the bus_resume() routine.
*/
ehci->bus_suspended = 0;
ehci->owned_ports = 0;
changed = 0;
fs_idle_delay = false;
port = HCS_N_PORTS(ehci->hcs_params); while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1 & ~PORT_WAKE_BITS;
/* keep track of which ports we suspend */ if (t1 & PORT_OWNER)
set_bit(port, &ehci->owned_ports); elseif ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
t2 |= PORT_SUSPEND;
set_bit(port, &ehci->bus_suspended);
}
/* enable remote wakeup on all ports, if told to do so */ if (hcd->self.root_hub->do_remote_wakeup) { /* only enable appropriate wake bits, otherwise the * hardware can not go phy low power mode. If a race * condition happens here(connection change during bits * set), the port change detection will finally fix it.
*/ if (t1 & PORT_CONNECT)
t2 |= PORT_WKOC_E | PORT_WKDISC_E; else
t2 |= PORT_WKOC_E | PORT_WKCONN_E;
}
if (t1 != t2) { /* * On some controllers, Wake-On-Disconnect will * generate false wakeup signals until the bus * switches over to full-speed idle. For their * sake, add a delay if we need one.
*/ if ((t2 & PORT_WKDISC_E) &&
ehci_port_speed(ehci, t2) ==
USB_PORT_STAT_HIGH_SPEED)
fs_idle_delay = true;
ehci_writel(ehci, t2, reg);
changed = 1;
}
}
spin_unlock_irq(&ehci->lock);
if (changed && ehci_has_fsl_susp_errata(ehci)) /* * Wait for at least 10 millisecondes to ensure the controller * enter the suspend status before initiating a port resume * using the Force Port Resume bit (Not-EHCI compatible).
*/
usleep_range(10000, 20000);
if ((changed && ehci->has_tdi_phy_lpm) || fs_idle_delay) { /* * Wait for HCD to enter low-power mode or for the bus * to switch to full-speed idle.
*/
usleep_range(5000, 5500);
}
if (changed && ehci->has_tdi_phy_lpm) {
spin_lock_irq(&ehci->lock);
port = HCS_N_PORTS(ehci->hcs_params); while (port--) {
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
u32 t3;
/* Apparently some devices need a >= 1-uframe delay here */ if (ehci->bus_suspended)
udelay(150);
/* turn off now-idle HC */
ehci_halt (ehci);
spin_lock_irq(&ehci->lock); if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD))
ehci_handle_controller_death(ehci); if (ehci->rh_state != EHCI_RH_RUNNING) goto done;
ehci->rh_state = EHCI_RH_SUSPENDED;
unlink_empty_async_suspended(ehci);
/* Some Synopsys controllers mistakenly leave IAA turned on */
ehci_writel(ehci, STS_IAA, &ehci->regs->status);
/* Any IAA cycle that started before the suspend is now invalid */
end_iaa_cycle(ehci);
ehci_handle_start_intr_unlinks(ehci);
ehci_handle_intr_unlinks(ehci);
end_free_itds(ehci);
/* caller has locked the root hub, and should reset/reinit on error */ staticint ehci_bus_resume (struct usb_hcd *hcd)
{ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
u32 power_okay; int i; unsignedlong resume_needed = 0;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
spin_lock_irq (&ehci->lock); if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown) goto shutdown;
if (unlikely(ehci->debug)) { if (!dbgp_reset_prep(hcd))
ehci->debug = NULL; else
dbgp_external_startup(hcd);
}
/* Ideally and we've got a real resume here, and no port's power * was lost. (For PCI, that means Vaux was maintained.) But we * could instead be restoring a swsusp snapshot -- so that BIOS was * the last user of the controller, not reset/pm hardware keeping * state we gave to it.
*/
power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
ehci_dbg(ehci, "resume root hub%s\n",
power_okay ? "" : " after power loss");
/* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready.
*/
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
/* * According to Bugzilla #8190, the port status for some controllers * will be wrong without a delay. At their wrong status, the port * is enabled, but not suspended neither resumed.
*/
i = HCS_N_PORTS(ehci->hcs_params); while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status[i]); if ((temp & PORT_PE) &&
!(temp & (PORT_SUSPEND | PORT_RESUME))) {
ehci_dbg(ehci, "Port status(0x%x) is wrong\n", temp);
spin_unlock_irq(&ehci->lock);
msleep(8);
spin_lock_irq(&ehci->lock); break;
}
}
if (ehci->shutdown) goto shutdown;
/* clear phy low-power mode before resume */ if (ehci->bus_suspended && ehci->has_tdi_phy_lpm) {
i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) {
u32 __iomem *hostpc_reg =
&ehci->regs->hostpc[i];
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params); while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(i, &ehci->bus_suspended) &&
(temp & PORT_SUSPEND)) {
temp |= PORT_RESUME;
set_bit(i, &resume_needed);
}
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
}
/* * msleep for USB_RESUME_TIMEOUT ms only if code is trying to resume * port
*/ if (resume_needed) {
spin_unlock_irq(&ehci->lock);
msleep(USB_RESUME_TIMEOUT);
spin_lock_irq(&ehci->lock); if (ehci->shutdown) goto shutdown;
}
i = HCS_N_PORTS (ehci->hcs_params); while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status [i]); if (test_bit(i, &resume_needed)) {
temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
}
}
/* Now we can safely re-enable irqs */
spin_lock_irq(&ehci->lock); if (ehci->shutdown) goto shutdown;
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
(void) ehci_readl(ehci, &ehci->regs->intr_enable);
spin_unlock_irq(&ehci->lock);
/* * Sets the owner of a port
*/ staticvoid set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
{
u32 __iomem *status_reg;
u32 port_status; inttry;
status_reg = &ehci->regs->port_status[portnum];
/* * The controller won't set the OWNER bit if the port is * enabled, so this loop will sometimes require at least two * iterations: one to disable the port and one to set OWNER.
*/ for (try = 4; try > 0; --try) {
spin_lock_irq(&ehci->lock);
port_status = ehci_readl(ehci, status_reg); if ((port_status & PORT_OWNER) == new_owner
|| (port_status & (PORT_OWNER | PORT_CONNECT))
== 0) try = 0; else {
port_status ^= PORT_OWNER;
port_status &= ~(PORT_PE | PORT_RWC_BITS);
ehci_writel(ehci, port_status, status_reg);
}
spin_unlock_irq(&ehci->lock); if (try > 1)
msleep(5);
}
}
staticint check_reset_complete ( struct ehci_hcd *ehci, int index,
u32 __iomem *status_reg, int port_status
) { if (!(port_status & PORT_CONNECT)) return port_status;
/* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) {
/* with integrated TT, there's nobody to hand it to! */ if (ehci_is_TDI(ehci)) {
ehci_dbg (ehci, "Failed to enable port %d on root hub TT\n",
index+1); return port_status;
}
ehci_dbg (ehci, "port %d full speed --> companion\n",
index + 1);
// what happens if HCS_N_CC(params) == 0 ?
port_status |= PORT_OWNER;
port_status &= ~PORT_RWC_BITS;
ehci_writel(ehci, port_status, status_reg);
/* ensure 440EPX ohci controller state is operational */ if (ehci->has_amcc_usb23)
set_ohci_hcfs(ehci, 1);
} else {
ehci_dbg(ehci, "port %d reset complete, port enabled\n",
index + 1); /* ensure 440EPx ohci controller state is suspended */ if (ehci->has_amcc_usb23)
set_ohci_hcfs(ehci, 0);
}
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) {
buf [1] = 0;
retval++;
}
/* Inform the core about resumes-in-progress by returning * a non-zero value even if there are no status changes.
*/
status = ehci->resuming_ports;
/* Some boards (mostly VIA?) report bogus overcurrent indications, * causing massive log spam unless we completely ignore them. It * may be relevant that VIA VT8235 controllers, where PORT_POWER is * always set, seem to clear PORT_OCC and PORT_CSC when writing to * PORT_POWER; that's surprising, but maybe within-spec.
*/ if (!ignore_oc && !ehci->spurious_oc)
mask = PORT_CSC | PORT_PEC | PORT_OCC; else
mask = PORT_CSC | PORT_PEC; // PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND
/* no hub change reports (bit 0) for now (power, ...) */
/* port N changes (bit N)? */
spin_lock_irqsave (&ehci->lock, flags);
/* get per-port change detect bits */ if (ehci->has_ppcd)
ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
for (i = 0; i < ports; i++) { /* leverage per-port change bits feature */ if (ppcd & (1 << i))
temp = ehci_readl(ehci, &ehci->regs->port_status[i]); else
temp = 0;
/* * Return status information even for ports with OWNER set. * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user.
*/
if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
|| (ehci->reset_done[i] && time_after_eq(
jiffies, ehci->reset_done[i]))
|| ehci_has_ci_pec_bug(ehci, temp)) { if (i < 7)
buf [0] |= 1 << (i + 1); else
buf [1] |= 1 << (i - 7);
status = STS_PCD;
}
}
/* 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;
}
/* * Avoid out-of-bounds values while calculating the port index * from wIndex. The compiler doesn't like pointers to invalid * addresses, even if they are never used.
*/
temp = (wIndex - 1) & 0xff; if (temp >= HCS_N_PORTS_MAX)
temp = 0;
status_reg = &ehci->regs->port_status[temp];
hostpc_reg = &ehci->regs->hostpc[temp];
/* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. * (track current state ourselves) ... blink for diagnostics, * power, "this is the one", etc. EHCI spec supports this.
*/
spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error;
} break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error;
wIndex--;
temp = ehci_readl(ehci, status_reg);
temp &= ~PORT_RWC_BITS;
/* * Even if OWNER is set, so the port is owned by the * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION).
*/
switch (wValue) { case USB_PORT_FEAT_ENABLE:
ehci_writel(ehci, temp & ~PORT_PE, status_reg); break; case USB_PORT_FEAT_C_ENABLE:
ehci_writel(ehci, temp | PORT_PEC, status_reg); break; case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) goto error; if (ehci->no_selective_suspend) break; #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1))
&& hcd->self.b_hnp_enable) {
otg_start_hnp(hcd->usb_phy->otg); break;
} #endif if (!(temp & PORT_SUSPEND)) break; if ((temp & PORT_PE) == 0) goto error;
/* clear phy low-power mode before resume */ if (ehci->has_tdi_phy_lpm) {
temp1 = ehci_readl(ehci, hostpc_reg);
ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
hostpc_reg);
spin_unlock_irqrestore(&ehci->lock, flags);
msleep(5);/* wait to leave low-power mode */
spin_lock_irqsave(&ehci->lock, flags);
} /* resume signaling for 20 msec */
temp &= ~PORT_WAKE_BITS;
ehci_writel(ehci, temp | PORT_RESUME, status_reg);
ehci->reset_done[wIndex] = jiffies
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
set_bit(wIndex, &ehci->resuming_ports);
usb_hcd_start_port_resume(&hcd->self, wIndex); break; case USB_PORT_FEAT_C_SUSPEND:
clear_bit(wIndex, &ehci->port_c_suspend); break; case USB_PORT_FEAT_POWER: if (HCS_PPC(ehci->hcs_params)) {
spin_unlock_irqrestore(&ehci->lock, flags);
ehci_port_power(ehci, wIndex, false);
spin_lock_irqsave(&ehci->lock, flags);
} break; case USB_PORT_FEAT_C_CONNECTION:
ehci_writel(ehci, temp | PORT_CSC, status_reg); break; case USB_PORT_FEAT_C_OVER_CURRENT:
ehci_writel(ehci, temp | PORT_OCC, status_reg); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ break; default: goto error;
}
ehci_readl(ehci, &ehci->regs->command); /* unblock posted write */ break; case GetHubDescriptor:
ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
buf); break; case GetHubStatus: /* no hub-wide feature/status flags */
memset (buf, 0, 4); //cpu_to_le32s ((u32 *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error;
wIndex--;
status = 0;
temp = ehci_readl(ehci, status_reg);
// wPortChange bits if (temp & PORT_CSC)
status |= USB_PORT_STAT_C_CONNECTION << 16; if (temp & PORT_PEC)
status |= USB_PORT_STAT_C_ENABLE << 16;
if (ehci_has_ci_pec_bug(ehci, temp)) {
status |= USB_PORT_STAT_C_ENABLE << 16;
ehci_info(ehci, "PE is cleared by HW port:%d PORTSC:%08x\n",
wIndex + 1, temp);
}
if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
/* * Hubs should disable port power on over-current. * However, not all EHCI implementations do this * automatically, even if they _do_ support per-port * power switching; they're allowed to just limit the * current. hub_wq will turn the power back on.
*/ if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle))
&& HCS_PPC(ehci->hcs_params)) {
spin_unlock_irqrestore(&ehci->lock, flags);
ehci_port_power(ehci, wIndex, false);
spin_lock_irqsave(&ehci->lock, flags);
temp = ehci_readl(ehci, status_reg);
}
}
/* no reset or resume pending */ if (!ehci->reset_done[wIndex]) {
/* Remote Wakeup received? */ if (temp & PORT_RESUME) { /* resume signaling for 20 msec */
ehci->reset_done[wIndex] = jiffies
+ msecs_to_jiffies(20);
usb_hcd_start_port_resume(&hcd->self, wIndex);
set_bit(wIndex, &ehci->resuming_ports); /* check the port again */
mod_timer(&ehci_to_hcd(ehci)->rh_timer,
ehci->reset_done[wIndex]);
}
/* reset or resume not yet complete */
} elseif (!time_after_eq(jiffies, ehci->reset_done[wIndex])) {
; /* wait until it is complete */
/* whoever resets must GetPortStatus to complete it!! */
} else {
status |= USB_PORT_STAT_C_RESET << 16;
ehci->reset_done [wIndex] = 0;
/* force reset to complete */
ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
status_reg); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely...
*/
retval = ehci_handshake(ehci, status_reg,
PORT_RESET, 0, 1000); if (retval != 0) {
ehci_err (ehci, "port %d reset error %d\n",
wIndex + 1, retval); goto error;
}
/* see what we found out */
temp = check_reset_complete (ehci, wIndex, status_reg,
ehci_readl(ehci, status_reg));
}
/* transfer dedicated ports to the companion hc */ if ((temp & PORT_CONNECT) &&
test_bit(wIndex, &ehci->companion_ports)) {
temp &= ~PORT_RWC_BITS;
temp |= PORT_OWNER;
ehci_writel(ehci, temp, status_reg);
ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1);
temp = ehci_readl(ehci, status_reg);
}
/* * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway).
*/
if (temp & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION; // status may be from integrated TT if (ehci->has_hostpc) {
temp1 = ehci_readl(ehci, hostpc_reg);
status |= ehci_port_speed(ehci, temp1);
} else
status |= ehci_port_speed(ehci, temp);
} if (temp & PORT_PE)
status |= USB_PORT_STAT_ENABLE;
/* maybe the port was unsuspended without our knowledge */ if (temp & (PORT_SUSPEND|PORT_RESUME)) {
status |= USB_PORT_STAT_SUSPEND;
} elseif (test_bit(wIndex, &ehci->suspended_ports)) {
clear_bit(wIndex, &ehci->suspended_ports);
clear_bit(wIndex, &ehci->resuming_ports);
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)
status |= USB_PORT_STAT_OVERCURRENT; if (temp & PORT_RESET)
status |= USB_PORT_STAT_RESET; if (temp & PORT_POWER)
status |= USB_PORT_STAT_POWER; if (test_bit(wIndex, &ehci->port_c_suspend))
status |= USB_PORT_STAT_C_SUSPEND << 16;
if (status & ~0xffff) /* only if wPortChange is interesting */
dbg_port(ehci, "GetStatus", wIndex + 1, temp);
put_unaligned_le32(status, buf); break; case SetHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error;
} break; case SetPortFeature:
selector = wIndex >> 8;
wIndex &= 0xff; if (unlikely(ehci->debug)) { /* If the debug port is active any port
* feature requests should get denied */ if (wIndex == HCS_DEBUG_PORT(ehci->hcs_params) &&
(readl(&ehci->debug->control) & DBGP_ENABLED)) {
retval = -ENODEV; goto error_exit;
}
} if (!wIndex || wIndex > ports) goto error;
wIndex--;
temp = ehci_readl(ehci, status_reg); if (temp & PORT_OWNER) break;
temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (ehci->no_selective_suspend) break; if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0) goto error;
/* After above check the port must be connected. * Set appropriate bit thus could put phy into low power * mode if we have tdi_phy_lpm feature
*/
temp &= ~PORT_WKCONN_E;
temp |= PORT_WKDISC_E | PORT_WKOC_E;
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); if (ehci->has_tdi_phy_lpm) {
spin_unlock_irqrestore(&ehci->lock, flags);
msleep(5);/* 5ms for HCD enter low pwr mode */
spin_lock_irqsave(&ehci->lock, flags);
temp1 = ehci_readl(ehci, hostpc_reg);
ehci_writel(ehci, temp1 | HOSTPC_PHCD,
hostpc_reg);
temp1 = ehci_readl(ehci, hostpc_reg);
ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
wIndex, (temp1 & HOSTPC_PHCD) ? "succeeded" : "failed");
} if (ehci_has_fsl_susp_errata(ehci)) { /* 10ms for HCD enter suspend */
spin_unlock_irqrestore(&ehci->lock, flags);
usleep_range(10000, 20000);
spin_lock_irqsave(&ehci->lock, flags);
}
set_bit(wIndex, &ehci->suspended_ports); break; case USB_PORT_FEAT_POWER: if (HCS_PPC(ehci->hcs_params)) {
spin_unlock_irqrestore(&ehci->lock, flags);
ehci_port_power(ehci, wIndex, true);
spin_lock_irqsave(&ehci->lock, flags);
} break; case USB_PORT_FEAT_RESET: if (temp & (PORT_SUSPEND|PORT_RESUME)) goto error; /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in.
*/ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& !ehci_is_TDI(ehci)
&& PORT_USB11 (temp)) {
ehci_dbg (ehci, "port %d low speed --> companion\n",
wIndex + 1);
temp |= PORT_OWNER;
} else {
temp |= PORT_RESET;
temp &= ~PORT_PE;
/* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root
*/
ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (50);
/* * Force full-speed connect for FSL high-speed * erratum; disable HS Chirp by setting PFSC bit
*/ if (ehci_has_fsl_hs_errata(ehci))
temp |= (1 << PORTSC_FSL_PFSC);
}
ehci_writel(ehci, temp, status_reg); break;
/* For downstream facing ports (these): one hub port is put * into test mode according to USB2 11.24.2.13, then the hub * must be reset (which for root hub now means rmmod+modprobe, * or else system reboot). See EHCI 2.3.9 and 4.14 for info * about the EHCI-specific stuff.
*/ case USB_PORT_FEAT_TEST: #ifdef CONFIG_USB_HCD_TEST_MODE if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
spin_unlock_irqrestore(&ehci->lock, flags);
retval = ehset_single_step_set_feature(hcd,
wIndex + 1);
spin_lock_irqsave(&ehci->lock, flags); break;
} #endif if (!selector || selector > 5) goto error;
spin_unlock_irqrestore(&ehci->lock, flags);
ehci_quiesce(ehci);
spin_lock_irqsave(&ehci->lock, flags);
/* Put all enabled ports into suspend */ while (ports--) {
u32 __iomem *sreg =
&ehci->regs->port_status[ports];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.