/* After a power loss, ports that were owned by the companion must be *resetsothatthecompanioncanstillownthem.
*/ 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);
/* 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, *butifsomethingwentwrongtheportmustnot *remainenabled.
*/
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 *forthecontroller,wemustadjustalltheportwakeupflags *whenthecontrollerissuspendedorresumed.Inallother *casestheydon'tneedtobechanged.
*/ 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. *Ifweareresumingthecontroller,setthewakeupflags.
*/ 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 *inprogresswon'tcomplete.Henceifremotewakeupisenabled *fortheroothubandanyportsareinthemiddleofaresumeor *remotewakeup,wemustfailthesuspend.
*/ 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;
}
}
/* 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 *hardwarecannotgophylowpowermode.Ifarace *conditionhappenshere(connectionchangeduringbits *set),theportchangedetectionwillfinallyfixit.
*/ if (t1 & PORT_CONNECT)
t2 |= PORT_WKOC_E | PORT_WKDISC_E; else
t2 |= PORT_WKOC_E | PORT_WKCONN_E;
}
/* 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 *waslost.(ForPCI,thatmeansVauxwasmaintained.)Butwe *couldinsteadberestoringaswsuspsnapshot--sothatBIOSwas *thelastuserofthecontroller,notreset/pmhardwarekeeping *statewegavetoit.
*/
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 *IRQsrightaway,sodelaythemuntilwe'reready.
*/
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
/* 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);
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);
}
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. *Setappropriatebitthuscouldputphyintolowpower *modeifwehavetdi_phy_lpmfeature
*/
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, *whichcanbefineifthisroothubhasa *transactiontranslatorbuiltin.
*/ 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;
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.