ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / usb / host / ohci-hub.c
1 /*
2  * OHCI HCD (Host Controller Driver) for USB.
3  * 
4  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
5  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
6  * 
7  * This file is licenced under GPL
8  */
9
10 /*-------------------------------------------------------------------------*/
11
12 /*
13  * OHCI Root Hub ... the nonsharable stuff
14  *
15  * Registers don't need cpu_to_le32, that happens transparently
16  */
17
18 /* AMD-756 (D2 rev) reports corrupt register contents in some cases.
19  * The erratum (#4) description is incorrect.  AMD's workaround waits
20  * till some bits (mostly reserved) are clear; ok for all revs.
21  */
22 #define read_roothub(hc, register, mask) ({ \
23         u32 temp = readl (&hc->regs->roothub.register); \
24         if (temp == -1) \
25                 disable (hc); \
26         else if (hc->flags & OHCI_QUIRK_AMD756) \
27                 while (temp & mask) \
28                         temp = readl (&hc->regs->roothub.register); \
29         temp; })
30
31 static u32 roothub_a (struct ohci_hcd *hc)
32         { return read_roothub (hc, a, 0xfc0fe000); }
33 static inline u32 roothub_b (struct ohci_hcd *hc)
34         { return readl (&hc->regs->roothub.b); }
35 static inline u32 roothub_status (struct ohci_hcd *hc)
36         { return readl (&hc->regs->roothub.status); }
37 static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
38         { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
39
40 /*-------------------------------------------------------------------------*/
41
42 #define dbg_port(hc,label,num,value) \
43         ohci_dbg (hc, \
44                 "%s roothub.portstatus [%d] " \
45                 "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
46                 label, num, temp, \
47                 (temp & RH_PS_PRSC) ? " PRSC" : "", \
48                 (temp & RH_PS_OCIC) ? " OCIC" : "", \
49                 (temp & RH_PS_PSSC) ? " PSSC" : "", \
50                 (temp & RH_PS_PESC) ? " PESC" : "", \
51                 (temp & RH_PS_CSC) ? " CSC" : "", \
52                 \
53                 (temp & RH_PS_LSDA) ? " LSDA" : "", \
54                 (temp & RH_PS_PPS) ? " PPS" : "", \
55                 (temp & RH_PS_PRS) ? " PRS" : "", \
56                 (temp & RH_PS_POCI) ? " POCI" : "", \
57                 (temp & RH_PS_PSS) ? " PSS" : "", \
58                 \
59                 (temp & RH_PS_PES) ? " PES" : "", \
60                 (temp & RH_PS_CCS) ? " CCS" : "" \
61                 );
62
63
64 /*-------------------------------------------------------------------------*/
65
66 /* build "status change" packet (one or two bytes) from HC registers */
67
68 static int
69 ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
70 {
71         struct ohci_hcd *ohci = hcd_to_ohci (hcd);
72         int             ports, i, changed = 0, length = 1;
73
74         ports = roothub_a (ohci) & RH_A_NDP; 
75         if (ports > MAX_ROOT_PORTS) {
76                 if (!HCD_IS_RUNNING(ohci->hcd.state))
77                         return -ESHUTDOWN;
78                 ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",
79                         ports, readl (&ohci->regs->roothub.a) & RH_A_NDP);
80                 /* retry later; "should not happen" */
81                 return 0;
82         }
83
84         /* init status */
85         if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
86                 buf [0] = changed = 1;
87         else
88                 buf [0] = 0;
89         if (ports > 7) {
90                 buf [1] = 0;
91                 length++;
92         }
93
94         /* look at each port */
95         for (i = 0; i < ports; i++) {
96                 u32     status = roothub_portstatus (ohci, i);
97
98                 status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
99                                 | RH_PS_OCIC | RH_PS_PRSC;
100                 if (status) {
101                         changed = 1;
102                         if (i < 7)
103                             buf [0] |= 1 << (i + 1);
104                         else
105                             buf [1] |= 1 << (i - 7);
106                 }
107         }
108         return changed ? length : 0;
109 }
110
111 /*-------------------------------------------------------------------------*/
112
113 static void
114 ohci_hub_descriptor (
115         struct ohci_hcd                 *ohci,
116         struct usb_hub_descriptor       *desc
117 ) {
118         u32             rh = roothub_a (ohci);
119         int             ports = rh & RH_A_NDP; 
120         u16             temp;
121
122         desc->bDescriptorType = 0x29;
123         desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
124         desc->bHubContrCurrent = 0;
125
126         desc->bNbrPorts = ports;
127         temp = 1 + (ports / 8);
128         desc->bDescLength = 7 + 2 * temp;
129
130         temp = 0;
131         if (rh & RH_A_NPS)              /* no power switching? */
132             temp |= 0x0002;
133         if (rh & RH_A_PSM)              /* per-port power switching? */
134             temp |= 0x0001;
135         if (rh & RH_A_NOCP)             /* no overcurrent reporting? */
136             temp |= 0x0010;
137         else if (rh & RH_A_OCPM)        /* per-port overcurrent reporting? */
138             temp |= 0x0008;
139         desc->wHubCharacteristics = cpu_to_le16 (temp);
140
141         /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
142         rh = roothub_b (ohci);
143         desc->bitmap [0] = rh & RH_B_DR;
144         if (ports > 7) {
145                 desc->bitmap [1] = (rh & RH_B_DR) >> 8;
146                 desc->bitmap [2] = desc->bitmap [3] = 0xff;
147         } else
148                 desc->bitmap [1] = 0xff;
149 }
150
151 /*-------------------------------------------------------------------------*/
152
153 static int ohci_hub_control (
154         struct usb_hcd  *hcd,
155         u16             typeReq,
156         u16             wValue,
157         u16             wIndex,
158         char            *buf,
159         u16             wLength
160 ) {
161         struct ohci_hcd *ohci = hcd_to_ohci (hcd);
162         int             ports = hcd_to_bus (hcd)->root_hub->maxchild;
163         u32             temp;
164         int             retval = 0;
165
166         switch (typeReq) {
167         case ClearHubFeature:
168                 switch (wValue) {
169                 case C_HUB_OVER_CURRENT:
170                         writel (RH_HS_OCIC, &ohci->regs->roothub.status);
171                 case C_HUB_LOCAL_POWER:
172                         break;
173                 default:
174                         goto error;
175                 }
176                 break;
177         case ClearPortFeature:
178                 if (!wIndex || wIndex > ports)
179                         goto error;
180                 wIndex--;
181
182                 switch (wValue) {
183                 case USB_PORT_FEAT_ENABLE:
184                         temp = RH_PS_CCS;
185                         break;
186                 case USB_PORT_FEAT_C_ENABLE:
187                         temp = RH_PS_PESC;
188                         break;
189                 case USB_PORT_FEAT_SUSPEND:
190                         temp = RH_PS_POCI;
191                         break;
192                 case USB_PORT_FEAT_C_SUSPEND:
193                         temp = RH_PS_PSSC;
194                         break;
195                 case USB_PORT_FEAT_POWER:
196                         temp = RH_PS_LSDA;
197                         break;
198                 case USB_PORT_FEAT_C_CONNECTION:
199                         temp = RH_PS_CSC;
200                         break;
201                 case USB_PORT_FEAT_C_OVER_CURRENT:
202                         temp = RH_PS_OCIC;
203                         break;
204                 case USB_PORT_FEAT_C_RESET:
205                         temp = RH_PS_PRSC;
206                         break;
207                 default:
208                         goto error;
209                 }
210                 writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
211                 // readl (&ohci->regs->roothub.portstatus [wIndex]);
212                 break;
213         case GetHubDescriptor:
214                 ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
215                 break;
216         case GetHubStatus:
217                 temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
218                 *(u32 *) buf = cpu_to_le32 (temp);
219                 break;
220         case GetPortStatus:
221                 if (!wIndex || wIndex > ports)
222                         goto error;
223                 wIndex--;
224                 temp = roothub_portstatus (ohci, wIndex);
225                 *(u32 *) buf = cpu_to_le32 (temp);
226
227 #ifndef OHCI_VERBOSE_DEBUG
228         if (*(u16*)(buf+2))     /* only if wPortChange is interesting */
229 #endif
230                 dbg_port (ohci, "GetStatus", wIndex + 1, temp);
231                 break;
232         case SetHubFeature:
233                 switch (wValue) {
234                 case C_HUB_OVER_CURRENT:
235                         // FIXME:  this can be cleared, yes?
236                 case C_HUB_LOCAL_POWER:
237                         break;
238                 default:
239                         goto error;
240                 }
241                 break;
242         case SetPortFeature:
243                 if (!wIndex || wIndex > ports)
244                         goto error;
245                 wIndex--;
246                 switch (wValue) {
247                 case USB_PORT_FEAT_SUSPEND:
248                         writel (RH_PS_PSS,
249                                 &ohci->regs->roothub.portstatus [wIndex]);
250                         break;
251                 case USB_PORT_FEAT_POWER:
252                         writel (RH_PS_PPS,
253                                 &ohci->regs->roothub.portstatus [wIndex]);
254                         break;
255                 case USB_PORT_FEAT_RESET:
256                         temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
257                         if (temp & RH_PS_CCS)
258                                 writel (RH_PS_PRS,
259                                     &ohci->regs->roothub.portstatus [wIndex]);
260                         break;
261                 default:
262                         goto error;
263                 }
264                 break;
265
266         default:
267 error:
268                 /* "protocol stall" on error */
269                 retval = -EPIPE;
270         }
271         return retval;
272 }
273