2 * Copyright (c) 2001-2002 by David Brownell
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* this file is part of ehci-hcd.c */
21 /*-------------------------------------------------------------------------*/
24 * EHCI Root Hub ... the nonsharable stuff
26 * Registers don't need cpu_to_le32, that happens transparently
29 /*-------------------------------------------------------------------------*/
31 static int check_reset_complete (
32 struct ehci_hcd *ehci,
36 if (!(port_status & PORT_CONNECT)) {
37 ehci->reset_done [index] = 0;
41 /* if reset finished and it's still not enabled -- handoff */
42 if (!(port_status & PORT_PE)) {
44 /* with integrated TT, there's nobody to hand it to! */
45 if (ehci_is_ARC(ehci)) {
47 "Failed to enable port %d on root hub TT\n",
52 ehci_dbg (ehci, "port %d full speed --> companion\n",
55 // what happens if HCS_N_CC(params) == 0 ?
56 port_status |= PORT_OWNER;
57 writel (port_status, &ehci->regs->port_status [index]);
60 ehci_dbg (ehci, "port %d high speed\n", index + 1);
65 /*-------------------------------------------------------------------------*/
68 /* build "status change" packet (one or two bytes) from HC registers */
71 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
73 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
75 int ports, i, retval = 1;
78 /* init status to no-changes */
80 ports = HCS_N_PORTS (ehci->hcs_params);
86 /* no hub change reports (bit 0) for now (power, ...) */
88 /* port N changes (bit N)? */
89 spin_lock_irqsave (&ehci->lock, flags);
90 for (i = 0; i < ports; i++) {
91 temp = readl (&ehci->regs->port_status [i]);
92 if (temp & PORT_OWNER) {
93 /* don't report this in GetPortStatus */
94 if (temp & PORT_CSC) {
96 writel (temp, &ehci->regs->port_status [i]);
100 if (!(temp & PORT_CONNECT))
101 ehci->reset_done [i] = 0;
102 if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
104 buf [0] |= 1 << (i + 1);
106 buf [1] |= 1 << (i - 7);
110 spin_unlock_irqrestore (&ehci->lock, flags);
111 return status ? retval : 0;
114 /*-------------------------------------------------------------------------*/
117 ehci_hub_descriptor (
118 struct ehci_hcd *ehci,
119 struct usb_hub_descriptor *desc
121 int ports = HCS_N_PORTS (ehci->hcs_params);
124 desc->bDescriptorType = 0x29;
125 desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
126 desc->bHubContrCurrent = 0;
128 desc->bNbrPorts = ports;
129 temp = 1 + (ports / 8);
130 desc->bDescLength = 7 + 2 * temp;
132 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
133 memset (&desc->bitmap [0], 0, temp);
134 memset (&desc->bitmap [temp], 0xff, temp);
136 temp = 0x0008; /* per-port overcurrent reporting */
137 if (HCS_PPC (ehci->hcs_params))
138 temp |= 0x0001; /* per-port power control */
139 if (HCS_INDICATOR (ehci->hcs_params))
140 temp |= 0x0080; /* per-port indicators (LEDs) */
141 desc->wHubCharacteristics = cpu_to_le16 (temp);
144 /*-------------------------------------------------------------------------*/
146 static int ehci_hub_control (
154 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
155 int ports = HCS_N_PORTS (ehci->hcs_params);
161 * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
162 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
163 * (track current state ourselves) ... blink for diagnostics,
164 * power, "this is the one", etc. EHCI spec supports this.
167 spin_lock_irqsave (&ehci->lock, flags);
169 case ClearHubFeature:
171 case C_HUB_LOCAL_POWER:
172 case C_HUB_OVER_CURRENT:
173 /* no hub-wide feature/status flags */
179 case ClearPortFeature:
180 if (!wIndex || wIndex > ports)
183 temp = readl (&ehci->regs->port_status [wIndex]);
184 if (temp & PORT_OWNER)
188 case USB_PORT_FEAT_ENABLE:
189 writel (temp & ~PORT_PE,
190 &ehci->regs->port_status [wIndex]);
192 case USB_PORT_FEAT_C_ENABLE:
193 writel (temp | PORT_PEC,
194 &ehci->regs->port_status [wIndex]);
196 case USB_PORT_FEAT_SUSPEND:
197 case USB_PORT_FEAT_C_SUSPEND:
200 case USB_PORT_FEAT_POWER:
201 if (HCS_PPC (ehci->hcs_params))
202 writel (temp & ~PORT_POWER,
203 &ehci->regs->port_status [wIndex]);
205 case USB_PORT_FEAT_C_CONNECTION:
206 writel (temp | PORT_CSC,
207 &ehci->regs->port_status [wIndex]);
209 case USB_PORT_FEAT_C_OVER_CURRENT:
210 writel (temp | PORT_OCC,
211 &ehci->regs->port_status [wIndex]);
213 case USB_PORT_FEAT_C_RESET:
214 /* GetPortStatus clears reset */
219 readl (&ehci->regs->command); /* unblock posted write */
221 case GetHubDescriptor:
222 ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
226 /* no hub-wide feature/status flags */
228 //cpu_to_le32s ((u32 *) buf);
231 if (!wIndex || wIndex > ports)
235 temp = readl (&ehci->regs->port_status [wIndex]);
239 status |= 1 << USB_PORT_FEAT_C_CONNECTION;
241 status |= 1 << USB_PORT_FEAT_C_ENABLE;
242 // USB_PORT_FEAT_C_SUSPEND
244 status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
246 /* whoever resets must GetPortStatus to complete it!! */
247 if ((temp & PORT_RESET)
248 && time_after (jiffies,
249 ehci->reset_done [wIndex])) {
250 status |= 1 << USB_PORT_FEAT_C_RESET;
252 /* force reset to complete */
253 writel (temp & ~PORT_RESET,
254 &ehci->regs->port_status [wIndex]);
256 &ehci->regs->port_status [wIndex],
259 ehci_err (ehci, "port %d reset error %d\n",
264 /* see what we found out */
265 temp = check_reset_complete (ehci, wIndex,
266 readl (&ehci->regs->port_status [wIndex]));
269 // don't show wPortStatus if it's owned by a companion hc
270 if (!(temp & PORT_OWNER)) {
271 if (temp & PORT_CONNECT) {
272 status |= 1 << USB_PORT_FEAT_CONNECTION;
273 // status may be from integrated TT
274 status |= ehci_port_speed(ehci, temp);
277 status |= 1 << USB_PORT_FEAT_ENABLE;
278 if (temp & PORT_SUSPEND)
279 status |= 1 << USB_PORT_FEAT_SUSPEND;
281 status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
282 if (temp & PORT_RESET)
283 status |= 1 << USB_PORT_FEAT_RESET;
284 if (temp & PORT_POWER)
285 status |= 1 << USB_PORT_FEAT_POWER;
288 #ifndef EHCI_VERBOSE_DEBUG
289 if (status & ~0xffff) /* only if wPortChange is interesting */
291 dbg_port (ehci, "GetStatus", wIndex + 1, temp);
292 // we "know" this alignment is good, caller used kmalloc()...
293 *((u32 *) buf) = cpu_to_le32 (status);
297 case C_HUB_LOCAL_POWER:
298 case C_HUB_OVER_CURRENT:
299 /* no hub-wide feature/status flags */
306 if (!wIndex || wIndex > ports)
309 temp = readl (&ehci->regs->port_status [wIndex]);
310 if (temp & PORT_OWNER)
314 case USB_PORT_FEAT_SUSPEND:
315 writel (temp | PORT_SUSPEND,
316 &ehci->regs->port_status [wIndex]);
318 case USB_PORT_FEAT_POWER:
319 if (HCS_PPC (ehci->hcs_params))
320 writel (temp | PORT_POWER,
321 &ehci->regs->port_status [wIndex]);
323 case USB_PORT_FEAT_RESET:
324 /* line status bits may report this as low speed,
325 * which can be fine if this root hub has a
326 * transaction translator built in.
328 if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
329 && !ehci_is_ARC(ehci)
330 && PORT_USB11 (temp)) {
332 "port %d low speed --> companion\n",
336 ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
341 * caller must wait, then call GetPortStatus
342 * usb 2.0 spec says 50 ms resets on root
344 ehci->reset_done [wIndex] = jiffies
345 + ((50 /* msec */ * HZ) / 1000);
347 writel (temp, &ehci->regs->port_status [wIndex]);
352 readl (&ehci->regs->command); /* unblock posted writes */
357 /* "stall" on error */
360 spin_unlock_irqrestore (&ehci->lock, flags);