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