📄 ehci-hub.c
字号:
/* * Copyright (C) 2001-2004 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* this file is part of ehci-hcd.c *//*-------------------------------------------------------------------------*//* * EHCI Root Hub ... the nonsharable stuff * * Registers don't need cpu_to_le32, that happens transparently *//*-------------------------------------------------------------------------*/#ifdef CONFIG_PMstatic int ehci_bus_suspend (struct usb_hcd *hcd){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; if (time_before (jiffies, ehci->next_statechange)) msleep(5); port = HCS_N_PORTS (ehci->hcs_params); spin_lock_irq (&ehci->lock); /* stop schedules, clean any completed work */ if (HC_IS_RUNNING(hcd->state)) { ehci_quiesce (ehci); hcd->state = HC_STATE_QUIESCING; } ehci->command = readl (&ehci->regs->command); if (ehci->reclaim) ehci->reclaim_ready = 1; ehci_work(ehci, NULL); /* suspend any active/unsuspended ports, maybe allow wakeup */ while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; u32 t1 = readl (reg) & ~PORT_RWC_BITS; u32 t2 = t1; if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) t2 |= PORT_SUSPEND; if (device_may_wakeup(&hcd->self.root_hub->dev)) t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; else t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); writel (t2, reg); } } /* turn off now-idle HC */ del_timer_sync (&ehci->watchdog); ehci_halt (ehci); hcd->state = HC_STATE_SUSPENDED; ehci->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irq (&ehci->lock); return 0;}/* caller has locked the root hub, and should reset/reinit on error */static int ehci_bus_resume (struct usb_hcd *hcd){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; int i; int intr_enable; if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); /* 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. */ /* re-init operational registers in case we lost power */ if (readl (&ehci->regs->intr_enable) == 0) { /* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready. */ intr_enable = 1; writel (0, &ehci->regs->segment); writel (ehci->periodic_dma, &ehci->regs->frame_list); writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); } else intr_enable = 0; ehci_dbg(ehci, "resume root hub%s\n", intr_enable ? " after power loss" : ""); /* restore CMD_RUN, framelist size, and irq threshold */ writel (ehci->command, &ehci->regs->command); /* take ports out of suspend */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { temp = readl (&ehci->regs->port_status [i]); temp &= ~(PORT_RWC_BITS | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); if (temp & PORT_SUSPEND) { ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); temp |= PORT_RESUME; } writel (temp, &ehci->regs->port_status [i]); } i = HCS_N_PORTS (ehci->hcs_params); mdelay (20); while (i--) { temp = readl (&ehci->regs->port_status [i]); if ((temp & PORT_SUSPEND) == 0) continue; temp &= ~(PORT_RWC_BITS | PORT_RESUME); writel (temp, &ehci->regs->port_status [i]); ehci_vdbg (ehci, "resumed port %d\n", i + 1); } (void) readl (&ehci->regs->command); /* maybe re-activate the schedule(s) */ temp = 0; if (ehci->async->qh_next.qh) temp |= CMD_ASE; if (ehci->periodic_sched) temp |= CMD_PSE; if (temp) { ehci->command |= temp; writel (ehci->command, &ehci->regs->command); } ehci->next_statechange = jiffies + msecs_to_jiffies(5); hcd->state = HC_STATE_RUNNING; /* Now we can safely re-enable irqs */ if (intr_enable) writel (INTR_MASK, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); return 0;}#else#define ehci_bus_suspend NULL#define ehci_bus_resume NULL#endif /* CONFIG_PM *//*-------------------------------------------------------------------------*/static int check_reset_complete ( struct ehci_hcd *ehci, int index, int port_status) { if (!(port_status & PORT_CONNECT)) { ehci->reset_done [index] = 0; 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; writel (port_status, &ehci->regs->port_status [index]); } else ehci_dbg (ehci, "port %d high speed\n", index + 1); return port_status;}/*-------------------------------------------------------------------------*//* build "status change" packet (one or two bytes) from HC registers */static intehci_hub_status_data (struct usb_hcd *hcd, char *buf){ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp, status = 0; int ports, i, retval = 1; unsigned long flags; /* if !USB_SUSPEND, root hub timers won't get shut down ... */ if (!HC_IS_RUNNING(hcd->state)) return 0; /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) { buf [1] = 0; retval++; } /* no hub change reports (bit 0) for now (power, ...) */ /* port N changes (bit N)? */ spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < ports; i++) { temp = readl (&ehci->regs->port_status [i]); if (temp & PORT_OWNER) { /* don't report this in GetPortStatus */ if (temp & PORT_CSC) { temp &= ~PORT_RWC_BITS; temp |= PORT_CSC; writel (temp, &ehci->regs->port_status [i]); } continue; } if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0 // PORT_STAT_C_SUSPEND? || ((temp & PORT_RESUME) != 0 && time_after (jiffies, ehci->reset_done [i]))) { if (i < 7) buf [0] |= 1 << (i + 1); else buf [1] |= 1 << (i - 7); status = STS_PCD; } } /* FIXME autosuspend idle root hubs */ spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0;}/*-------------------------------------------------------------------------*/static voidehci_hub_descriptor ( struct ehci_hcd *ehci, struct usb_hub_descriptor *desc) { int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -