]> git.sur5r.net Git - u-boot/blob - drivers/sl811_usb.c
* Modify KUP4X board configuration to use SL811 driver for USB memory
[u-boot] / 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;
233         int err = 0;
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_ADDR_A, 0x10);
244         sl811_write(SL811_LEN_A, len);
245         if (!dir_to_host && len)
246                 sl811_write_buf(0x10, buffer, len);
247
248         while (err < 3) {
249                 if (sl811_read(SL811_SOFCNTDIV)*64 < len * 8 * 2)
250                         ctrl |= SL811_USB_CTRL_SOF;
251                 else
252                         ctrl &= ~SL811_USB_CTRL_SOF;
253                 sl811_write(SL811_CTRL_A, ctrl);
254                 while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A))
255                         ; /* do nothing */
256
257                 sl811_write(SL811_INTRSTS, 0xff);
258                 status = sl811_read(SL811_STS_A);
259
260                 if (status & SL811_USB_STS_ACK) {
261                         int remainder = sl811_read(SL811_CNT_A);
262                         if (remainder) {
263                                 PDEBUG(0, "usb transfer remainder = %d\n", remainder);
264                                 len -= remainder;
265                         }
266                         if (dir_to_host && len)
267                                 sl811_read_buf(0x10, buffer, len);
268                         return len;
269                 }
270
271                 if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK)
272                         continue;
273
274                 PDEBUG(0, "usb transfer error %#x\n", (int)status);
275                 err++;
276         }
277
278         return -1;
279 }
280
281 int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
282                     int len)
283 {
284         int dir_out = usb_pipeout(pipe);
285         int ep = usb_pipeendpoint(pipe);
286         __u8* buf = (__u8*)buffer;
287         int max = usb_maxpacket(dev, pipe);
288         int done = 0;
289
290         PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n",
291                usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
292
293         dev->status = 0;
294
295         sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
296         sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
297         while (done < len) {
298                 int res = sl811_send_packet(!dir_out, usb_gettoggle(dev, ep, dir_out),
299                                             buf+done,
300                                             max > len - done ? len - done : max);
301                 if (res < 0) {
302                         dev->status = res;
303                         return res;
304                 }
305
306                 if (!dir_out && res < max) /* short packet */
307                         break;
308
309                 done += res;
310                 usb_dotoggle(dev, ep, dir_out);
311         }
312
313         dev->act_len = done;
314
315         return 0;
316 }
317
318 int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
319                        int len,struct devrequest *setup)
320 {
321         int done = 0;
322         int devnum = usb_pipedevice(pipe);
323
324         dev->status = 0;
325
326         if (devnum == root_hub_devnum)
327                 return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
328
329         PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x\n",
330                devnum, usb_pipeendpoint(pipe), buffer, len, (int)setup->requesttype,
331                (int)setup->request);
332
333         sl811_write(SL811_DEV_A, devnum);
334         sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, 0));
335         /* setup phase */
336         if (sl811_send_packet(0, 0, (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
337                 int dir_in = setup->requesttype & USB_DIR_IN;
338                 __u8* buf = (__u8*)buffer;
339                 int data1 = 1;
340                 int max = usb_maxpacket(dev, pipe);
341
342                 /* data phase */
343                 sl811_write(SL811_PIDEP_A,
344                             PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, 0));
345                 while (done < len) {
346                         int res = sl811_send_packet(dir_in, data1, buf+done,
347                                                     max > len - done ? len - done : max);
348                         if (res < 0)
349                                 return res;
350                         done += res;
351
352                         if (dir_in && res < max) /* short packet */
353                                 break;
354
355                         data1 = !data1;
356                 }
357
358                 /* status phase */
359                 sl811_write(SL811_PIDEP_A,
360                             PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, 0));
361                 if (sl811_send_packet(!dir_in, 1, 0, 0) < 0) {
362                         PDEBUG(0, "status phase failed!\n");
363                         dev->status = -1;
364                 }
365         } else {
366                 PDEBUG(0, "setup phase failed!\n");
367                 dev->status = -1;
368         }
369
370         dev->act_len = done;
371
372         return done;
373 }
374
375 int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
376                    int len, int interval)
377 {
378         PDEBUG(7, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
379                buffer, len, interval);
380         return -1;
381 }
382
383 /*
384  * SL811 Virtual Root Hub
385  */
386
387 /* Device descriptor */
388 static __u8 sl811_rh_dev_des[] =
389 {
390         0x12,       /*  __u8  bLength; */
391         0x01,       /*  __u8  bDescriptorType; Device */
392         0x10,       /*  __u16 bcdUSB; v1.1 */
393         0x01,
394         0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
395         0x00,       /*  __u8  bDeviceSubClass; */
396         0x00,       /*  __u8  bDeviceProtocol; */
397         0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
398         0x00,       /*  __u16 idVendor; */
399         0x00,
400         0x00,       /*  __u16 idProduct; */
401         0x00,
402         0x00,       /*  __u16 bcdDevice; */
403         0x00,
404         0x00,       /*  __u8  iManufacturer; */
405         0x02,       /*  __u8  iProduct; */
406         0x01,       /*  __u8  iSerialNumber; */
407         0x01        /*  __u8  bNumConfigurations; */
408 };
409
410 /* Configuration descriptor */
411 static __u8 sl811_rh_config_des[] =
412 {
413         0x09,       /*  __u8  bLength; */
414         0x02,       /*  __u8  bDescriptorType; Configuration */
415         0x19,       /*  __u16 wTotalLength; */
416         0x00,
417         0x01,       /*  __u8  bNumInterfaces; */
418         0x01,       /*  __u8  bConfigurationValue; */
419         0x00,       /*  __u8  iConfiguration; */
420         0x40,       /*  __u8  bmAttributes;
421                     Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
422                     4..0: resvd */
423         0x00,       /*  __u8  MaxPower; */
424
425         /* interface */
426         0x09,       /*  __u8  if_bLength; */
427         0x04,       /*  __u8  if_bDescriptorType; Interface */
428         0x00,       /*  __u8  if_bInterfaceNumber; */
429         0x00,       /*  __u8  if_bAlternateSetting; */
430         0x01,       /*  __u8  if_bNumEndpoints; */
431         0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
432         0x00,       /*  __u8  if_bInterfaceSubClass; */
433         0x00,       /*  __u8  if_bInterfaceProtocol; */
434         0x00,       /*  __u8  if_iInterface; */
435
436         /* endpoint */
437         0x07,       /*  __u8  ep_bLength; */
438         0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
439         0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
440         0x03,       /*  __u8  ep_bmAttributes; Interrupt */
441         0x08,       /*  __u16 ep_wMaxPacketSize; */
442         0x00,
443         0xff        /*  __u8  ep_bInterval; 255 ms */
444 };
445
446 /* root hub class descriptor*/
447 static __u8 sl811_rh_hub_des[] =
448 {
449         0x09,                   /*  __u8  bLength; */
450         0x29,                   /*  __u8  bDescriptorType; Hub-descriptor */
451         0x01,                   /*  __u8  bNbrPorts; */
452         0x00,                   /* __u16  wHubCharacteristics; */
453         0x00,
454         0x50,                   /*  __u8  bPwrOn2pwrGood; 2ms */
455         0x00,                   /*  __u8  bHubContrCurrent; 0 mA */
456         0xfc,                   /*  __u8  DeviceRemovable; *** 7 Ports max *** */
457         0xff                    /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
458 };
459
460 /*
461  * helper routine for returning string descriptors in UTF-16LE
462  * input can actually be ISO-8859-1; ASCII is its 7-bit subset
463  */
464 static int ascii2utf (char *s, u8 *utf, int utfmax)
465 {
466         int retval;
467
468         for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
469                 *utf++ = *s++;
470                 *utf++ = 0;
471         }
472         return retval;
473 }
474
475 /*
476  * root_hub_string is used by each host controller's root hub code,
477  * so that they're identified consistently throughout the system.
478  */
479 int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
480 {
481         char buf [30];
482
483         /* assert (len > (2 * (sizeof (buf) + 1)));
484            assert (strlen (type) <= 8);*/
485
486         /* language ids */
487         if (id == 0) {
488                 *data++ = 4; *data++ = 3;       /* 4 bytes data */
489                 *data++ = 0; *data++ = 0;       /* some language id */
490                 return 4;
491
492         /* serial number */
493         } else if (id == 1) {
494                 sprintf (buf, "%x", serial);
495
496         /* product description */
497         } else if (id == 2) {
498                 sprintf (buf, "USB %s Root Hub", type);
499
500         /* id 3 == vendor description */
501
502         /* unsupported IDs --> "stall" */
503         } else
504             return 0;
505
506         data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
507         data [1] = 3;
508         return data [0];
509 }
510
511 /* helper macro */
512 #define OK(x)   len = (x); break
513
514 /*
515  * This function handles all USB request to the the virtual root hub
516  */
517 static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
518                                void *data, int buf_len, struct devrequest *cmd)
519 {
520         __u8 data_buf[16];
521         __u8 *bufp = data_buf;
522         int len = 0;
523         int status = 0;
524
525         __u16 bmRType_bReq;
526         __u16 wValue;
527         __u16 wIndex;
528         __u16 wLength;
529
530         if (usb_pipeint(pipe)) {
531                 PDEBUG(0, "interrupt transfer unimplemented!\n");
532                 return 0;
533         }
534
535         bmRType_bReq  = cmd->requesttype | (cmd->request << 8);
536         wValue        = le16_to_cpu (cmd->value);
537         wIndex        = le16_to_cpu (cmd->index);
538         wLength       = le16_to_cpu (cmd->length);
539
540         PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n",
541                bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength);
542
543         /* Request Destination:
544                    without flags: Device,
545                    USB_RECIP_INTERFACE: interface,
546                    USB_RECIP_ENDPOINT: endpoint,
547                    USB_TYPE_CLASS means HUB here,
548                    USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here
549         */
550         switch (bmRType_bReq) {
551         case RH_GET_STATUS:
552                 *(__u16 *)bufp = cpu_to_le16(1);
553                 OK(2);
554
555         case RH_GET_STATUS | USB_RECIP_INTERFACE:
556                 *(__u16 *)bufp = cpu_to_le16(0);
557                 OK(2);
558
559         case RH_GET_STATUS | USB_RECIP_ENDPOINT:
560                 *(__u16 *)bufp = cpu_to_le16(0);
561                 OK(2);
562
563         case RH_GET_STATUS | USB_TYPE_CLASS:
564                 *(__u32 *)bufp = cpu_to_le32(0);
565                 OK(4);
566
567         case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
568                 *(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus);
569                 OK(4);
570
571         case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
572                 switch (wValue) {
573                 case 1:
574                         OK(0);
575                 }
576                 break;
577
578         case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
579                 switch (wValue) {
580                 case C_HUB_LOCAL_POWER:
581                         OK(0);
582
583                 case C_HUB_OVER_CURRENT:
584                         OK(0);
585                 }
586                 break;
587
588         case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
589                 switch (wValue) {
590                 case USB_PORT_FEAT_ENABLE:
591                         rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
592                         OK(0);
593
594                 case USB_PORT_FEAT_SUSPEND:
595                         rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
596                         OK(0);
597
598                 case USB_PORT_FEAT_POWER:
599                         rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
600                         OK(0);
601
602                 case USB_PORT_FEAT_C_CONNECTION:
603                         rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
604                         OK(0);
605
606                 case USB_PORT_FEAT_C_ENABLE:
607                         rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
608                         OK(0);
609
610                 case USB_PORT_FEAT_C_SUSPEND:
611                         rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
612                         OK(0);
613
614                 case USB_PORT_FEAT_C_OVER_CURRENT:
615                         rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
616                         OK(0);
617
618                 case USB_PORT_FEAT_C_RESET:
619                         rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
620                         OK(0);
621                 }
622                 break;
623
624         case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
625                 switch (wValue) {
626                 case USB_PORT_FEAT_SUSPEND:
627                         rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
628                         OK(0);
629
630                 case USB_PORT_FEAT_RESET:
631                         rh_status.wPortStatus |= USB_PORT_STAT_RESET;
632                         rh_status.wPortChange = 0;
633                         rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
634                         rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
635                         rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
636                         OK(0);
637
638                 case USB_PORT_FEAT_POWER:
639                         rh_status.wPortStatus |= USB_PORT_STAT_POWER;
640                         OK(0);
641
642                 case USB_PORT_FEAT_ENABLE:
643                         rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
644                         OK(0);
645                 }
646                 break;
647
648         case RH_SET_ADDRESS:
649                 root_hub_devnum = wValue;
650                 OK(0);
651
652         case RH_GET_DESCRIPTOR:
653                 switch ((wValue & 0xff00) >> 8) {
654                 case USB_DT_DEVICE:
655                         len = sizeof(sl811_rh_dev_des);
656                         bufp = sl811_rh_dev_des;
657                         OK(len);
658
659                 case USB_DT_CONFIG:
660                         len = sizeof(sl811_rh_config_des);
661                         bufp = sl811_rh_config_des;
662                         OK(len);
663
664                 case USB_DT_STRING:
665                         len = usb_root_hub_string(wValue & 0xff, (int)(long)0,  "SL811HS", data, wLength);
666                         if (len > 0) {
667                                 bufp = data;
668                                 OK(len);
669                         }
670
671                 default:
672                         status = -32;
673                 }
674                 break;
675
676         case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
677                 len = sizeof(sl811_rh_hub_des);
678                 bufp = sl811_rh_hub_des;
679                 OK(len);
680
681         case RH_GET_CONFIGURATION:
682                 bufp[0] = 0x01;
683                 OK(1);
684
685         case RH_SET_CONFIGURATION:
686                 OK(0);
687
688         default:
689                 PDEBUG(1, "unsupported root hub command\n");
690                 status = -32;
691         }
692
693         len = min(len, buf_len);
694         if (data != bufp)
695                 memcpy(data, bufp, len);
696
697         PDEBUG(5, "len = %d, status = %d\n", len, status);
698
699         usb_dev->status = status;
700         usb_dev->act_len = len;
701
702         return status == 0 ? len : status;
703 }
704
705 #endif  /* CONFIG_USB_SL811HS */