[PATCH] USB UHCI: Add root hub states
[deliverable/linux.git] / drivers / usb / host / uhci-hub.c
CommitLineData
1da177e4
LT
1/*
2 * Universal Host Controller Interface driver for USB.
3 *
4 * Maintainer: Alan Stern <stern@rowland.harvard.edu>
5 *
6 * (C) Copyright 1999 Linus Torvalds
7 * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
8 * (C) Copyright 1999 Randy Dunlap
9 * (C) Copyright 1999 Georg Acher, acher@in.tum.de
10 * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
11 * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
12 * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
13 */
14
15static __u8 root_hub_hub_des[] =
16{
17 0x09, /* __u8 bLength; */
18 0x29, /* __u8 bDescriptorType; Hub-descriptor */
19 0x02, /* __u8 bNbrPorts; */
20 0x0a, /* __u16 wHubCharacteristics; */
21 0x00, /* (per-port OC, no power switching) */
22 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
23 0x00, /* __u8 bHubContrCurrent; 0 mA */
24 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
25 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
26};
27
28#define UHCI_RH_MAXCHILD 7
29
30/* must write as zeroes */
31#define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
32
33/* status change bits: nonzero writes will clear */
34#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
35
f5946f82
AS
36/* A port that either is connected or has a changed-bit set will prevent
37 * us from AUTO_STOPPING.
38 */
39static int any_ports_active(struct uhci_hcd *uhci)
40{
41 int port;
42
43 for (port = 0; port < uhci->rh_numports; ++port) {
44 if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) &
45 (USBPORTSC_CCS | RWC_BITS)) ||
46 test_bit(port, &uhci->port_c_suspend))
47 return 1;
48 }
49 return 0;
50}
51
1da177e4
LT
52static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
53{
54 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
55 int port;
56
57 *buf = 0;
58 for (port = 0; port < uhci->rh_numports; ++port) {
59 if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
60 test_bit(port, &uhci->port_c_suspend))
61 *buf |= (1 << (port + 1));
62 }
c8f4fe43 63 if (*buf && uhci->is_stopped)
1da177e4
LT
64 uhci->resume_detect = 1;
65 return !!*buf;
66}
67
68#define OK(x) len = (x); break
69
70#define CLR_RH_PORTSTAT(x) \
71 status = inw(port_addr); \
72 status &= ~(RWC_BITS|WZ_BITS); \
73 status &= ~(x); \
74 status |= RWC_BITS & (x); \
75 outw(status, port_addr)
76
77#define SET_RH_PORTSTAT(x) \
78 status = inw(port_addr); \
79 status |= (x); \
80 status &= ~(RWC_BITS|WZ_BITS); \
81 outw(status, port_addr)
82
83/* UHCI controllers don't automatically stop resume signalling after 20 msec,
84 * so we have to poll and check timeouts in order to take care of it.
85 */
86static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
87 unsigned long port_addr)
88{
89 int status;
90
91 if (test_bit(port, &uhci->suspended_ports)) {
92 CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
93 clear_bit(port, &uhci->suspended_ports);
94 clear_bit(port, &uhci->resuming_ports);
95 set_bit(port, &uhci->port_c_suspend);
96
97 /* The controller won't actually turn off the RD bit until
98 * it has had a chance to send a low-speed EOP sequence,
99 * which takes 3 bit times (= 2 microseconds). We'll delay
100 * slightly longer for good luck. */
101 udelay(4);
102 }
103}
104
105static void uhci_check_ports(struct uhci_hcd *uhci)
106{
107 unsigned int port;
108 unsigned long port_addr;
109 int status;
110
111 for (port = 0; port < uhci->rh_numports; ++port) {
112 port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
113 status = inw(port_addr);
114 if (unlikely(status & USBPORTSC_PR)) {
115 if (time_after_eq(jiffies, uhci->ports_timeout)) {
116 CLR_RH_PORTSTAT(USBPORTSC_PR);
117 udelay(10);
118
119 /* If the port was enabled before, turning
120 * reset on caused a port enable change.
121 * Turning reset off causes a port connect
122 * status change. Clear these changes. */
123 CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
124 SET_RH_PORTSTAT(USBPORTSC_PE);
125 }
126 }
127 if (unlikely(status & USBPORTSC_RD)) {
128 if (!test_bit(port, &uhci->resuming_ports)) {
129
130 /* Port received a wakeup request */
131 set_bit(port, &uhci->resuming_ports);
132 uhci->ports_timeout = jiffies +
133 msecs_to_jiffies(20);
134 } else if (time_after_eq(jiffies,
135 uhci->ports_timeout)) {
136 uhci_finish_suspend(uhci, port, port_addr);
137 }
138 }
139 }
140}
141
142/* size of returned buffer is part of USB spec */
143static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
144 u16 wIndex, char *buf, u16 wLength)
145{
146 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
147 int status, lstatus, retval = 0, len = 0;
148 unsigned int port = wIndex - 1;
149 unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
150 u16 wPortChange, wPortStatus;
151 unsigned long flags;
152
153 spin_lock_irqsave(&uhci->lock, flags);
154 switch (typeReq) {
155
156 case GetHubStatus:
157 *(__le32 *)buf = cpu_to_le32(0);
158 OK(4); /* hub power */
159 case GetPortStatus:
160 if (port >= uhci->rh_numports)
161 goto err;
162
163 uhci_check_ports(uhci);
164 status = inw(port_addr);
165
166 /* Intel controllers report the OverCurrent bit active on.
167 * VIA controllers report it active off, so we'll adjust the
168 * bit value. (It's not standardized in the UHCI spec.)
169 */
170 if (to_pci_dev(hcd->self.controller)->vendor ==
171 PCI_VENDOR_ID_VIA)
172 status ^= USBPORTSC_OC;
173
174 /* UHCI doesn't support C_RESET (always false) */
175 wPortChange = lstatus = 0;
176 if (status & USBPORTSC_CSC)
177 wPortChange |= USB_PORT_STAT_C_CONNECTION;
178 if (status & USBPORTSC_PEC)
179 wPortChange |= USB_PORT_STAT_C_ENABLE;
180 if (status & USBPORTSC_OCC)
181 wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
182
183 if (test_bit(port, &uhci->port_c_suspend)) {
184 wPortChange |= USB_PORT_STAT_C_SUSPEND;
185 lstatus |= 1;
186 }
187 if (test_bit(port, &uhci->suspended_ports))
188 lstatus |= 2;
189 if (test_bit(port, &uhci->resuming_ports))
190 lstatus |= 4;
191
192 /* UHCI has no power switching (always on) */
193 wPortStatus = USB_PORT_STAT_POWER;
194 if (status & USBPORTSC_CCS)
195 wPortStatus |= USB_PORT_STAT_CONNECTION;
196 if (status & USBPORTSC_PE) {
197 wPortStatus |= USB_PORT_STAT_ENABLE;
198 if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
199 wPortStatus |= USB_PORT_STAT_SUSPEND;
200 }
201 if (status & USBPORTSC_OC)
202 wPortStatus |= USB_PORT_STAT_OVERCURRENT;
203 if (status & USBPORTSC_PR)
204 wPortStatus |= USB_PORT_STAT_RESET;
205 if (status & USBPORTSC_LSDA)
206 wPortStatus |= USB_PORT_STAT_LOW_SPEED;
207
208 if (wPortChange)
209 dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
210 wIndex, status, lstatus);
211
212 *(__le16 *)buf = cpu_to_le16(wPortStatus);
213 *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
214 OK(4);
215 case SetHubFeature: /* We don't implement these */
216 case ClearHubFeature:
217 switch (wValue) {
218 case C_HUB_OVER_CURRENT:
219 case C_HUB_LOCAL_POWER:
220 OK(0);
221 default:
222 goto err;
223 }
224 break;
225 case SetPortFeature:
226 if (port >= uhci->rh_numports)
227 goto err;
228
229 switch (wValue) {
230 case USB_PORT_FEAT_SUSPEND:
231 set_bit(port, &uhci->suspended_ports);
232 SET_RH_PORTSTAT(USBPORTSC_SUSP);
233 OK(0);
234 case USB_PORT_FEAT_RESET:
235 SET_RH_PORTSTAT(USBPORTSC_PR);
236
237 /* Reset terminates Resume signalling */
238 uhci_finish_suspend(uhci, port, port_addr);
239
240 /* USB v2.0 7.1.7.5 */
241 uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
242 OK(0);
243 case USB_PORT_FEAT_POWER:
244 /* UHCI has no power switching */
245 OK(0);
246 default:
247 goto err;
248 }
249 break;
250 case ClearPortFeature:
251 if (port >= uhci->rh_numports)
252 goto err;
253
254 switch (wValue) {
255 case USB_PORT_FEAT_ENABLE:
256 CLR_RH_PORTSTAT(USBPORTSC_PE);
257
258 /* Disable terminates Resume signalling */
259 uhci_finish_suspend(uhci, port, port_addr);
260 OK(0);
261 case USB_PORT_FEAT_C_ENABLE:
262 CLR_RH_PORTSTAT(USBPORTSC_PEC);
263 OK(0);
264 case USB_PORT_FEAT_SUSPEND:
265 if (test_bit(port, &uhci->suspended_ports) &&
266 !test_and_set_bit(port,
267 &uhci->resuming_ports)) {
268 SET_RH_PORTSTAT(USBPORTSC_RD);
269
270 /* The controller won't allow RD to be set
271 * if the port is disabled. When this happens
272 * just skip the Resume signalling.
273 */
274 if (!(inw(port_addr) & USBPORTSC_RD))
275 uhci_finish_suspend(uhci, port,
276 port_addr);
277 else
278 /* USB v2.0 7.1.7.7 */
279 uhci->ports_timeout = jiffies +
280 msecs_to_jiffies(20);
281 }
282 OK(0);
283 case USB_PORT_FEAT_C_SUSPEND:
284 clear_bit(port, &uhci->port_c_suspend);
285 OK(0);
286 case USB_PORT_FEAT_POWER:
287 /* UHCI has no power switching */
288 goto err;
289 case USB_PORT_FEAT_C_CONNECTION:
290 CLR_RH_PORTSTAT(USBPORTSC_CSC);
291 OK(0);
292 case USB_PORT_FEAT_C_OVER_CURRENT:
293 CLR_RH_PORTSTAT(USBPORTSC_OCC);
294 OK(0);
295 case USB_PORT_FEAT_C_RESET:
296 /* this driver won't report these */
297 OK(0);
298 default:
299 goto err;
300 }
301 break;
302 case GetHubDescriptor:
303 len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
304 memcpy(buf, root_hub_hub_des, len);
305 if (len > 2)
306 buf[2] = uhci->rh_numports;
307 OK(len);
308 default:
309err:
310 retval = -EPIPE;
311 }
312 spin_unlock_irqrestore(&uhci->lock, flags);
313
314 return retval;
315}
This page took 0.047758 seconds and 5 git commands to generate.