2 * Universal Host Controller Interface driver for USB.
4 * Maintainer: Alan Stern <stern@rowland.harvard.edu>
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
15 static __u8 root_hub_hub_des[] =
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 *** */
28 #define UHCI_RH_MAXCHILD 7
30 /* must write as zeroes */
31 #define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
33 /* status change bits: nonzero writes will clear */
34 #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
36 static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
38 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
39 unsigned int io_addr = uhci->io_addr;
43 for (i = 0; i < uhci->rh_numports; i++) {
44 if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS)
45 *buf |= (1 << (i + 1));
50 #define OK(x) len = (x); break
52 #define CLR_RH_PORTSTAT(x) \
53 status = inw(port_addr); \
54 status &= ~(RWC_BITS|WZ_BITS); \
56 status |= RWC_BITS & (x); \
57 outw(status, port_addr)
59 #define SET_RH_PORTSTAT(x) \
60 status = inw(port_addr); \
62 status &= ~(RWC_BITS|WZ_BITS); \
63 outw(status, port_addr)
66 /* size of returned buffer is part of USB spec */
67 static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
68 u16 wIndex, char *buf, u16 wLength)
70 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
71 int status, retval = 0, len = 0;
72 unsigned int port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1);
73 __u16 wPortChange, wPortStatus;
76 /* Request Destination:
77 without flags: Device,
78 RH_INTERFACE: interface,
79 RH_ENDPOINT: endpoint,
80 RH_CLASS means HUB here,
81 RH_OTHER | RH_CLASS almost ever means HUB_PORT here
85 *(__u32 *)buf = cpu_to_le32(0);
86 OK(4); /* hub power */
88 if (!wIndex || wIndex > uhci->rh_numports)
90 status = inw(port_addr);
92 /* Intel controllers report the OverCurrent bit active on.
93 * VIA controllers report it active off, so we'll adjust the
94 * bit value. (It's not standardized in the UHCI spec.)
96 if (to_pci_dev(hcd->self.controller)->vendor ==
98 status ^= USBPORTSC_OC;
100 /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */
102 if (status & USBPORTSC_CSC)
103 wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16);
104 if (status & USBPORTSC_PEC)
105 wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16);
106 if (status & USBPORTSC_OCC)
107 wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16);
109 /* UHCI has no power switching (always on) */
110 wPortStatus = 1 << USB_PORT_FEAT_POWER;
111 if (status & USBPORTSC_CCS)
112 wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION;
113 if (status & USBPORTSC_PE) {
114 wPortStatus |= 1 << USB_PORT_FEAT_ENABLE;
115 if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
116 wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND;
118 if (status & USBPORTSC_OC)
119 wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT;
120 if (status & USBPORTSC_PR)
121 wPortStatus |= 1 << USB_PORT_FEAT_RESET;
122 if (status & USBPORTSC_LSDA)
123 wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED;
126 dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n",
129 *(__u16 *)buf = cpu_to_le16(wPortStatus);
130 *(__u16 *)(buf + 2) = cpu_to_le16(wPortChange);
132 case SetHubFeature: /* We don't implement these */
133 case ClearHubFeature:
135 case C_HUB_OVER_CURRENT:
136 case C_HUB_LOCAL_POWER:
143 if (!wIndex || wIndex > uhci->rh_numports)
147 case USB_PORT_FEAT_SUSPEND:
148 SET_RH_PORTSTAT(USBPORTSC_SUSP);
150 case USB_PORT_FEAT_RESET:
151 SET_RH_PORTSTAT(USBPORTSC_PR);
152 mdelay(50); /* USB v1.1 7.1.7.3 */
153 CLR_RH_PORTSTAT(USBPORTSC_PR);
155 SET_RH_PORTSTAT(USBPORTSC_PE);
157 CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
159 case USB_PORT_FEAT_POWER:
160 /* UHCI has no power switching */
166 case ClearPortFeature:
167 if (!wIndex || wIndex > uhci->rh_numports)
171 case USB_PORT_FEAT_ENABLE:
172 CLR_RH_PORTSTAT(USBPORTSC_PE);
174 case USB_PORT_FEAT_C_ENABLE:
175 CLR_RH_PORTSTAT(USBPORTSC_PEC);
177 case USB_PORT_FEAT_SUSPEND:
178 CLR_RH_PORTSTAT(USBPORTSC_SUSP);
180 case USB_PORT_FEAT_C_SUSPEND:
181 /* this driver won't report these */
183 case USB_PORT_FEAT_POWER:
184 /* UHCI has no power switching */
186 case USB_PORT_FEAT_C_CONNECTION:
187 CLR_RH_PORTSTAT(USBPORTSC_CSC);
189 case USB_PORT_FEAT_C_OVER_CURRENT:
190 CLR_RH_PORTSTAT(USBPORTSC_OCC);
192 case USB_PORT_FEAT_C_RESET:
193 /* this driver won't report these */
199 case GetHubDescriptor:
200 len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
201 memcpy(buf, root_hub_hub_des, len);
203 buf[2] = uhci->rh_numports;