1 /*
2 * Copyright (c) 2001-2002 by David Brownell
3 *
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.
8 *
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
12 * for more details.
13 *
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.
17 */
18
19 /* this file is part of ehci-hcd.c */
20
21 /*-------------------------------------------------------------------------*/
22
23 /*
24 * EHCI Root Hub ... the nonsharable stuff
25 *
26 * Registers don't need cpu_to_le32, that happens transparently
27 */
28
29 /*-------------------------------------------------------------------------*/
30
check_reset_complete(struct ehci_hcd * ehci,int index,int port_status)31 static int check_reset_complete (
32 struct ehci_hcd *ehci,
33 int index,
34 int port_status
35 ) {
36 if (!(port_status & PORT_CONNECT)) {
37 ehci->reset_done [index] = 0;
38 return port_status;
39 }
40
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",
44 index + 1);
45
46 // what happens if HCS_N_CC(params) == 0 ?
47 port_status |= PORT_OWNER;
48 writel (port_status, &ehci->regs->port_status [index]);
49
50 } else
51 ehci_dbg (ehci, "port %d high speed\n", index + 1);
52
53 return port_status;
54 }
55
56 /*-------------------------------------------------------------------------*/
57
58
59 /* build "status change" packet (one or two bytes) from HC registers */
60
61 static int
ehci_hub_status_data(struct usb_hcd * hcd,char * buf)62 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
63 {
64 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
65 u32 temp, status = 0;
66 int ports, i, retval = 1;
67 unsigned long flags;
68
69 /* init status to no-changes */
70 buf [0] = 0;
71 ports = HCS_N_PORTS (ehci->hcs_params);
72 if (ports > 7) {
73 buf [1] = 0;
74 retval++;
75 }
76
77 /* no hub change reports (bit 0) for now (power, ...) */
78
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) {
86 temp &= ~PORT_CSC;
87 writel (temp, &ehci->regs->port_status [i]);
88 }
89 continue;
90 }
91 if (!(temp & PORT_CONNECT))
92 ehci->reset_done [i] = 0;
93 if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
94 if (i < 7)
95 buf [0] |= 1 << (i + 1);
96 else
97 buf [1] |= 1 << (i - 7);
98 status = STS_PCD;
99 }
100 }
101 spin_unlock_irqrestore (&ehci->lock, flags);
102 return status ? retval : 0;
103 }
104
105 /*-------------------------------------------------------------------------*/
106
107 static void
ehci_hub_descriptor(struct ehci_hcd * ehci,struct usb_hub_descriptor * desc)108 ehci_hub_descriptor (
109 struct ehci_hcd *ehci,
110 struct usb_hub_descriptor *desc
111 ) {
112 int ports = HCS_N_PORTS (ehci->hcs_params);
113 u16 temp;
114
115 desc->bDescriptorType = 0x29;
116 desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
117 desc->bHubContrCurrent = 0;
118
119 desc->bNbrPorts = ports;
120 temp = 1 + (ports / 8);
121 desc->bDescLength = 7 + 2 * temp;
122
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);
126
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);
133 }
134
135 /*-------------------------------------------------------------------------*/
136
ehci_hub_control(struct usb_hcd * hcd,u16 typeReq,u16 wValue,u16 wIndex,char * buf,u16 wLength)137 static int ehci_hub_control (
138 struct usb_hcd *hcd,
139 u16 typeReq,
140 u16 wValue,
141 u16 wIndex,
142 char *buf,
143 u16 wLength
144 ) {
145 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
146 int ports = HCS_N_PORTS (ehci->hcs_params);
147 u32 temp, status;
148 unsigned long flags;
149 int retval = 0;
150
151 /*
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.
156 */
157
158 spin_lock_irqsave (&ehci->lock, flags);
159 switch (typeReq) {
160 case ClearHubFeature:
161 switch (wValue) {
162 case C_HUB_LOCAL_POWER:
163 case C_HUB_OVER_CURRENT:
164 /* no hub-wide feature/status flags */
165 break;
166 default:
167 goto error;
168 }
169 break;
170 case ClearPortFeature:
171 if (!wIndex || wIndex > ports)
172 goto error;
173 wIndex--;
174 temp = readl (&ehci->regs->port_status [wIndex]);
175 if (temp & PORT_OWNER)
176 break;
177
178 switch (wValue) {
179 case USB_PORT_FEAT_ENABLE:
180 writel (temp & ~PORT_PE,
181 &ehci->regs->port_status [wIndex]);
182 break;
183 case USB_PORT_FEAT_C_ENABLE:
184 writel (temp | PORT_PEC,
185 &ehci->regs->port_status [wIndex]);
186 break;
187 case USB_PORT_FEAT_SUSPEND:
188 case USB_PORT_FEAT_C_SUSPEND:
189 /* ? */
190 break;
191 case USB_PORT_FEAT_POWER:
192 if (HCS_PPC (ehci->hcs_params))
193 writel (temp & ~PORT_POWER,
194 &ehci->regs->port_status [wIndex]);
195 break;
196 case USB_PORT_FEAT_C_CONNECTION:
197 writel (temp | PORT_CSC,
198 &ehci->regs->port_status [wIndex]);
199 break;
200 case USB_PORT_FEAT_C_OVER_CURRENT:
201 writel (temp | PORT_OCC,
202 &ehci->regs->port_status [wIndex]);
203 break;
204 case USB_PORT_FEAT_C_RESET:
205 /* GetPortStatus clears reset */
206 break;
207 default:
208 goto error;
209 }
210 readl (&ehci->regs->command); /* unblock posted write */
211 break;
212 case GetHubDescriptor:
213 ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
214 buf);
215 break;
216 case GetHubStatus:
217 /* no hub-wide feature/status flags */
218 memset (buf, 0, 4);
219 //cpu_to_le32s ((u32 *) buf);
220 break;
221 case GetPortStatus:
222 if (!wIndex || wIndex > ports)
223 goto error;
224 wIndex--;
225 status = 0;
226 temp = readl (&ehci->regs->port_status [wIndex]);
227
228 // wPortChange bits
229 if (temp & PORT_CSC)
230 status |= 1 << USB_PORT_FEAT_C_CONNECTION;
231 if (temp & PORT_PEC)
232 status |= 1 << USB_PORT_FEAT_C_ENABLE;
233 // USB_PORT_FEAT_C_SUSPEND
234 if (temp & PORT_OCC)
235 status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
236
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;
242
243 /* force reset to complete */
244 writel (temp & ~PORT_RESET,
245 &ehci->regs->port_status [wIndex]);
246 do {
247 temp = readl (
248 &ehci->regs->port_status [wIndex]);
249 udelay (10);
250 } while (temp & PORT_RESET);
251
252 /* see what we found out */
253 temp = check_reset_complete (ehci, wIndex, temp);
254 }
255
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;
261 }
262 if (temp & PORT_PE)
263 status |= 1 << USB_PORT_FEAT_ENABLE;
264 if (temp & PORT_SUSPEND)
265 status |= 1 << USB_PORT_FEAT_SUSPEND;
266 if (temp & PORT_OC)
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;
272 }
273
274 #ifndef EHCI_VERBOSE_DEBUG
275 if (status & ~0xffff) /* only if wPortChange is interesting */
276 #endif
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);
280 break;
281 case SetHubFeature:
282 switch (wValue) {
283 case C_HUB_LOCAL_POWER:
284 case C_HUB_OVER_CURRENT:
285 /* no hub-wide feature/status flags */
286 break;
287 default:
288 goto error;
289 }
290 break;
291 case SetPortFeature:
292 if (!wIndex || wIndex > ports)
293 goto error;
294 wIndex--;
295 temp = readl (&ehci->regs->port_status [wIndex]);
296 if (temp & PORT_OWNER)
297 break;
298
299 switch (wValue) {
300 case USB_PORT_FEAT_SUSPEND:
301 writel (temp | PORT_SUSPEND,
302 &ehci->regs->port_status [wIndex]);
303 break;
304 case USB_PORT_FEAT_POWER:
305 if (HCS_PPC (ehci->hcs_params))
306 writel (temp | PORT_POWER,
307 &ehci->regs->port_status [wIndex]);
308 break;
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)) {
313 ehci_dbg (ehci,
314 "port %d low speed --> companion\n",
315 wIndex + 1);
316 temp |= PORT_OWNER;
317 } else {
318 ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
319 temp |= PORT_RESET;
320 temp &= ~PORT_PE;
321
322 /*
323 * caller must wait, then call GetPortStatus
324 * usb 2.0 spec says 50 ms resets on root
325 */
326 ehci->reset_done [wIndex] = jiffies
327 + ((50 /* msec */ * HZ) / 1000);
328 }
329 writel (temp, &ehci->regs->port_status [wIndex]);
330 break;
331 default:
332 goto error;
333 }
334 readl (&ehci->regs->command); /* unblock posted writes */
335 break;
336
337 default:
338 error:
339 /* "stall" on error */
340 retval = -EPIPE;
341 }
342 spin_unlock_irqrestore (&ehci->lock, flags);
343 return retval;
344 }
345