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