]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/sl811_usb.c
* Fix minor NAND JFFS2 related issue
[karo-tx-uboot.git] / drivers / sl811_usb.c
1 /*
2  * (C) Copyright 2004
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * This code is based on linux driver for sl811hs chip, source at
6  * drivers/usb/host/sl811.c:
7  *
8  * SL811 Host Controller Interface driver for USB.
9  *
10  * Copyright (c) 2003/06, Courage Co., Ltd.
11  *
12  * Based on:
13  *      1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
14  *        Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
15  *        Adam Richter, Gregory P. Smith;
16  *      2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
17  *      3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
18  *
19  * See file CREDITS for list of people who contributed to this
20  * project.
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License as
24  * published by the Free Software Foundation; either version 2 of
25  * the License, or (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
35  * MA 02111-1307 USA
36  */
37
38 #include <common.h>
39 #ifdef CONFIG_USB_SL811HS
40 #include <mpc8xx.h>
41 #include <usb.h>
42 #include "sl811.h"
43
44 #include "../board/kup/common/kup.h"
45
46 #ifdef __PPC__
47 # define EIEIO          __asm__ volatile ("eieio")
48 #else
49 # define EIEIO          /* nothing */
50 #endif
51
52 #define  SL811_ADR (0x50000000)
53 #define  SL811_DAT (0x50000001)
54
55 #define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
56
57 #ifdef SL811_DEBUG
58 static int debug = 9;
59 #endif
60
61 static int root_hub_devnum = 0;
62 static struct usb_port_status rh_status = { 0 };/* root hub port status */
63
64 static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
65                                void *data, int buf_len, struct devrequest *cmd);
66
67 static void sl811_write (__u8 index, __u8 data)
68 {
69         *(volatile unsigned char *) (SL811_ADR) = index;
70         EIEIO;
71         *(volatile unsigned char *) (SL811_DAT) = data;
72         EIEIO;
73 }
74
75 static __u8 sl811_read (__u8 index)
76 {
77         __u8 data;
78
79         *(volatile unsigned char *) (SL811_ADR) = index;
80         EIEIO;
81         data = *(volatile unsigned char *) (SL811_DAT);
82         EIEIO;
83         return (data);
84 }
85
86 /*
87  * Read consecutive bytes of data from the SL811H/SL11H buffer
88  */
89 static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size)
90 {
91         *(volatile unsigned char *) (SL811_ADR) = offset;
92         EIEIO;
93         while (size--) {
94                 *buf++ = *(volatile unsigned char *) (SL811_DAT);
95                 EIEIO;
96         }
97 }
98
99 /*
100  * Write consecutive bytes of data to the SL811H/SL11H buffer
101  */
102 static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size)
103 {
104         *(volatile unsigned char *) (SL811_ADR) = offset;
105         EIEIO;
106         while (size--) {
107                 *(volatile unsigned char *) (SL811_DAT) = *buf++;
108                 EIEIO;
109         }
110 }
111
112 int usb_init_kup4x (void)
113 {
114         volatile immap_t *immap = (immap_t *) CFG_IMMR;
115         volatile memctl8xx_t *memctl = &immap->im_memctl;
116         int i;
117         unsigned char tmp;
118
119         memctl = &immap->im_memctl;
120         memctl->memc_or7 = 0xFFFF8726;
121         memctl->memc_br7 = 0x50000401;  /* start at 0x50000000 */
122         /* BP 14 low = USB ON */
123         immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC);
124         /* PB 14 nomal port */
125         immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC);
126         /* output */
127         immap->im_cpm.cp_pbdir |= (BP_USB_VCC);
128
129         puts ("USB:   ");
130
131         for (i = 0x10; i < 0xff; i++) {
132                 sl811_write(i, i);
133                 tmp = (sl811_read(i));
134                 if (tmp != i) {
135                         printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp);
136                         return (-1);
137                 }
138         }
139         printf ("SL811 ready\n");
140         return (0);
141 }
142
143 /*
144  * This function resets SL811HS controller and detects the speed of
145  * the connecting device
146  *
147  * Return: 0 = no device attached; 1 = USB device attached
148  */
149 static int sl811_hc_reset(void)
150 {
151         int status ;
152
153         sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
154         sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
155
156         mdelay(20);
157
158         /* Disable hardware SOF generation, clear all irq status. */
159         sl811_write(SL811_CTRL1, 0);
160         mdelay(2);
161         sl811_write(SL811_INTRSTS, 0xff);
162         status = sl811_read(SL811_INTRSTS);
163
164         if (status & SL811_INTR_NOTPRESENT) {
165                 /* Device is not present */
166                 PDEBUG(0, "Device not present\n");
167                 rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
168                 rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
169                 sl811_write(SL811_INTR, SL811_INTR_INSRMV);
170                 return 0;
171         }
172
173         /* Send SOF to address 0, endpoint 0. */
174         sl811_write(SL811_LEN_B, 0);
175         sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
176         sl811_write(SL811_DEV_B, 0x00);
177         sl811_write(SL811_SOFLOW, SL811_12M_LOW);
178
179         if (status & SL811_INTR_SPEED_FULL) {
180                 /* full speed device connect directly to root hub */
181                 PDEBUG (0, "Full speed Device attached\n");
182
183                 sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
184                 mdelay(20);
185                 sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
186                 sl811_write(SL811_CTRL1, SL811_CTRL1_SOF);
187
188                 /* start the SOF or EOP */
189                 sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
190                 rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
191                 rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
192                 mdelay(2);
193                 sl811_write(SL811_INTRSTS, 0xff);
194         } else {
195                 /* slow speed device connect directly to root-hub */
196                 PDEBUG(0, "Low speed Device attached\n");
197
198                 sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
199                 mdelay(20);
200                 sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
201                 sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
202
203                 /* start the SOF or EOP */
204                 sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
205                 rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
206                 mdelay(2);
207                 sl811_write(SL811_INTRSTS, 0xff);
208         }
209
210         rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
211         sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A);
212
213         return 1;
214 }
215
216 int usb_lowlevel_init(void)
217 {
218         root_hub_devnum = 0;
219         sl811_hc_reset();
220         return 0;
221 }
222
223 int usb_lowlevel_stop(void)
224 {
225         sl811_hc_reset();
226         return 0;
227 }
228
229 int sl811_send_packet(int dir_to_host, int data1, __u8 *buffer, int len)
230 {
231         __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
232         __u16 status = 0;
233         int err = 0, timeout = get_timer(0) + 5*CFG_HZ;
234
235         if (len > 239)
236                 return -1;
237
238         if (!dir_to_host)
239                 ctrl |= SL811_USB_CTRL_DIR_OUT;
240         if (data1)
241                 ctrl |= SL811_USB_CTRL_TOGGLE_1;
242
243         sl811_write(SL811_INTRSTS, 0xff);
244
245         while (err < 3) {
246                 sl811_write(SL811_ADDR_A, 0x10);
247                 sl811_write(SL811_LEN_A, len);
248                 if (!dir_to_host && len)
249                         sl811_write_buf(0x10, buffer, len);
250
251                 if (sl811_read(SL811_SOFCNTDIV)*64 < len * 8 * 2)
252                         ctrl |= SL811_USB_CTRL_SOF;
253                 else
254                         ctrl &= ~SL811_USB_CTRL_SOF;
255
256                 sl811_write(SL811_CTRL_A, ctrl);
257                 while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) {
258                         if (timeout < get_timer(0)) {
259                                 printf("USB transmit timed out\n");
260                                 return -USB_ST_CRC_ERR;
261                         }
262                 }
263
264                 sl811_write(SL811_INTRSTS, 0xff);
265                 status = sl811_read(SL811_STS_A);
266
267                 if (status & SL811_USB_STS_ACK) {
268                         int remainder = sl811_read(SL811_CNT_A);
269                         if (remainder) {
270                                 PDEBUG(0, "usb transfer remainder = %d\n", remainder);
271                                 len -= remainder;
272                         }
273                         if (dir_to_host && len)
274                                 sl811_read_buf(0x10, buffer, len);
275                         return len;
276                 }
277
278                 if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK)
279                         continue;
280
281                 PDEBUG(0, "usb transfer error %#x\n", (int)status);
282                 err++;
283         }
284
285         err = 0;
286
287         if (status & SL811_USB_STS_ERROR)
288                 err |= USB_ST_BUF_ERR;
289         if (status & SL811_USB_STS_TIMEOUT)
290                 err |= USB_ST_CRC_ERR;
291         if (status & SL811_USB_STS_STALL)
292                 err |= USB_ST_STALLED;
293
294         return -err;
295 }
296
297 int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
298                     int len)
299 {
300         int dir_out = usb_pipeout(pipe);
301         int ep = usb_pipeendpoint(pipe);
302         __u8* buf = (__u8*)buffer;
303         int max = usb_maxpacket(dev, pipe);
304         int done = 0;
305
306         PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n",
307                usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
308
309         dev->status = 0;
310
311         sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
312         sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
313         while (done < len) {
314                 int res = sl811_send_packet(!dir_out, usb_gettoggle(dev, ep, dir_out),
315                                             buf+done,
316                                             max > len - done ? len - done : max);
317                 if (res < 0) {
318                         dev->status = -res;
319                         return res;
320                 }
321
322                 if (!dir_out && res < max) /* short packet */
323                         break;
324
325                 done += res;
326                 usb_dotoggle(dev, ep, dir_out);
327         }
328
329         dev->act_len = done;
330
331         return 0;
332 }
333
334 int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
335                        int len,struct devrequest *setup)
336 {
337         int done = 0;
338         int devnum = usb_pipedevice(pipe);
339
340         dev->status = 0;
341
342         if (devnum == root_hub_devnum)
343                 return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
344
345         PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x\n",
346                devnum, usb_pipeendpoint(pipe), buffer, len, (int)setup->requesttype,
347                (int)setup->request);
348
349         sl811_write(SL811_DEV_A, devnum);
350         sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, 0));
351         /* setup phase */
352         if (sl811_send_packet(0, 0, (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
353                 int dir_in = setup->requesttype & USB_DIR_IN;
354                 __u8* buf = (__u8*)buffer;
355                 int data1 = 1;
356                 int max = usb_maxpacket(dev, pipe);
357
358                 /* data phase */
359                 sl811_write(SL811_PIDEP_A,
360                             PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, 0));
361                 while (done < len) {
362                         int res = sl811_send_packet(dir_in, data1, buf+done,
363                                                     max > len - done ? len - done : max);
364                         if (res < 0) {
365                                 dev->status = -res;
366                                 return 0;
367                         }
368                         done += res;
369
370                         if (dir_in && res < max) /* short packet */
371                                 break;
372
373                         data1 = !data1;
374                 }
375
376                 /* status phase */
377                 sl811_write(SL811_PIDEP_A,
378                             PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, 0));
379                 if (sl811_send_packet(!dir_in, 1, 0, 0) < 0) {
380                         PDEBUG(0, "status phase failed!\n");
381                         dev->status = -1;
382                 }
383         } else {
384                 PDEBUG(0, "setup phase failed!\n");
385                 dev->status = -1;
386         }
387
388         dev->act_len = done;
389
390         return done;
391 }
392
393 int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
394                    int len, int interval)
395 {
396         PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
397                buffer, len, interval);
398         return -1;
399 }
400
401 /*
402  * SL811 Virtual Root Hub
403  */
404
405 /* Device descriptor */
406 static __u8 sl811_rh_dev_des[] =
407 {
408         0x12,       /*  __u8  bLength; */
409         0x01,       /*  __u8  bDescriptorType; Device */
410         0x10,       /*  __u16 bcdUSB; v1.1 */
411         0x01,
412         0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
413         0x00,       /*  __u8  bDeviceSubClass; */
414         0x00,       /*  __u8  bDeviceProtocol; */
415         0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
416         0x00,       /*  __u16 idVendor; */
417         0x00,
418         0x00,       /*  __u16 idProduct; */
419         0x00,
420         0x00,       /*  __u16 bcdDevice; */
421         0x00,
422         0x00,       /*  __u8  iManufacturer; */
423         0x02,       /*  __u8  iProduct; */
424         0x01,       /*  __u8  iSerialNumber; */
425         0x01        /*  __u8  bNumConfigurations; */
426 };
427
428 /* Configuration descriptor */
429 static __u8 sl811_rh_config_des[] =
430 {
431         0x09,       /*  __u8  bLength; */
432         0x02,       /*  __u8  bDescriptorType; Configuration */
433         0x19,       /*  __u16 wTotalLength; */
434         0x00,
435         0x01,       /*  __u8  bNumInterfaces; */
436         0x01,       /*  __u8  bConfigurationValue; */
437         0x00,       /*  __u8  iConfiguration; */
438         0x40,       /*  __u8  bmAttributes;
439                     Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
440                     4..0: resvd */
441         0x00,       /*  __u8  MaxPower; */
442
443         /* interface */
444         0x09,       /*  __u8  if_bLength; */
445         0x04,       /*  __u8  if_bDescriptorType; Interface */
446         0x00,       /*  __u8  if_bInterfaceNumber; */
447         0x00,       /*  __u8  if_bAlternateSetting; */
448         0x01,       /*  __u8  if_bNumEndpoints; */
449         0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
450         0x00,       /*  __u8  if_bInterfaceSubClass; */
451         0x00,       /*  __u8  if_bInterfaceProtocol; */
452         0x00,       /*  __u8  if_iInterface; */
453
454         /* endpoint */
455         0x07,       /*  __u8  ep_bLength; */
456         0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
457         0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
458         0x03,       /*  __u8  ep_bmAttributes; Interrupt */
459         0x08,       /*  __u16 ep_wMaxPacketSize; */
460         0x00,
461         0xff        /*  __u8  ep_bInterval; 255 ms */
462 };
463
464 /* root hub class descriptor*/
465 static __u8 sl811_rh_hub_des[] =
466 {
467         0x09,                   /*  __u8  bLength; */
468         0x29,                   /*  __u8  bDescriptorType; Hub-descriptor */
469         0x01,                   /*  __u8  bNbrPorts; */
470         0x00,                   /* __u16  wHubCharacteristics; */
471         0x00,
472         0x50,                   /*  __u8  bPwrOn2pwrGood; 2ms */
473         0x00,                   /*  __u8  bHubContrCurrent; 0 mA */
474         0xfc,                   /*  __u8  DeviceRemovable; *** 7 Ports max *** */
475         0xff                    /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
476 };
477
478 /*
479  * helper routine for returning string descriptors in UTF-16LE
480  * input can actually be ISO-8859-1; ASCII is its 7-bit subset
481  */
482 static int ascii2utf (char *s, u8 *utf, int utfmax)
483 {
484         int retval;
485
486         for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
487                 *utf++ = *s++;
488                 *utf++ = 0;
489         }
490         return retval;
491 }
492
493 /*
494  * root_hub_string is used by each host controller's root hub code,
495  * so that they're identified consistently throughout the system.
496  */
497 int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
498 {
499         char buf [30];
500
501         /* assert (len > (2 * (sizeof (buf) + 1)));
502            assert (strlen (type) <= 8);*/
503
504         /* language ids */
505         if (id == 0) {
506                 *data++ = 4; *data++ = 3;       /* 4 bytes data */
507                 *data++ = 0; *data++ = 0;       /* some language id */
508                 return 4;
509
510         /* serial number */
511         } else if (id == 1) {
512                 sprintf (buf, "%x", serial);
513
514         /* product description */
515         } else if (id == 2) {
516                 sprintf (buf, "USB %s Root Hub", type);
517
518         /* id 3 == vendor description */
519
520         /* unsupported IDs --> "stall" */
521         } else
522             return 0;
523
524         data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
525         data [1] = 3;
526         return data [0];
527 }
528
529 /* helper macro */
530 #define OK(x)   len = (x); break
531
532 /*
533  * This function handles all USB request to the the virtual root hub
534  */
535 static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
536                                void *data, int buf_len, struct devrequest *cmd)
537 {
538         __u8 data_buf[16];
539         __u8 *bufp = data_buf;
540         int len = 0;
541         int status = 0;
542
543         __u16 bmRType_bReq;
544         __u16 wValue;
545         __u16 wIndex;
546         __u16 wLength;
547
548         if (usb_pipeint(pipe)) {
549                 PDEBUG(0, "interrupt transfer unimplemented!\n");
550                 return 0;
551         }
552
553         bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
554         wValue        = le16_to_cpu (cmd->value);
555         wIndex        = le16_to_cpu (cmd->index);
556         wLength       = le16_to_cpu (cmd->length);
557
558         PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n",
559                bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength);
560
561         /* Request Destination:
562                    without flags: Device,
563                    USB_RECIP_INTERFACE: interface,
564                    USB_RECIP_ENDPOINT: endpoint,
565                    USB_TYPE_CLASS means HUB here,
566                    USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here
567         */
568         switch (bmRType_bReq) {
569         case RH_GET_STATUS:
570                 *(__u16 *)bufp = cpu_to_le16(1);
571                 OK(2);
572
573         case RH_GET_STATUS | USB_RECIP_INTERFACE:
574                 *(__u16 *)bufp = cpu_to_le16(0);
575                 OK(2);
576
577         case RH_GET_STATUS | USB_RECIP_ENDPOINT:
578                 *(__u16 *)bufp = cpu_to_le16(0);
579                 OK(2);
580
581         case RH_GET_STATUS | USB_TYPE_CLASS:
582                 *(__u32 *)bufp = cpu_to_le32(0);
583                 OK(4);
584
585         case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
586                 *(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus);
587                 OK(4);
588
589         case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
590                 switch (wValue) {
591                 case 1:
592                         OK(0);
593                 }
594                 break;
595
596         case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
597                 switch (wValue) {
598                 case C_HUB_LOCAL_POWER:
599                         OK(0);
600
601                 case C_HUB_OVER_CURRENT:
602                         OK(0);
603                 }
604                 break;
605
606         case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
607                 switch (wValue) {
608                 case USB_PORT_FEAT_ENABLE:
609                         rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
610                         OK(0);
611
612                 case USB_PORT_FEAT_SUSPEND:
613                         rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
614                         OK(0);
615
616                 case USB_PORT_FEAT_POWER:
617                         rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
618                         OK(0);
619
620                 case USB_PORT_FEAT_C_CONNECTION:
621                         rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
622                         OK(0);
623
624                 case USB_PORT_FEAT_C_ENABLE:
625                         rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
626                         OK(0);
627
628                 case USB_PORT_FEAT_C_SUSPEND:
629                         rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
630                         OK(0);
631
632                 case USB_PORT_FEAT_C_OVER_CURRENT:
633                         rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
634                         OK(0);
635
636                 case USB_PORT_FEAT_C_RESET:
637                         rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
638                         OK(0);
639                 }
640                 break;
641
642         case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
643                 switch (wValue) {
644                 case USB_PORT_FEAT_SUSPEND:
645                         rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
646                         OK(0);
647
648                 case USB_PORT_FEAT_RESET:
649                         rh_status.wPortStatus |= USB_PORT_STAT_RESET;
650                         rh_status.wPortChange = 0;
651                         rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
652                         rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
653                         rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
654                         OK(0);
655
656                 case USB_PORT_FEAT_POWER:
657                         rh_status.wPortStatus |= USB_PORT_STAT_POWER;
658                         OK(0);
659
660                 case USB_PORT_FEAT_ENABLE:
661                         rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
662                         OK(0);
663                 }
664                 break;
665
666         case RH_SET_ADDRESS:
667                 root_hub_devnum = wValue;
668                 OK(0);
669
670         case RH_GET_DESCRIPTOR:
671                 switch ((wValue & 0xff00) >> 8) {
672                 case USB_DT_DEVICE:
673                         len = sizeof(sl811_rh_dev_des);
674                         bufp = sl811_rh_dev_des;
675                         OK(len);
676
677                 case USB_DT_CONFIG:
678                         len = sizeof(sl811_rh_config_des);
679                         bufp = sl811_rh_config_des;
680                         OK(len);
681
682                 case USB_DT_STRING:
683                         len = usb_root_hub_string(wValue & 0xff, (int)(long)0,  "SL811HS", data, wLength);
684                         if (len > 0) {
685                                 bufp = data;
686                                 OK(len);
687                         }
688
689                 default:
690                         status = -32;
691                 }
692                 break;
693
694         case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
695                 len = sizeof(sl811_rh_hub_des);
696                 bufp = sl811_rh_hub_des;
697                 OK(len);
698
699         case RH_GET_CONFIGURATION:
700                 bufp[0] = 0x01;
701                 OK(1);
702
703         case RH_SET_CONFIGURATION:
704                 OK(0);
705
706         default:
707                 PDEBUG(1, "unsupported root hub command\n");
708                 status = -32;
709         }
710
711         len = min(len, buf_len);
712         if (data != bufp)
713                 memcpy(data, bufp, len);
714
715         PDEBUG(5, "len = %d, status = %d\n", len, status);
716
717         usb_dev->status = status;
718         usb_dev->act_len = len;
719
720         return status == 0 ? len : status;
721 }
722
723 #endif  /* CONFIG_USB_SL811HS */