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