d1e7d43a4f4808d4423ee506f33609fba4844bde
[openwrt.git] / target / linux / adm5120 / files / drivers / usb / host / adm5120-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-2004 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
16 #define dbg_port(hc,label,num,value) \
17         admhc_dbg(hc, \
18                 "%s port%d " \
19                 "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
20                 label, num, value, \
21                 (value & ADMHC_PS_PRSC) ? " PRSC" : "", \
22                 (value & ADMHC_PS_OCIC) ? " OCIC" : "", \
23                 (value & ADMHC_PS_PSSC) ? " PSSC" : "", \
24                 (value & ADMHC_PS_PESC) ? " PESC" : "", \
25                 (value & ADMHC_PS_CSC) ? " CSC" : "", \
26                 \
27                 (value & ADMHC_PS_LSDA) ? " LSDA" : "", \
28                 (value & ADMHC_PS_PPS) ? " PPS" : "", \
29                 (value & ADMHC_PS_PRS) ? " PRS" : "", \
30                 (value & ADMHC_PS_POCI) ? " POCI" : "", \
31                 (value & ADMHC_PS_PSS) ? " PSS" : "", \
32                 \
33                 (value & ADMHC_PS_PES) ? " PES" : "", \
34                 (value & ADMHC_PS_CCS) ? " CCS" : "" \
35                 );
36
37 #define dbg_port_write(hc,label,num,value) \
38         admhc_dbg(hc, \
39                 "%s port%d " \
40                 "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
41                 label, num, value, \
42                 (value & ADMHC_PS_PRSC) ? " PRSC" : "", \
43                 (value & ADMHC_PS_OCIC) ? " OCIC" : "", \
44                 (value & ADMHC_PS_PSSC) ? " PSSC" : "", \
45                 (value & ADMHC_PS_PESC) ? " PESC" : "", \
46                 (value & ADMHC_PS_CSC) ? " CSC" : "", \
47                 \
48                 (value & ADMHC_PS_CPP) ? " CPP" : "", \
49                 (value & ADMHC_PS_SPP) ? " SPP" : "", \
50                 (value & ADMHC_PS_SPR) ? " SPR" : "", \
51                 (value & ADMHC_PS_CPS) ? " CPS" : "", \
52                 (value & ADMHC_PS_SPS) ? " SPS" : "", \
53                 \
54                 (value & ADMHC_PS_SPE) ? " SPE" : "", \
55                 (value & ADMHC_PS_CPE) ? " CPE" : "" \
56                 );
57
58 /*-------------------------------------------------------------------------*/
59
60 /* hcd->hub_irq_enable() */
61 static void admhc_hub_irq_enable(struct usb_hcd *hcd)
62 {
63         struct admhcd   *ahcd = hcd_to_admhcd(hcd);
64
65         spin_lock_irq(&ahcd->lock);
66         if (!ahcd->autostop)
67                 del_timer(&hcd->rh_timer);      /* Prevent next poll */
68         admhc_intr_enable(ahcd, ADMHC_INTR_INSM);
69         spin_unlock_irq(&ahcd->lock);
70 }
71
72 /*-------------------------------------------------------------------------*/
73
74 /* build "status change" packet (one or two bytes) from HC registers */
75
76 static int
77 admhc_hub_status_data(struct usb_hcd *hcd, char *buf)
78 {
79         struct admhcd   *ahcd = hcd_to_admhcd(hcd);
80         int             i, changed = 0, length = 1;
81         int             any_connected = 0;
82         unsigned long   flags;
83         u32             status;
84
85         spin_lock_irqsave(&ahcd->lock, flags);
86         if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
87                 goto done;
88
89         /* init status */
90         status = admhc_read_rhdesc(ahcd);
91         if (status & (ADMHC_RH_LPSC | ADMHC_RH_OCIC))
92                 buf [0] = changed = 1;
93         else
94                 buf [0] = 0;
95         if (ahcd->num_ports > 7) {
96                 buf [1] = 0;
97                 length++;
98         }
99
100         /* look at each port */
101         for (i = 0; i < ahcd->num_ports; i++) {
102                 status = admhc_read_portstatus(ahcd, i);
103
104                 /* can't autostop if ports are connected */
105                 any_connected |= (status & ADMHC_PS_CCS);
106
107                 if (status & (ADMHC_PS_CSC | ADMHC_PS_PESC | ADMHC_PS_PSSC
108                                 | ADMHC_PS_OCIC | ADMHC_PS_PRSC)) {
109                         changed = 1;
110                         if (i < 7)
111                             buf [0] |= 1 << (i + 1);
112                         else
113                             buf [1] |= 1 << (i - 7);
114                 }
115         }
116
117         hcd->poll_rh = admhc_root_hub_state_changes(ahcd, changed,
118                         any_connected);
119
120 done:
121         spin_unlock_irqrestore(&ahcd->lock, flags);
122
123         return changed ? length : 0;
124 }
125
126 /*-------------------------------------------------------------------------*/
127
128 static int admhc_get_hub_descriptor(struct admhcd *ahcd, char *buf)
129 {
130         struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf;
131         u32 rh = admhc_read_rhdesc(ahcd);
132         u16 temp;
133
134         desc->bDescriptorType = USB_DT_HUB;     /* Hub-descriptor */
135         desc->bPwrOn2PwrGood = ADMHC_POTPGT/2;  /* use default value */
136         desc->bHubContrCurrent = 0x00;          /* 0mA */
137
138         desc->bNbrPorts = ahcd->num_ports;
139         temp = 1 + (ahcd->num_ports / 8);
140         desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp;
141
142         /* FIXME */
143         temp = 0;
144         if (rh & ADMHC_RH_NPS)          /* no power switching? */
145             temp |= 0x0002;
146         if (rh & ADMHC_RH_PSM)          /* per-port power switching? */
147             temp |= 0x0001;
148         if (rh & ADMHC_RH_NOCP)         /* no overcurrent reporting? */
149             temp |= 0x0010;
150         else if (rh & ADMHC_RH_OCPM)    /* per-port overcurrent reporting? */
151             temp |= 0x0008;
152         desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ahcd, temp);
153
154         /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
155         desc->bitmap[0] = 0;
156         desc->bitmap[0] = ~0;
157
158         return 0;
159 }
160
161 static int admhc_get_hub_status(struct admhcd *ahcd, char *buf)
162 {
163         struct usb_hub_status *hs = (struct usb_hub_status *)buf;
164         u32 t = admhc_read_rhdesc(ahcd);
165         u16 status, change;
166
167         status = 0;
168         status |= (t & ADMHC_RH_LPS) ? HUB_STATUS_LOCAL_POWER : 0;
169         status |= (t & ADMHC_RH_OCI) ? HUB_STATUS_OVERCURRENT : 0;
170
171         change = 0;
172         change |= (t & ADMHC_RH_LPSC) ? HUB_CHANGE_LOCAL_POWER : 0;
173         change |= (t & ADMHC_RH_OCIC) ? HUB_CHANGE_OVERCURRENT : 0;
174
175         hs->wHubStatus = (__force __u16)cpu_to_hc16(ahcd, status);
176         hs->wHubChange = (__force __u16)cpu_to_hc16(ahcd, change);
177
178         return 0;
179 }
180
181 static int admhc_get_port_status(struct admhcd *ahcd, unsigned port, char *buf)
182 {
183         struct usb_port_status *ps = (struct usb_port_status *)buf;
184         u32 t = admhc_read_portstatus(ahcd, port);
185         u16 status, change;
186
187         status = 0;
188         status |= (t & ADMHC_PS_CCS) ? USB_PORT_STAT_CONNECTION : 0;
189         status |= (t & ADMHC_PS_PES) ? USB_PORT_STAT_ENABLE : 0;
190         status |= (t & ADMHC_PS_PSS) ? USB_PORT_STAT_SUSPEND : 0;
191         status |= (t & ADMHC_PS_POCI) ? USB_PORT_STAT_OVERCURRENT : 0;
192         status |= (t & ADMHC_PS_PRS) ? USB_PORT_STAT_RESET : 0;
193         status |= (t & ADMHC_PS_PPS) ? USB_PORT_STAT_POWER : 0;
194         status |= (t & ADMHC_PS_LSDA) ? USB_PORT_STAT_LOW_SPEED : 0;
195
196         change = 0;
197         change |= (t & ADMHC_PS_CSC) ? USB_PORT_STAT_C_CONNECTION : 0;
198         change |= (t & ADMHC_PS_PESC) ? USB_PORT_STAT_C_ENABLE : 0;
199         change |= (t & ADMHC_PS_PSSC) ? USB_PORT_STAT_C_SUSPEND : 0;
200         change |= (t & ADMHC_PS_OCIC) ? USB_PORT_STAT_C_OVERCURRENT : 0;
201         change |= (t & ADMHC_PS_PRSC) ? USB_PORT_STAT_C_RESET : 0;
202
203         ps->wPortStatus = (__force __u16)cpu_to_hc16(ahcd, status);
204         ps->wPortChange = (__force __u16)cpu_to_hc16(ahcd, change);
205
206         return 0;
207 }
208
209 /*-------------------------------------------------------------------------*/
210
211 #ifdef  CONFIG_USB_OTG
212
213 static int admhc_start_port_reset(struct usb_hcd *hcd, unsigned port)
214 {
215         struct admhcd   *ahcd = hcd_to_admhcd(hcd);
216         u32                     status;
217
218         if (!port)
219                 return -EINVAL;
220         port--;
221
222         /* start port reset before HNP protocol times out */
223         status = admhc_read_portstatus(ahcd, port);
224         if (!(status & ADMHC_PS_CCS))
225                 return -ENODEV;
226
227         /* khubd will finish the reset later */
228         admhc_write_portstatus(ahcd, port, ADMHC_PS_PRS);
229         return 0;
230 }
231
232 static void start_hnp(struct admhcd *ahcd);
233
234 #else
235
236 #define admhc_start_port_reset          NULL
237
238 #endif
239
240 /*-------------------------------------------------------------------------*/
241
242
243 /* See usb 7.1.7.5:  root hubs must issue at least 50 msec reset signaling,
244  * not necessarily continuous ... to guard against resume signaling.
245  * The short timeout is safe for non-root hubs, and is backward-compatible
246  * with earlier Linux hosts.
247  */
248 #ifdef  CONFIG_USB_SUSPEND
249 #define PORT_RESET_MSEC         50
250 #else
251 #define PORT_RESET_MSEC         10
252 #endif
253
254 /* this timer value might be vendor-specific ... */
255 #define PORT_RESET_HW_MSEC      10
256
257 /* wrap-aware logic morphed from <linux/jiffies.h> */
258 #define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
259
260 /* called from some task, normally khubd */
261 static inline int admhc_port_reset(struct admhcd *ahcd, unsigned port)
262 {
263         u32 t;
264
265         admhc_vdbg(ahcd, "reset port%d\n", port);
266         t = admhc_read_portstatus(ahcd, port);
267         if (!(t & ADMHC_PS_CCS))
268                 return -ENODEV;
269
270         admhc_write_portstatus(ahcd, port, ADMHC_PS_SPR);
271         mdelay(10);
272         admhc_write_portstatus(ahcd, port, (ADMHC_PS_SPE | ADMHC_PS_CSC));
273         mdelay(100);
274
275         return 0;
276 }
277
278 static inline int admhc_port_enable(struct admhcd *ahcd, unsigned port)
279 {
280         u32 t;
281
282         admhc_vdbg(ahcd, "enable port%d\n", port);
283         t = admhc_read_portstatus(ahcd, port);
284         if (!(t & ADMHC_PS_CCS))
285                 return -ENODEV;
286
287         admhc_write_portstatus(ahcd, port, ADMHC_PS_SPE);
288
289         return 0;
290 }
291
292 static inline int admhc_port_disable(struct admhcd *ahcd, unsigned port)
293 {
294         u32 t;
295
296         admhc_vdbg(ahcd, "disable port%d\n", port);
297         t = admhc_read_portstatus(ahcd, port);
298         if (!(t & ADMHC_PS_CCS))
299                 return -ENODEV;
300
301         admhc_write_portstatus(ahcd, port, ADMHC_PS_CPE);
302
303         return 0;
304 }
305
306 static inline int admhc_port_write(struct admhcd *ahcd, unsigned port,
307                 u32 val)
308 {
309 #ifdef ADMHC_VERBOSE_DEBUG
310         dbg_port_write(ahcd, "write", port, val);
311 #endif
312         admhc_write_portstatus(ahcd, port, val);
313
314         return 0;
315 }
316
317 static int admhc_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
318                 u16 wIndex, char *buf, u16 wLength)
319 {
320         struct admhcd   *ahcd = hcd_to_admhcd(hcd);
321         int             ports = hcd_to_bus(hcd)->root_hub->maxchild;
322         int             ret = 0;
323
324         if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
325                 return -ESHUTDOWN;
326
327         switch (typeReq) {
328         case ClearHubFeature:
329                 switch (wValue) {
330                 case C_HUB_OVER_CURRENT:
331 #if 0                   /* FIXME */
332                         admhc_writel(ahcd, ADMHC_RH_OCIC,
333                                         &ahcd->regs->roothub.status);
334 #endif
335                 case C_HUB_LOCAL_POWER:
336                         break;
337                 default:
338                         goto error;
339                 }
340                 break;
341         case ClearPortFeature:
342                 if (!wIndex || wIndex > ports)
343                         goto error;
344                 wIndex--;
345
346                 switch (wValue) {
347                 case USB_PORT_FEAT_ENABLE:
348                         ret = admhc_port_disable(ahcd, wIndex);
349                         break;
350                 case USB_PORT_FEAT_SUSPEND:
351                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_CPS);
352                         break;
353                 case USB_PORT_FEAT_POWER:
354                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_CPP);
355                         break;
356                 case USB_PORT_FEAT_C_CONNECTION:
357                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_CSC);
358                         break;
359                 case USB_PORT_FEAT_C_ENABLE:
360                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_PESC);
361                         break;
362                 case USB_PORT_FEAT_C_SUSPEND:
363                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_PSSC);
364                         break;
365                 case USB_PORT_FEAT_C_OVER_CURRENT:
366                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_OCIC);
367                         break;
368                 case USB_PORT_FEAT_C_RESET:
369                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_PRSC);
370                         break;
371                 default:
372                         goto error;
373                 }
374                 break;
375         case GetHubDescriptor:
376                 ret = admhc_get_hub_descriptor(ahcd, buf);
377                 break;
378         case GetHubStatus:
379                 ret = admhc_get_hub_status(ahcd, buf);
380                 break;
381         case GetPortStatus:
382                 if (!wIndex || wIndex > ports)
383                         goto error;
384                 wIndex--;
385
386                 ret = admhc_get_port_status(ahcd, wIndex, buf);
387                 break;
388         case SetHubFeature:
389                 switch (wValue) {
390                 case C_HUB_OVER_CURRENT:
391                         /* FIXME:  this can be cleared, yes? */
392                 case C_HUB_LOCAL_POWER:
393                         break;
394                 default:
395                         goto error;
396                 }
397                 break;
398         case SetPortFeature:
399                 if (!wIndex || wIndex > ports)
400                         goto error;
401                 wIndex--;
402
403                 switch (wValue) {
404                 case USB_PORT_FEAT_ENABLE:
405                         ret = admhc_port_enable(ahcd, wIndex);
406                         break;
407                 case USB_PORT_FEAT_RESET:
408                         ret = admhc_port_reset(ahcd, wIndex);
409                         break;
410                 case USB_PORT_FEAT_SUSPEND:
411 #ifdef  CONFIG_USB_OTG
412                         if (hcd->self.otg_port == (wIndex + 1)
413                                         && hcd->self.b_hnp_enable)
414                                 start_hnp(ahcd);
415                         else
416 #endif
417                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_SPS);
418                         break;
419                 case USB_PORT_FEAT_POWER:
420                         ret = admhc_port_write(ahcd, wIndex, ADMHC_PS_SPP);
421                         break;
422                 default:
423                         goto error;
424                 }
425                 break;
426
427         default:
428 error:
429                 /* "protocol stall" on error */
430                 ret = -EPIPE;
431         }
432
433         return ret;
434 }
435