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)) {
43 ehci_dbg (ehci, "port %d full speed --> companion\n",
46 // what happens if HCS_N_CC(params) == 0 ?
47 port_status |= PORT_OWNER;
48 writel (port_status, &ehci->regs->port_status [index]);
51 ehci_dbg (ehci, "port %d high speed\n", index + 1);
56 /*-------------------------------------------------------------------------*/
59 /* build "status change" packet (one or two bytes) from HC registers */
62 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
64 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
66 int ports, i, retval = 1;
69 /* init status to no-changes */
71 ports = HCS_N_PORTS (ehci->hcs_params);
77 /* no hub change reports (bit 0) for now (power, ...) */
79 /* port N changes (bit N)? */
80 spin_lock_irqsave (&ehci->lock, flags);
81 for (i = 0; i < ports; i++) {
82 temp = readl (&ehci->regs->port_status [i]);
83 if (temp & PORT_OWNER) {
84 /* don't report this in GetPortStatus */
85 if (temp & PORT_CSC) {
87 writel (temp, &ehci->regs->port_status [i]);
91 if (!(temp & PORT_CONNECT))
92 ehci->reset_done [i] = 0;
93 if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
95 buf [0] |= 1 << (i + 1);
97 buf [1] |= 1 << (i - 7);
101 spin_unlock_irqrestore (&ehci->lock, flags);
102 return status ? retval : 0;
105 /*-------------------------------------------------------------------------*/
108 ehci_hub_descriptor (
109 struct ehci_hcd *ehci,
110 struct usb_hub_descriptor *desc
112 int ports = HCS_N_PORTS (ehci->hcs_params);
115 desc->bDescriptorType = 0x29;
116 desc->bPwrOn2PwrGood = 10; /* FIXME: f(system power) */
117 desc->bHubContrCurrent = 0;
119 desc->bNbrPorts = ports;
120 temp = 1 + (ports / 8);
121 desc->bDescLength = 7 + 2 * temp;
123 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
124 memset (&desc->bitmap [0], 0, temp);
125 memset (&desc->bitmap [temp], 0xff, temp);
127 temp = 0x0008; /* per-port overcurrent reporting */
128 if (HCS_PPC (ehci->hcs_params))
129 temp |= 0x0001; /* per-port power control */
130 if (HCS_INDICATOR (ehci->hcs_params))
131 temp |= 0x0080; /* per-port indicators (LEDs) */
132 desc->wHubCharacteristics = cpu_to_le16 (temp);
135 /*-------------------------------------------------------------------------*/
137 static int ehci_hub_control (
145 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
146 int ports = HCS_N_PORTS (ehci->hcs_params);
152 * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
153 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
154 * (track current state ourselves) ... blink for diagnostics,
155 * power, "this is the one", etc. EHCI spec supports this.
158 spin_lock_irqsave (&ehci->lock, flags);
160 case ClearHubFeature:
162 case C_HUB_LOCAL_POWER:
163 case C_HUB_OVER_CURRENT:
164 /* no hub-wide feature/status flags */
170 case ClearPortFeature:
171 if (!wIndex || wIndex > ports)
174 temp = readl (&ehci->regs->port_status [wIndex]);
175 if (temp & PORT_OWNER)
179 case USB_PORT_FEAT_ENABLE:
180 writel (temp & ~PORT_PE,
181 &ehci->regs->port_status [wIndex]);
183 case USB_PORT_FEAT_C_ENABLE:
184 writel (temp | PORT_PEC,
185 &ehci->regs->port_status [wIndex]);
187 case USB_PORT_FEAT_SUSPEND:
188 case USB_PORT_FEAT_C_SUSPEND:
191 case USB_PORT_FEAT_POWER:
192 if (HCS_PPC (ehci->hcs_params))
193 writel (temp & ~PORT_POWER,
194 &ehci->regs->port_status [wIndex]);
196 case USB_PORT_FEAT_C_CONNECTION:
197 writel (temp | PORT_CSC,
198 &ehci->regs->port_status [wIndex]);
200 case USB_PORT_FEAT_C_OVER_CURRENT:
201 writel (temp | PORT_OCC,
202 &ehci->regs->port_status [wIndex]);
204 case USB_PORT_FEAT_C_RESET:
205 /* GetPortStatus clears reset */
210 readl (&ehci->regs->command); /* unblock posted write */
212 case GetHubDescriptor:
213 ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
217 /* no hub-wide feature/status flags */
219 //cpu_to_le32s ((u32 *) buf);
222 if (!wIndex || wIndex > ports)
226 temp = readl (&ehci->regs->port_status [wIndex]);
230 status |= 1 << USB_PORT_FEAT_C_CONNECTION;
232 status |= 1 << USB_PORT_FEAT_C_ENABLE;
233 // USB_PORT_FEAT_C_SUSPEND
235 status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
237 /* whoever resets must GetPortStatus to complete it!! */
238 if ((temp & PORT_RESET)
239 && time_after (jiffies,
240 ehci->reset_done [wIndex])) {
241 status |= 1 << USB_PORT_FEAT_C_RESET;
243 /* force reset to complete */
244 writel (temp & ~PORT_RESET,
245 &ehci->regs->port_status [wIndex]);
248 &ehci->regs->port_status [wIndex]);
250 } while (temp & PORT_RESET);
252 /* see what we found out */
253 temp = check_reset_complete (ehci, wIndex, temp);
256 // don't show wPortStatus if it's owned by a companion hc
257 if (!(temp & PORT_OWNER)) {
258 if (temp & PORT_CONNECT) {
259 status |= 1 << USB_PORT_FEAT_CONNECTION;
260 status |= 1 << USB_PORT_FEAT_HIGHSPEED;
263 status |= 1 << USB_PORT_FEAT_ENABLE;
264 if (temp & PORT_SUSPEND)
265 status |= 1 << USB_PORT_FEAT_SUSPEND;
267 status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
268 if (temp & PORT_RESET)
269 status |= 1 << USB_PORT_FEAT_RESET;
270 if (temp & PORT_POWER)
271 status |= 1 << USB_PORT_FEAT_POWER;
274 #ifndef EHCI_VERBOSE_DEBUG
275 if (status & ~0xffff) /* only if wPortChange is interesting */
277 dbg_port (ehci, "GetStatus", wIndex + 1, temp);
278 // we "know" this alignment is good, caller used kmalloc()...
279 *((u32 *) buf) = cpu_to_le32 (status);
283 case C_HUB_LOCAL_POWER:
284 case C_HUB_OVER_CURRENT:
285 /* no hub-wide feature/status flags */
292 if (!wIndex || wIndex > ports)
295 temp = readl (&ehci->regs->port_status [wIndex]);
296 if (temp & PORT_OWNER)
300 case USB_PORT_FEAT_SUSPEND:
301 writel (temp | PORT_SUSPEND,
302 &ehci->regs->port_status [wIndex]);
304 case USB_PORT_FEAT_POWER:
305 if (HCS_PPC (ehci->hcs_params))
306 writel (temp | PORT_POWER,
307 &ehci->regs->port_status [wIndex]);
309 case USB_PORT_FEAT_RESET:
310 /* line status bits may report this as low speed */
311 if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
312 && PORT_USB11 (temp)) {
314 "port %d low speed --> companion\n",
318 ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
323 * caller must wait, then call GetPortStatus
324 * usb 2.0 spec says 50 ms resets on root
326 ehci->reset_done [wIndex] = jiffies
327 + ((50 /* msec */ * HZ) / 1000);
329 writel (temp, &ehci->regs->port_status [wIndex]);
334 readl (&ehci->regs->command); /* unblock posted writes */
339 /* "stall" on error */
342 spin_unlock_irqrestore (&ehci->lock, flags);