diff options
author | Wolfgang Denk <wd@denx.de> | 2009-01-28 21:09:46 +0100 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2009-01-28 21:09:46 +0100 |
commit | 6b7243aa89cdacefe46963dbe11cee0cbf021a49 (patch) | |
tree | 529dea142504dc68c22249a3003cca98f4eabdb8 | |
parent | ee64d0acc93f4f15850736eafaacdeadd7f12d25 (diff) | |
parent | 1a448db77b10153703bc5e4ad13dd55d88beb1d6 (diff) | |
download | u-boot-imx-6b7243aa89cdacefe46963dbe11cee0cbf021a49.zip u-boot-imx-6b7243aa89cdacefe46963dbe11cee0cbf021a49.tar.gz u-boot-imx-6b7243aa89cdacefe46963dbe11cee0cbf021a49.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-usb
36 files changed, 3410 insertions, 359 deletions
diff --git a/board/davinci/dvevm/dvevm.c b/board/davinci/dvevm/dvevm.c index abf60b3..bf36f73 100644 --- a/board/davinci/dvevm/dvevm.c +++ b/board/davinci/dvevm/dvevm.c @@ -52,6 +52,7 @@ int board_init(void) lpsc_on(DAVINCI_LPSC_UART0); lpsc_on(DAVINCI_LPSC_TIMER1); lpsc_on(DAVINCI_LPSC_GPIO); + lpsc_on(DAVINCI_LPSC_USB); #if !defined(CONFIG_SYS_USE_DSPLINK) /* Powerup the DSP */ @@ -101,3 +102,26 @@ int misc_init_r(void) return(0); } + +#ifdef CONFIG_USB_DAVINCI + +/* IO Expander I2C address and USB VBUS enable mask */ +#define IOEXP_I2C_ADDR 0x3A +#define IOEXP_VBUSEN_MASK 1 + +/* + * This function enables USB VBUS by writting to IO expander using I2C. + * Note that the I2C is already initialized at this stage. This + * function is used by davinci specific USB wrapper code. + */ +void enable_vbus(void) +{ + uchar data; /* IO Expander data to enable VBUS */ + + /* Write to IO expander to enable VBUS */ + i2c_read(IOEXP_I2C_ADDR, 0, 0, &data, 1); + data &= ~IOEXP_VBUSEN_MASK; + i2c_write(IOEXP_I2C_ADDR, 0, 0, &data, 1); +} +#endif + diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 423a29f..a18e16e 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -264,6 +264,16 @@ void usb_display_config(struct usb_device *dev) printf("\n"); } +static inline char *portspeed(int speed) +{ + if (speed == USB_SPEED_HIGH) + return "480 Mb/s"; + else if (speed == USB_SPEED_LOW) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} + /* shows the device tree recursively */ void usb_show_tree_graph(struct usb_device *dev, char *pre) { @@ -310,7 +320,7 @@ void usb_show_tree_graph(struct usb_device *dev, char *pre) pre[index] = 0; printf(" %s (%s, %dmA)\n", usb_get_class_desc( dev->config.if_desc[0].bInterfaceClass), - dev->slow ? "1.5MBit/s" : "12MBit/s", + portspeed(dev->speed), dev->config.MaxPower * 2); if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); diff --git a/common/usb.c b/common/usb.c index ee18152..87fca70 100644 --- a/common/usb.c +++ b/common/usb.c @@ -681,7 +681,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) err = usb_string_sub(dev, 0, 0, tbuf); if (err < 0) { USB_PRINTF("error getting string descriptor 0 " \ - "(error=%x)\n", dev->status); + "(error=%lx)\n", dev->status); return -1; } else if (tbuf[0] < 4) { USB_PRINTF("string descriptor 0 too short\n"); @@ -939,8 +939,10 @@ void usb_scan_devices(void) dev_index = 0; /* device 0 is always present (root hub, so let it analyze) */ dev = usb_alloc_new_device(); - usb_new_device(dev); - printf("%d USB Device(s) found\n", dev_index); + if (usb_new_device(dev)) + printf("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", dev_index); /* insert "driver" if possible */ #ifdef CONFIG_USB_KEYBOARD drv_usb_kbd_init(); @@ -1041,6 +1043,16 @@ struct usb_hub_device *usb_hub_allocate(void) #define MAX_TRIES 5 +static inline char *portspeed(int portstatus) +{ + if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) + return "480 Mb/s"; + else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} + static int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat) { @@ -1061,10 +1073,11 @@ static int hub_port_reset(struct usb_device *dev, int port, } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); + USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, portchange, - portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? \ - "Low Speed" : "High Speed"); + portspeed(portstatus)); + USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ " USB_PORT_STAT_ENABLE %d\n", (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, @@ -1109,9 +1122,7 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port) portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); USB_HUB_PRINTF("portstatus %x, change %x, %s\n", - portstatus, portchange, - portstatus&(1 << USB_PORT_FEAT_LOWSPEED) ? \ - "Low Speed" : "High Speed"); + portstatus, portchange, portspeed(portstatus)); /* Clear the connection change status */ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); @@ -1136,7 +1147,13 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port) /* Allocate a new device struct for it */ usb = usb_alloc_new_device(); - usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + + if (portstatus & USB_PORT_STAT_HIGH_SPEED) + usb->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + usb->speed = USB_SPEED_LOW; + else + usb->speed = USB_SPEED_FULL; dev->children[port] = usb; usb->parent = dev; diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 89e6ee7..e0d006c 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -183,6 +183,7 @@ int drv_usb_kbd_init(void) usb_kbd_dev.puts = NULL; usb_kbd_dev.getc = usb_kbd_getc; usb_kbd_dev.tstc = usb_kbd_testc; + usb_kbd_dev.priv = (void *)dev; error = device_register (&usb_kbd_dev); if(error==0) { /* check if this is the standard input device */ diff --git a/common/usb_storage.c b/common/usb_storage.c index 94f659f..51f0789 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -45,8 +45,6 @@ * New Note: * Support for USB Mass Storage Devices (BBB) has been added. It has * only been tested with USB memory sticks. - * Nota bene: if you are using the BBB support with a little-endian - * CPU then you MUST define LITTLEENDIAN in the configuration file! */ @@ -63,9 +61,9 @@ #undef BBB_XPORT_TRACE #ifdef USB_STOR_DEBUG -#define USB_STOR_PRINTF(fmt,args...) printf (fmt ,##args) +#define USB_STOR_PRINTF(fmt, args...) printf(fmt , ##args) #else -#define USB_STOR_PRINTF(fmt,args...) +#define USB_STOR_PRINTF(fmt, args...) #endif #include <scsi.h> @@ -110,7 +108,7 @@ typedef struct { __u8 CBWCDB[CBWCDBLENGTH]; } umass_bbb_cbw_t; #define UMASS_BBB_CBW_SIZE 31 -static __u32 CBWTag = 0; +static __u32 CBWTag; /* Command Status Wrapper */ typedef struct { @@ -126,34 +124,35 @@ typedef struct { #define UMASS_BBB_CSW_SIZE 13 #define USB_MAX_STOR_DEV 5 -static int usb_max_devs = 0; /* number of highest available usb device */ +static int usb_max_devs; /* number of highest available usb device */ static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV]; struct us_data; -typedef int (*trans_cmnd)(ccb*, struct us_data*); -typedef int (*trans_reset)(struct us_data*); +typedef int (*trans_cmnd)(ccb *cb, struct us_data *data); +typedef int (*trans_reset)(struct us_data *data); struct us_data { - struct usb_device *pusb_dev; /* this usb_device */ - unsigned int flags; /* from filter initially */ - unsigned char ifnum; /* interface number */ - unsigned char ep_in; /* in endpoint */ - unsigned char ep_out; /* out ....... */ - unsigned char ep_int; /* interrupt . */ - unsigned char subclass; /* as in overview */ - unsigned char protocol; /* .............. */ - unsigned char attention_done; /* force attn on first cmd */ - unsigned short ip_data; /* interrupt data */ - int action; /* what to do */ - int ip_wanted; /* needed */ - int *irq_handle; /* for USB int requests */ - unsigned int irqpipe; /* pipe for release_irq */ - unsigned char irqmaxp; /* max packed for irq Pipe */ - unsigned char irqinterval; /* Intervall for IRQ Pipe */ - ccb *srb; /* current srb */ - trans_reset transport_reset; /* reset routine */ - trans_cmnd transport; /* transport routine */ + struct usb_device *pusb_dev; /* this usb_device */ + + unsigned int flags; /* from filter initially */ + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ + unsigned char attention_done; /* force attn on first cmd */ + unsigned short ip_data; /* interrupt data */ + int action; /* what to do */ + int ip_wanted; /* needed */ + int *irq_handle; /* for USB int requests */ + unsigned int irqpipe; /* pipe for release_irq */ + unsigned char irqmaxp; /* max packed for irq Pipe */ + unsigned char irqinterval; /* Intervall for IRQ Pipe */ + ccb *srb; /* current srb */ + trans_reset transport_reset; /* reset routine */ + trans_cmnd transport; /* transport routine */ }; static struct us_data usb_stor[USB_MAX_STOR_DEV]; @@ -163,10 +162,12 @@ static struct us_data usb_stor[USB_MAX_STOR_DEV]; #define USB_STOR_TRANSPORT_FAILED -1 #define USB_STOR_TRANSPORT_ERROR -2 - -int usb_stor_get_info(struct usb_device *dev, struct us_data *us, block_dev_desc_t *dev_desc); -int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data *ss); -unsigned long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, void *buffer); +int usb_stor_get_info(struct usb_device *dev, struct us_data *us, + block_dev_desc_t *dev_desc); +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, + struct us_data *ss); +unsigned long usb_stor_read(int device, unsigned long blknr, + unsigned long blkcnt, void *buffer); struct usb_device * usb_get_dev_index(int index); void uhci_show_temp_int_td(void); @@ -181,7 +182,7 @@ void usb_show_progress(void) printf("."); } -/********************************************************************************* +/******************************************************************************* * show info on storage devices; 'usb start/init' must be invoked earlier * as we only retrieve structures populated during devices initialization */ @@ -191,7 +192,7 @@ int usb_stor_info(void) if (usb_max_devs > 0) { for (i = 0; i < usb_max_devs; i++) { - printf (" Device %d: ", i); + printf(" Device %d: ", i); dev_print(&usb_dev_desc[i]); } return 0; @@ -201,7 +202,7 @@ int usb_stor_info(void) return 1; } -/********************************************************************************* +/******************************************************************************* * scan the usb and reports device info * to the user if mode = 1 * returns current device or -1 if no @@ -214,67 +215,69 @@ int usb_stor_scan(int mode) /* GJ */ memset(usb_stor_buf, 0, sizeof(usb_stor_buf)); - if(mode==1) { + if (mode == 1) printf(" scanning bus for storage devices... "); - } + usb_disable_asynch(1); /* asynch transfer not allowed */ - for(i=0;i<USB_MAX_STOR_DEV;i++) { - memset(&usb_dev_desc[i],0,sizeof(block_dev_desc_t)); - usb_dev_desc[i].target=0xff; - usb_dev_desc[i].if_type=IF_TYPE_USB; - usb_dev_desc[i].dev=i; - usb_dev_desc[i].part_type=PART_TYPE_UNKNOWN; - usb_dev_desc[i].block_read=usb_stor_read; + for (i = 0; i < USB_MAX_STOR_DEV; i++) { + memset(&usb_dev_desc[i], 0, sizeof(block_dev_desc_t)); + usb_dev_desc[i].target = 0xff; + usb_dev_desc[i].if_type = IF_TYPE_USB; + usb_dev_desc[i].dev = i; + usb_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + usb_dev_desc[i].block_read = usb_stor_read; } - usb_max_devs=0; - for(i=0;i<USB_MAX_DEVICE;i++) { - dev=usb_get_dev_index(i); /* get device */ - USB_STOR_PRINTF("i=%d\n",i); - if(dev==NULL) { + usb_max_devs = 0; + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); /* get device */ + USB_STOR_PRINTF("i=%d\n", i); + if (dev == NULL) break; /* no more devices avaiable */ - } - if(usb_storage_probe(dev,0,&usb_stor[usb_max_devs])) { /* ok, it is a storage devices */ - /* get info and fill it in */ - if(usb_stor_get_info(dev, &usb_stor[usb_max_devs], &usb_dev_desc[usb_max_devs])) + + if (usb_storage_probe(dev, 0, &usb_stor[usb_max_devs])) { + /* ok, it is a storage devices + * get info and fill it in + */ + if (usb_stor_get_info(dev, &usb_stor[usb_max_devs], + &usb_dev_desc[usb_max_devs])) usb_max_devs++; - } /* if storage device */ - if(usb_max_devs==USB_MAX_STOR_DEV) { - printf("max USB Storage Device reached: %d stopping\n",usb_max_devs); + } + /* if storage device */ + if (usb_max_devs == USB_MAX_STOR_DEV) { + printf("max USB Storage Device reached: %d stopping\n", + usb_max_devs); break; } } /* for */ usb_disable_asynch(0); /* asynch transfer allowed */ printf("%d Storage Device(s) found\n", usb_max_devs); - if(usb_max_devs>0) + if (usb_max_devs > 0) return 0; - else - return-1; + return -1; } static int usb_stor_irq(struct usb_device *dev) { struct us_data *us; - us=(struct us_data *)dev->privptr; + us = (struct us_data *)dev->privptr; - if(us->ip_wanted) { - us->ip_wanted=0; - } + if (us->ip_wanted) + us->ip_wanted = 0; return 0; } #ifdef USB_STOR_DEBUG -static void usb_show_srb(ccb * pccb) +static void usb_show_srb(ccb *pccb) { int i; - printf("SRB: len %d datalen 0x%lX\n ",pccb->cmdlen,pccb->datalen); - for(i=0;i<12;i++) { - printf("%02X ",pccb->cmd[i]); - } + printf("SRB: len %d datalen 0x%lX\n ", pccb->cmdlen, pccb->datalen); + for (i = 0; i < 12; i++) + printf("%02X ", pccb->cmd[i]); printf("\n"); } @@ -322,11 +325,14 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) USB_STOR_PRINTF("Bulk xfer 0x%x(%d) try #%d\n", (unsigned int)buf, this_xfer, 11 - maxtry); result = usb_bulk_msg(us->pusb_dev, pipe, buf, - this_xfer, &partial, USB_CNTL_TIMEOUT*5); + this_xfer, &partial, + USB_CNTL_TIMEOUT * 5); USB_STOR_PRINTF("bulk_msg returned %d xferred %d/%d\n", result, partial, this_xfer); - if(us->pusb_dev->status!=0) { - /* if we stall, we need to clear it before we go on */ + if (us->pusb_dev->status != 0) { + /* if we stall, we need to clear it before + * we go on + */ #ifdef USB_STOR_DEBUG display_int_status(us->pusb_dev->status); #endif @@ -334,9 +340,9 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) USB_STOR_PRINTF("stalled ->clearing endpoint halt for pipe 0x%x\n", pipe); stat = us->pusb_dev->status; usb_clear_halt(us->pusb_dev, pipe); - us->pusb_dev->status=stat; - if(this_xfer == partial) { - USB_STOR_PRINTF("bulk transferred with error %X, but data ok\n",us->pusb_dev->status); + us->pusb_dev->status = stat; + if (this_xfer == partial) { + USB_STOR_PRINTF("bulk transferred with error %X, but data ok\n", us->pusb_dev->status); return 0; } else @@ -346,12 +352,15 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) USB_STOR_PRINTF("Device NAKed bulk_msg\n"); return result; } - if(this_xfer == partial) { - USB_STOR_PRINTF("bulk transferred with error %d, but data ok\n",us->pusb_dev->status); + USB_STOR_PRINTF("bulk transferred with error"); + if (this_xfer == partial) { + USB_STOR_PRINTF(" %d, but data ok\n", + us->pusb_dev->status); return 0; } /* if our try counter reaches 0, bail out */ - USB_STOR_PRINTF("bulk transferred with error %d, data %d\n",us->pusb_dev->status,partial); + USB_STOR_PRINTF(" %d, data %d\n", + us->pusb_dev->status, partial); if (!maxtry--) return result; } @@ -359,7 +368,7 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) this_xfer -= partial; buf += partial; /* continue until this transfer is done */ - } while ( this_xfer ); + } while (this_xfer); } /* if we get here, we're done and successful */ @@ -386,29 +395,33 @@ static int usb_stor_BBB_reset(struct us_data *us) * This comment stolen from FreeBSD's /sys/dev/usb/umass.c. */ USB_STOR_PRINTF("BBB_reset\n"); - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_BBB_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, us->ifnum, 0, 0, USB_CNTL_TIMEOUT*5); + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_BBB_RESET, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, 0, 0, USB_CNTL_TIMEOUT * 5); - if((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) - { + if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { USB_STOR_PRINTF("RESET:stall\n"); return -1; } /* long wait for reset */ wait_ms(150); - USB_STOR_PRINTF("BBB_reset result %d: status %X reset\n",result,us->pusb_dev->status); + USB_STOR_PRINTF("BBB_reset result %d: status %X reset\n", result, + us->pusb_dev->status); pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); result = usb_clear_halt(us->pusb_dev, pipe); /* long wait for reset */ wait_ms(150); - USB_STOR_PRINTF("BBB_reset result %d: status %X clearing IN endpoint\n",result,us->pusb_dev->status); + USB_STOR_PRINTF("BBB_reset result %d: status %X clearing IN endpoint\n", + result, us->pusb_dev->status); /* long wait for reset */ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); result = usb_clear_halt(us->pusb_dev, pipe); wait_ms(150); - USB_STOR_PRINTF("BBB_reset result %d: status %X clearing OUT endpoint\n",result,us->pusb_dev->status); + USB_STOR_PRINTF("BBB_reset result %d: status %X" + " clearing OUT endpoint\n", result, + us->pusb_dev->status); USB_STOR_PRINTF("BBB_reset done\n"); return 0; } @@ -423,16 +436,20 @@ static int usb_stor_CB_reset(struct us_data *us) int result; USB_STOR_PRINTF("CB_reset\n"); - memset(cmd, 0xFF, sizeof(cmd)); + memset(cmd, 0xff, sizeof(cmd)); cmd[0] = SCSI_SEND_DIAG; cmd[1] = 4; - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, us->ifnum, cmd, sizeof(cmd), USB_CNTL_TIMEOUT*5); + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, cmd, sizeof(cmd), + USB_CNTL_TIMEOUT * 5); /* long wait for reset */ wait_ms(1500); - USB_STOR_PRINTF("CB_reset result %d: status %X clearing endpoint halt\n",result,us->pusb_dev->status); + USB_STOR_PRINTF("CB_reset result %d: status %X" + " clearing endpoint halt\n", result, + us->pusb_dev->status); usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); @@ -455,9 +472,11 @@ int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) dir_in = US_DIRECTION(srb->cmd[0]); #ifdef BBB_COMDAT_TRACE - printf("dir %d lun %d cmdlen %d cmd %p datalen %d pdata %p\n", dir_in, srb->lun, srb->cmdlen, srb->cmd, srb->datalen, srb->pdata); + printf("dir %d lun %d cmdlen %d cmd %p datalen %d pdata %p\n", + dir_in, srb->lun, srb->cmdlen, srb->cmd, srb->datalen, + srb->pdata); if (srb->cmdlen) { - for(result = 0;result < srb->cmdlen;result++) + for (result = 0; result < srb->cmdlen; result++) printf("cmd[%d] %#x ", result, srb->cmd[result]); printf("\n"); } @@ -474,13 +493,14 @@ int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) cbw.dCBWSignature = cpu_to_le32(CBWSIGNATURE); cbw.dCBWTag = cpu_to_le32(CBWTag++); cbw.dCBWDataTransferLength = cpu_to_le32(srb->datalen); - cbw.bCBWFlags = (dir_in? CBWFLAGS_IN : CBWFLAGS_OUT); + cbw.bCBWFlags = (dir_in ? CBWFLAGS_IN : CBWFLAGS_OUT); cbw.bCBWLUN = srb->lun; cbw.bCDBLength = srb->cmdlen; /* copy the command data into the CBW command data buffer */ /* DST SRC LEN!!! */ memcpy(cbw.CBWCDB, srb->cmd, srb->cmdlen); - result = usb_bulk_msg(us->pusb_dev, pipe, &cbw, UMASS_BBB_CBW_SIZE, &actlen, USB_CNTL_TIMEOUT*5); + result = usb_bulk_msg(us->pusb_dev, pipe, &cbw, UMASS_BBB_CBW_SIZE, + &actlen, USB_CNTL_TIMEOUT * 5); if (result < 0) USB_STOR_PRINTF("usb_stor_BBB_comdat:usb_bulk_msg error\n"); return result; @@ -492,46 +512,61 @@ int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) int usb_stor_CB_comdat(ccb *srb, struct us_data *us) { int result = 0; - int dir_in,retry; + int dir_in, retry; unsigned int pipe; unsigned long status; - retry=5; - dir_in=US_DIRECTION(srb->cmd[0]); + retry = 5; + dir_in = US_DIRECTION(srb->cmd[0]); - if(dir_in) - pipe=usb_rcvbulkpipe(us->pusb_dev, us->ep_in); - else - pipe=usb_sndbulkpipe(us->pusb_dev, us->ep_out); - while(retry--) { - USB_STOR_PRINTF("CBI gets a command: Try %d\n",5-retry); + if (dir_in) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + while (retry--) { + USB_STOR_PRINTF("CBI gets a command: Try %d\n", 5 - retry); #ifdef USB_STOR_DEBUG usb_show_srb(srb); #endif /* let's send the command via the control pipe */ - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev , 0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, - srb->cmd, srb->cmdlen, USB_CNTL_TIMEOUT*5); - USB_STOR_PRINTF("CB_transport: control msg returned %d, status %X\n",result,us->pusb_dev->status); + srb->cmd, srb->cmdlen, + USB_CNTL_TIMEOUT * 5); + USB_STOR_PRINTF("CB_transport: control msg returned %d," + " status %X\n", result, us->pusb_dev->status); /* check the return code for the command */ if (result < 0) { - if(us->pusb_dev->status & USB_ST_STALLED) { - status=us->pusb_dev->status; - USB_STOR_PRINTF(" stall during command found, clear pipe\n"); - usb_clear_halt(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0)); - us->pusb_dev->status=status; + if (us->pusb_dev->status & USB_ST_STALLED) { + status = us->pusb_dev->status; + USB_STOR_PRINTF(" stall during command found," + " clear pipe\n"); + usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, 0)); + us->pusb_dev->status = status; } - USB_STOR_PRINTF(" error during command %02X Stat = %X\n",srb->cmd[0],us->pusb_dev->status); + USB_STOR_PRINTF(" error during command %02X" + " Stat = %X\n", srb->cmd[0], + us->pusb_dev->status); return result; } /* transfer the data payload for this command, if one exists*/ - USB_STOR_PRINTF("CB_transport: control msg returned %d, direction is %s to go 0x%lx\n",result,dir_in ? "IN" : "OUT",srb->datalen); + USB_STOR_PRINTF("CB_transport: control msg returned %d," + " direction is %s to go 0x%lx\n", result, + dir_in ? "IN" : "OUT", srb->datalen); if (srb->datalen) { - result = us_one_transfer(us, pipe, (char *)srb->pdata,srb->datalen); - USB_STOR_PRINTF("CBI attempted to transfer data, result is %d status %lX, len %d\n", result,us->pusb_dev->status,us->pusb_dev->act_len); - if(!(us->pusb_dev->status & USB_ST_NAK_REC)) + result = us_one_transfer(us, pipe, (char *)srb->pdata, + srb->datalen); + USB_STOR_PRINTF("CBI attempted to transfer data," + " result is %d status %lX, len %d\n", + result, us->pusb_dev->status, + us->pusb_dev->act_len); + if (!(us->pusb_dev->status & USB_ST_NAK_REC)) break; } /* if (srb->datalen) */ else @@ -543,21 +578,21 @@ int usb_stor_CB_comdat(ccb *srb, struct us_data *us) } -int usb_stor_CBI_get_status (ccb * srb, struct us_data *us) +int usb_stor_CBI_get_status(ccb *srb, struct us_data *us) { int timeout; us->ip_wanted = 1; - submit_int_msg (us->pusb_dev, us->irqpipe, + submit_int_msg(us->pusb_dev, us->irqpipe, (void *) &us->ip_data, us->irqmaxp, us->irqinterval); timeout = 1000; while (timeout--) { if ((volatile int *) us->ip_wanted == 0) break; - wait_ms (10); + wait_ms(10); } if (us->ip_wanted) { - printf (" Did not get interrupt on CBI\n"); + printf(" Did not get interrupt on CBI\n"); us->ip_wanted = 0; return USB_STOR_TRANSPORT_ERROR; } @@ -596,9 +631,9 @@ int usb_stor_BBB_clear_endpt_stall(struct us_data *us, __u8 endpt) int result; /* ENDPOINT_HALT = 0, so set value to 0 */ - result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, - 0, endpt, 0, 0, USB_CNTL_TIMEOUT*5); + 0, endpt, 0, 0, USB_CNTL_TIMEOUT * 5); return result; } @@ -638,12 +673,14 @@ int usb_stor_BBB_transport(ccb *srb, struct us_data *us) pipe = pipein; else pipe = pipeout; - result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, &data_actlen, USB_CNTL_TIMEOUT*5); + result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, + &data_actlen, USB_CNTL_TIMEOUT * 5); /* special handling of STALL in DATA phase */ - if((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { + if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { USB_STOR_PRINTF("DATA:stall\n"); /* clear the STALL on the endpoint */ - result = usb_stor_BBB_clear_endpt_stall(us, dir_in? us->ep_in : us->ep_out); + result = usb_stor_BBB_clear_endpt_stall(us, + dir_in ? us->ep_in : us->ep_out); if (result >= 0) /* continue on to STATUS phase */ goto st; @@ -660,15 +697,16 @@ int usb_stor_BBB_transport(ccb *srb, struct us_data *us) printf("\n"); #endif /* STATUS phase + error handling */ - st: +st: retry = 0; - again: +again: USB_STOR_PRINTF("STATUS phase\n"); result = usb_bulk_msg(us->pusb_dev, pipein, &csw, UMASS_BBB_CSW_SIZE, &actlen, USB_CNTL_TIMEOUT*5); /* special handling of STALL in STATUS phase */ - if((result < 0) && (retry < 1) && (us->pusb_dev->status & USB_ST_STALLED)) { + if ((result < 0) && (retry < 1) && + (us->pusb_dev->status & USB_ST_STALLED)) { USB_STOR_PRINTF("STATUS:stall\n"); /* clear the STALL on the endpoint */ result = usb_stor_BBB_clear_endpt_stall(us, us->ep_in); @@ -722,32 +760,33 @@ int usb_stor_BBB_transport(ccb *srb, struct us_data *us) int usb_stor_CB_transport(ccb *srb, struct us_data *us) { - int result,status; + int result, status; ccb *psrb; ccb reqsrb; - int retry,notready; + int retry, notready; psrb = &reqsrb; - status=USB_STOR_TRANSPORT_GOOD; - retry=0; - notready=0; + status = USB_STOR_TRANSPORT_GOOD; + retry = 0; + notready = 0; /* issue the command */ do_retry: - result=usb_stor_CB_comdat(srb,us); - USB_STOR_PRINTF("command / Data returned %d, status %X\n",result,us->pusb_dev->status); + result = usb_stor_CB_comdat(srb, us); + USB_STOR_PRINTF("command / Data returned %d, status %X\n", + result, us->pusb_dev->status); /* if this is an CBI Protocol, get IRQ */ - if(us->protocol==US_PR_CBI) { - status=usb_stor_CBI_get_status(srb,us); + if (us->protocol == US_PR_CBI) { + status = usb_stor_CBI_get_status(srb, us); /* if the status is error, report it */ - if(status==USB_STOR_TRANSPORT_ERROR) { + if (status == USB_STOR_TRANSPORT_ERROR) { USB_STOR_PRINTF(" USB CBI Command Error\n"); return status; } - srb->sense_buf[12]=(unsigned char)(us->ip_data>>8); - srb->sense_buf[13]=(unsigned char)(us->ip_data&0xff); - if(!us->ip_data) { - /* if the status is good, report it */ - if(status==USB_STOR_TRANSPORT_GOOD) { + srb->sense_buf[12] = (unsigned char)(us->ip_data >> 8); + srb->sense_buf[13] = (unsigned char)(us->ip_data & 0xff); + if (!us->ip_data) { + /* if the status is good, report it */ + if (status == USB_STOR_TRANSPORT_GOOD) { USB_STOR_PRINTF(" USB CBI Command Good\n"); return status; } @@ -755,51 +794,62 @@ do_retry: } /* do we have to issue an auto request? */ /* HERE we have to check the result */ - if((result<0) && !(us->pusb_dev->status & USB_ST_STALLED)) { - USB_STOR_PRINTF("ERROR %X\n",us->pusb_dev->status); + if ((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + USB_STOR_PRINTF("ERROR %X\n", us->pusb_dev->status); us->transport_reset(us); return USB_STOR_TRANSPORT_ERROR; } - if((us->protocol==US_PR_CBI) && - ((srb->cmd[0]==SCSI_REQ_SENSE) || - (srb->cmd[0]==SCSI_INQUIRY))) { /* do not issue an autorequest after request sense */ + if ((us->protocol == US_PR_CBI) && + ((srb->cmd[0] == SCSI_REQ_SENSE) || + (srb->cmd[0] == SCSI_INQUIRY))) { + /* do not issue an autorequest after request sense */ USB_STOR_PRINTF("No auto request and good\n"); return USB_STOR_TRANSPORT_GOOD; } /* issue an request_sense */ - memset(&psrb->cmd[0],0,12); - psrb->cmd[0]=SCSI_REQ_SENSE; - psrb->cmd[1]=srb->lun<<5; - psrb->cmd[4]=18; - psrb->datalen=18; + memset(&psrb->cmd[0], 0, 12); + psrb->cmd[0] = SCSI_REQ_SENSE; + psrb->cmd[1] = srb->lun << 5; + psrb->cmd[4] = 18; + psrb->datalen = 18; psrb->pdata = &srb->sense_buf[0]; - psrb->cmdlen=12; + psrb->cmdlen = 12; /* issue the command */ - result=usb_stor_CB_comdat(psrb,us); - USB_STOR_PRINTF("auto request returned %d\n",result); + result = usb_stor_CB_comdat(psrb, us); + USB_STOR_PRINTF("auto request returned %d\n", result); /* if this is an CBI Protocol, get IRQ */ - if(us->protocol==US_PR_CBI) { - status=usb_stor_CBI_get_status(psrb,us); - } - if((result<0)&&!(us->pusb_dev->status & USB_ST_STALLED)) { - USB_STOR_PRINTF(" AUTO REQUEST ERROR %d\n",us->pusb_dev->status); + if (us->protocol == US_PR_CBI) + status = usb_stor_CBI_get_status(psrb, us); + + if ((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + USB_STOR_PRINTF(" AUTO REQUEST ERROR %d\n", + us->pusb_dev->status); return USB_STOR_TRANSPORT_ERROR; } - USB_STOR_PRINTF("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\n",srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + USB_STOR_PRINTF("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\n", + srb->sense_buf[0], srb->sense_buf[2], + srb->sense_buf[12], srb->sense_buf[13]); /* Check the auto request result */ - if((srb->sense_buf[2]==0) && - (srb->sense_buf[12]==0) && - (srb->sense_buf[13]==0)) /* ok, no sense */ + if ((srb->sense_buf[2] == 0) && + (srb->sense_buf[12] == 0) && + (srb->sense_buf[13] == 0)) { + /* ok, no sense */ return USB_STOR_TRANSPORT_GOOD; + } + /* Check the auto request result */ - switch(srb->sense_buf[2]) { - case 0x01: /* Recovered Error */ + switch (srb->sense_buf[2]) { + case 0x01: + /* Recovered Error */ return USB_STOR_TRANSPORT_GOOD; break; - case 0x02: /* Not Ready */ - if(notready++ > USB_TRANSPORT_NOT_READY_RETRY) { - printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X (NOT READY)\n", - srb->cmd[0],srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + case 0x02: + /* Not Ready */ + if (notready++ > USB_TRANSPORT_NOT_READY_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X" + " 0x%02X (NOT READY)\n", srb->cmd[0], + srb->sense_buf[0], srb->sense_buf[2], + srb->sense_buf[12], srb->sense_buf[13]); return USB_STOR_TRANSPORT_FAILED; } else { wait_ms(100); @@ -807,113 +857,116 @@ do_retry: } break; default: - if(retry++ > USB_TRANSPORT_UNKNOWN_RETRY) { - printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X\n", - srb->cmd[0],srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + if (retry++ > USB_TRANSPORT_UNKNOWN_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X" + " 0x%02X\n", srb->cmd[0], srb->sense_buf[0], + srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); return USB_STOR_TRANSPORT_FAILED; - } else { + } else goto do_retry; - } break; } return USB_STOR_TRANSPORT_FAILED; } -static int usb_inquiry(ccb *srb,struct us_data *ss) +static int usb_inquiry(ccb *srb, struct us_data *ss) { - int retry,i; - retry=5; + int retry, i; + retry = 5; do { - memset(&srb->cmd[0],0,12); - srb->cmd[0]=SCSI_INQUIRY; - srb->cmd[1]=srb->lun<<5; - srb->cmd[4]=36; - srb->datalen=36; - srb->cmdlen=12; - i=ss->transport(srb,ss); - USB_STOR_PRINTF("inquiry returns %d\n",i); - if(i==0) + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_INQUIRY; + srb->cmd[1] = srb->lun<<5; + srb->cmd[4] = 36; + srb->datalen = 36; + srb->cmdlen = 12; + i = ss->transport(srb, ss); + USB_STOR_PRINTF("inquiry returns %d\n", i); + if (i == 0) break; - } while(retry--); + } while (retry--); - if(!retry) { + if (!retry) { printf("error in inquiry\n"); return -1; } return 0; } -static int usb_request_sense(ccb *srb,struct us_data *ss) +static int usb_request_sense(ccb *srb, struct us_data *ss) { char *ptr; - ptr=(char *)srb->pdata; - memset(&srb->cmd[0],0,12); - srb->cmd[0]=SCSI_REQ_SENSE; - srb->cmd[1]=srb->lun<<5; - srb->cmd[4]=18; - srb->datalen=18; + ptr = (char *)srb->pdata; + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_REQ_SENSE; + srb->cmd[1] = srb->lun << 5; + srb->cmd[4] = 18; + srb->datalen = 18; srb->pdata = &srb->sense_buf[0]; - srb->cmdlen=12; - ss->transport(srb,ss); - USB_STOR_PRINTF("Request Sense returned %02X %02X %02X\n",srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); - srb->pdata=(uchar *)ptr; + srb->cmdlen = 12; + ss->transport(srb, ss); + USB_STOR_PRINTF("Request Sense returned %02X %02X %02X\n", + srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); + srb->pdata = (uchar *)ptr; return 0; } -static int usb_test_unit_ready(ccb *srb,struct us_data *ss) +static int usb_test_unit_ready(ccb *srb, struct us_data *ss) { int retries = 10; do { - memset(&srb->cmd[0],0,12); - srb->cmd[0]=SCSI_TST_U_RDY; - srb->cmd[1]=srb->lun<<5; - srb->datalen=0; - srb->cmdlen=12; - if(ss->transport(srb,ss)==USB_STOR_TRANSPORT_GOOD) { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_TST_U_RDY; + srb->cmd[1] = srb->lun << 5; + srb->datalen = 0; + srb->cmdlen = 12; + if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) return 0; - } - usb_request_sense (srb, ss); - wait_ms (100); - } while(retries--); + usb_request_sense(srb, ss); + wait_ms(100); + } while (retries--); return -1; } -static int usb_read_capacity(ccb *srb,struct us_data *ss) +static int usb_read_capacity(ccb *srb, struct us_data *ss) { int retry; - retry = 3; /* retries */ + /* XXX retries */ + retry = 3; do { - memset(&srb->cmd[0],0,12); - srb->cmd[0]=SCSI_RD_CAPAC; - srb->cmd[1]=srb->lun<<5; - srb->datalen=8; - srb->cmdlen=12; - if(ss->transport(srb,ss)==USB_STOR_TRANSPORT_GOOD) { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_RD_CAPAC; + srb->cmd[1] = srb->lun << 5; + srb->datalen = 8; + srb->cmdlen = 12; + if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) return 0; - } - } while(retry--); + } while (retry--); return -1; } -static int usb_read_10(ccb *srb,struct us_data *ss, unsigned long start, unsigned short blocks) +static int usb_read_10(ccb *srb, struct us_data *ss, unsigned long start, + unsigned short blocks) { - memset(&srb->cmd[0],0,12); - srb->cmd[0]=SCSI_READ10; - srb->cmd[1]=srb->lun<<5; - srb->cmd[2]=((unsigned char) (start>>24))&0xff; - srb->cmd[3]=((unsigned char) (start>>16))&0xff; - srb->cmd[4]=((unsigned char) (start>>8))&0xff; - srb->cmd[5]=((unsigned char) (start))&0xff; - srb->cmd[7]=((unsigned char) (blocks>>8))&0xff; - srb->cmd[8]=(unsigned char) blocks & 0xff; - srb->cmdlen=12; - USB_STOR_PRINTF("read10: start %lx blocks %x\n",start,blocks); - return ss->transport(srb,ss); + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_READ10; + srb->cmd[1] = srb->lun << 5; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + USB_STOR_PRINTF("read10: start %lx blocks %x\n", start, blocks); + return ss->transport(srb, ss); } @@ -933,85 +986,94 @@ static void usb_bin_fixup(struct usb_device_descriptor descriptor, const unsigned char max_vendor_len = 40; const unsigned char max_product_len = 20; if (descriptor.idVendor == 0x0424 && descriptor.idProduct == 0x223a) { - strncpy ((char *)vendor, "SMSC", max_vendor_len); - strncpy ((char *)product, "Flash Media Cntrller", max_product_len); + strncpy((char *)vendor, "SMSC", max_vendor_len); + strncpy((char *)product, "Flash Media Cntrller", + max_product_len); } } #endif /* CONFIG_USB_BIN_FIXUP */ #define USB_MAX_READ_BLK 20 -unsigned long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, void *buffer) +unsigned long usb_stor_read(int device, unsigned long blknr, + unsigned long blkcnt, void *buffer) { - unsigned long start,blks, buf_addr; + unsigned long start, blks, buf_addr; unsigned short smallblks; struct usb_device *dev; - int retry,i; + int retry, i; ccb *srb = &usb_ccb; if (blkcnt == 0) return 0; device &= 0xff; - /* Setup device - */ - USB_STOR_PRINTF("\nusb_read: dev %d \n",device); - dev=NULL; - for(i=0;i<USB_MAX_DEVICE;i++) { - dev=usb_get_dev_index(i); - if(dev==NULL) { + /* Setup device */ + USB_STOR_PRINTF("\nusb_read: dev %d \n", device); + dev = NULL; + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); + if (dev == NULL) return 0; - } - if(dev->devnum==usb_dev_desc[device].target) + if (dev->devnum == usb_dev_desc[device].target) break; } usb_disable_asynch(1); /* asynch transfer not allowed */ - srb->lun=usb_dev_desc[device].lun; - buf_addr=(unsigned long)buffer; - start=blknr; - blks=blkcnt; - if(usb_test_unit_ready(srb,(struct us_data *)dev->privptr)) { - printf("Device NOT ready\n Request Sense returned %02X %02X %02X\n", - srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + srb->lun = usb_dev_desc[device].lun; + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + if (usb_test_unit_ready(srb, (struct us_data *)dev->privptr)) { + printf("Device NOT ready\n Request Sense returned %02X %02X" + " %02X\n", srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); return 0; } - USB_STOR_PRINTF("\nusb_read: dev %d startblk %lx, blccnt %lx buffer %lx\n",device,start,blks, buf_addr); + + USB_STOR_PRINTF("\nusb_read: dev %d startblk %lx, blccnt %lx" + " buffer %lx\n", device, start, blks, buf_addr); + do { - retry=2; - srb->pdata=(unsigned char *)buf_addr; - if(blks>USB_MAX_READ_BLK) { - smallblks=USB_MAX_READ_BLK; - } else { - smallblks=(unsigned short) blks; - } + /* XXX need some comment here */ + retry = 2; + srb->pdata = (unsigned char *)buf_addr; + if (blks > USB_MAX_READ_BLK) + smallblks = USB_MAX_READ_BLK; + else + smallblks = (unsigned short) blks; retry_it: - if(smallblks==USB_MAX_READ_BLK) + if (smallblks == USB_MAX_READ_BLK) usb_show_progress(); - srb->datalen=usb_dev_desc[device].blksz * smallblks; - srb->pdata=(unsigned char *)buf_addr; - if(usb_read_10(srb,(struct us_data *)dev->privptr, start, smallblks)) { + srb->datalen = usb_dev_desc[device].blksz * smallblks; + srb->pdata = (unsigned char *)buf_addr; + if (usb_read_10(srb, (struct us_data *)dev->privptr, start, + smallblks)) { USB_STOR_PRINTF("Read ERROR\n"); - usb_request_sense(srb,(struct us_data *)dev->privptr); - if(retry--) + usb_request_sense(srb, (struct us_data *)dev->privptr); + if (retry--) goto retry_it; - blkcnt-=blks; + blkcnt -= blks; break; } - start+=smallblks; - blks-=smallblks; - buf_addr+=srb->datalen; - } while(blks!=0); - USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\n",start,smallblks,buf_addr); + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } while (blks != 0); + + USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\n", + start, smallblks, buf_addr); + usb_disable_asynch(0); /* asynch transfer allowed */ - if(blkcnt>=USB_MAX_READ_BLK) + if (blkcnt >= USB_MAX_READ_BLK) printf("\n"); - return(blkcnt); + return blkcnt; } /* Probe to see if a new device is actually a Storage device */ -int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data *ss) +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, + struct us_data *ss) { struct usb_interface_descriptor *iface; int i; @@ -1025,8 +1087,11 @@ int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data #if 0 /* this is the place to patch some storage devices */ - USB_STOR_PRINTF("iVendor %X iProduct %X\n",dev->descriptor.idVendor,dev->descriptor.idProduct); - if ((dev->descriptor.idVendor) == 0x066b && (dev->descriptor.idProduct) == 0x0103) { + USB_STOR_PRINTF("iVendor %X iProduct %X\n", dev->descriptor.idVendor, + dev->descriptor.idProduct); + + if ((dev->descriptor.idVendor) == 0x066b && + (dev->descriptor.idProduct) == 0x0103) { USB_STOR_PRINTF("patched for E-USB\n"); protocol = US_PR_CB; subclass = US_SC_UFI; /* an assumption */ @@ -1095,19 +1160,20 @@ int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data */ for (i = 0; i < iface->bNumEndpoints; i++) { /* is it an BULK endpoint? */ - if ((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK) { + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN) ss->ep_in = iface->ep_desc[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; else - ss->ep_out = iface->ep_desc[i].bEndpointAddress & + ss->ep_out = + iface->ep_desc[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; } /* is it an interrupt endpoint? */ - if ((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT) { + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { ss->ep_int = iface->ep_desc[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; ss->irqinterval = iface->ep_desc[i].bInterval; @@ -1130,26 +1196,28 @@ int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data */ if (ss->subclass != US_SC_UFI && ss->subclass != US_SC_SCSI && ss->subclass != US_SC_8070) { - printf("Sorry, protocol %d not yet supported.\n",ss->subclass); + printf("Sorry, protocol %d not yet supported.\n", ss->subclass); return 0; } - if(ss->ep_int) { /* we had found an interrupt endpoint, prepare irq pipe */ - /* set up the IRQ pipe and handler */ - + if (ss->ep_int) { + /* we had found an interrupt endpoint, prepare irq pipe + * set up the IRQ pipe and handler + */ ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255; ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe); - dev->irq_handle=usb_stor_irq; + dev->irq_handle = usb_stor_irq; } - dev->privptr=(void *)ss; + dev->privptr = (void *)ss; return 1; } -int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t *dev_desc) +int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, + block_dev_desc_t *dev_desc) { - unsigned char perq,modi; + unsigned char perq, modi; unsigned long cap[2]; - unsigned long *capacity,*blksz; + unsigned long *capacity, *blksz; ccb *pccb = &usb_ccb; /* for some reasons a couple of devices would not survive this reset */ @@ -1157,7 +1225,6 @@ int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t /* Sony USM256E */ (dev->descriptor.idVendor == 0x054c && dev->descriptor.idProduct == 0x019e) - || /* USB007 Mini-USB2 Flash Drive */ (dev->descriptor.idVendor == 0x066f && @@ -1166,6 +1233,13 @@ int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t /* SanDisk Corporation Cruzer Micro 20044318410546613953 */ (dev->descriptor.idVendor == 0x0781 && dev->descriptor.idProduct == 0x5151) + || + /* + * SanDisk Corporation U3 Cruzer Micro 1/4GB + * Flash Drive 000016244373FFB4 + */ + (dev->descriptor.idVendor == 0x0781 && + dev->descriptor.idProduct == 0x5406) ) USB_STOR_PRINTF("usb_stor_get_info: skipping RESET..\n"); else @@ -1175,17 +1249,20 @@ int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t dev_desc->target = dev->devnum; pccb->lun = dev_desc->lun; - USB_STOR_PRINTF(" address %d\n",dev_desc->target); + USB_STOR_PRINTF(" address %d\n", dev_desc->target); - if(usb_inquiry(pccb,ss)) + if (usb_inquiry(pccb, ss)) return -1; perq = usb_stor_buf[0]; modi = usb_stor_buf[1]; - if((perq & 0x1f) == 0x1f) { - return 0; /* skip unknown devices */ + + if ((perq & 0x1f) == 0x1f) { + /* skip unknown devices */ + return 0; } - if((modi&0x80) == 0x80) {/* drive is removable */ + if ((modi&0x80) == 0x80) { + /* drive is removable */ dev_desc->removable = 1; } memcpy(&dev_desc->vendor[0], &usb_stor_buf[8], 8); @@ -1195,29 +1272,34 @@ int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t dev_desc->product[16] = 0; dev_desc->revision[4] = 0; #ifdef CONFIG_USB_BIN_FIXUP - usb_bin_fixup(dev->descriptor, (uchar *)dev_desc->vendor, (uchar *)dev_desc->product); + usb_bin_fixup(dev->descriptor, (uchar *)dev_desc->vendor, + (uchar *)dev_desc->product); #endif /* CONFIG_USB_BIN_FIXUP */ - USB_STOR_PRINTF("ISO Vers %X, Response Data %X\n",usb_stor_buf[2],usb_stor_buf[3]); - if(usb_test_unit_ready(pccb,ss)) { - printf("Device NOT ready\n Request Sense returned %02X %02X %02X\n",pccb->sense_buf[2],pccb->sense_buf[12],pccb->sense_buf[13]); - if(dev_desc->removable == 1) { + USB_STOR_PRINTF("ISO Vers %X, Response Data %X\n", usb_stor_buf[2], + usb_stor_buf[3]); + if (usb_test_unit_ready(pccb, ss)) { + printf("Device NOT ready\n" + " Request Sense returned %02X %02X %02X\n", + pccb->sense_buf[2], pccb->sense_buf[12], + pccb->sense_buf[13]); + if (dev_desc->removable == 1) { dev_desc->type = perq; return 1; } - else - return 0; + return 0; } pccb->pdata = (unsigned char *)&cap[0]; - memset(pccb->pdata,0,8); - if(usb_read_capacity(pccb,ss) != 0) { + memset(pccb->pdata, 0, 8); + if (usb_read_capacity(pccb, ss) != 0) { printf("READ_CAP ERROR\n"); cap[0] = 2880; cap[1] = 0x200; } - USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\n",cap[0],cap[1]); + USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\n", cap[0], + cap[1]); #if 0 - if(cap[0]>(0x200000 * 10)) /* greater than 10 GByte */ - cap[0]>>=16; + if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */ + cap[0] >>= 16; #endif cap[0] = cpu_to_be32(cap[0]); cap[1] = cpu_to_be32(cap[1]); @@ -1226,15 +1308,16 @@ int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t cap[0] += 1; capacity = &cap[0]; blksz = &cap[1]; - USB_STOR_PRINTF("Capacity = 0x%lx, blocksz = 0x%lx\n",*capacity,*blksz); + USB_STOR_PRINTF("Capacity = 0x%lx, blocksz = 0x%lx\n", + *capacity, *blksz); dev_desc->lba = *capacity; dev_desc->blksz = *blksz; dev_desc->type = perq; - USB_STOR_PRINTF(" address %d\n",dev_desc->target); - USB_STOR_PRINTF("partype: %d\n",dev_desc->part_type); + USB_STOR_PRINTF(" address %d\n", dev_desc->target); + USB_STOR_PRINTF("partype: %d\n", dev_desc->part_type); init_part(dev_desc); - USB_STOR_PRINTF("partype: %d\n",dev_desc->part_type); + USB_STOR_PRINTF("partype: %d\n", dev_desc->part_type); return 1; } diff --git a/cpu/arm920t/s3c24x0/usb_ohci.c b/cpu/arm920t/s3c24x0/usb_ohci.c index 641f270..9cbd477 100644 --- a/cpu/arm920t/s3c24x0/usb_ohci.c +++ b/cpu/arm920t/s3c24x0/usb_ohci.c @@ -29,9 +29,7 @@ */ /* * IMPORTANT NOTES - * 1 - you MUST define LITTLEENDIAN in the configuration file for the - * board or this driver will NOT work! - * 2 - this driver is intended for use with USB Mass Storage Devices + * 1 - this driver is intended for use with USB Mass Storage Devices * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! */ diff --git a/cpu/mips/au1x00_usb_ohci.c b/cpu/mips/au1x00_usb_ohci.c index 17489da..ea02efb 100644 --- a/cpu/mips/au1x00_usb_ohci.c +++ b/cpu/mips/au1x00_usb_ohci.c @@ -27,9 +27,7 @@ */ /* * IMPORTANT NOTES - * 1 - you MUST define LITTLEENDIAN in the configuration file for the - * board or this driver will NOT work! - * 2 - this driver is intended for use with USB Mass Storage Devices + * 1 - this driver is intended for use with USB Mass Storage Devices * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! */ @@ -56,7 +54,7 @@ #define USBH_ENABLE_CE (1<<3) #define USBH_ENABLE_RD (1<<4) -#ifdef LITTLEENDIAN +#ifdef __LITTLE_ENDIAN #define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C) #else #define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE) diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 856f51a..b306a65 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -28,11 +28,18 @@ LIB := $(obj)libusb.a # core COBJS-y += usbdcore.o COBJS-$(CONFIG_USB_OHCI_NEW) += usb_ohci.o +COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o # host COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o +COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o +COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o +COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o +COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o +COBJS-$(CONFIG_USB_DAVINCI) += davinci_usb.o +COBJS-$(CONFIG_USB_EHCI_VCT) += usb_ehci_vct.o # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/davinci_usb.c b/drivers/usb/davinci_usb.c new file mode 100644 index 0000000..e66f660 --- /dev/null +++ b/drivers/usb/davinci_usb.c @@ -0,0 +1,106 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> +#include <asm/io.h> +#include "davinci_usb.h" + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + (struct musb_regs *)MENTOR_USB0_BASE, + DAVINCI_USB_TIMEOUT, + 0 +}; + +/* MUSB module register overlay */ +struct davinci_usb_regs *dregs; + +/* + * Enable the USB phy + */ +static u8 phy_on(void) +{ + u32 timeout; + + /* Wait until the USB phy is turned on */ + writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); + timeout = musb_cfg.timeout; + while (timeout--) + if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD) + return 1; + + /* USB phy was not turned on */ + return 0; +} + +/* + * Disable the USB phy + */ +static void phy_off(void) +{ + /* powerdown the on-chip PHY and its oscillator */ + writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR); +} + +/* + * This function performs Davinci platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + + /* enable USB VBUS */ + enable_vbus(); + + /* start the on-chip USB phy and its pll */ + if (!phy_on()) + return -1; + + /* reset the controller */ + dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE; + writel(1, &dregs->ctrlr); + udelay(5000); + + /* Returns zero if e.g. not clocked */ + revision = readl(&dregs->version); + if (!revision) + return -1; + + /* Disable all interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK | + DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr); + return 0; +} + +/* + * This function performs Davinci platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn of the phy */ + phy_off(); + + /* flush any interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK | + DAVINCI_USB_RXINT_MASK , &dregs->intclrr); +} diff --git a/drivers/usb/davinci_usb.h b/drivers/usb/davinci_usb.h new file mode 100644 index 0000000..d270861 --- /dev/null +++ b/drivers/usb/davinci_usb.h @@ -0,0 +1,87 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __DAVINCI_USB_H__ +#define __DAVINCI_USB_H__ + +#include <asm/arch/hardware.h> +#include "musb_core.h" + +/* Base address of DAVINCI usb0 wrapper */ +#define DAVINCI_USB0_BASE 0x01C64000 + +/* Base address of DAVINCI musb core */ +#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400) + +/* + * Davinci platform USB wrapper register overlay. Note: Only the required + * registers are included in this structure. It can be expanded as required. + */ +struct davinci_usb_regs { + u32 version; + u32 ctrlr; + u32 reserved[0x20]; + u32 intclrr; + u32 intmskr; + u32 intmsksetr; +}; + +#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ +#define DAVINCI_USB_USBINT_SHIFT 16 +#define DAVINCI_USB_TXINT_SHIFT 0 +#define DAVINCI_USB_RXINT_SHIFT 8 +#define DAVINCI_INTR_DRVVBUS 0x0100 + +#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DAVINCI_USB_TXINT_MASK \ + (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) +#define DAVINCI_USB_RXINT_MASK \ + (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +/* Integrated highspeed/otg PHY */ +#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) +#define USBPHY_PHYCLKGD (1 << 8) +#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ +#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ +#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ +#define USBPHY_CLKO1SEL (1 << 3) +#define USBPHY_OSCPDWN (1 << 2) +#define USBPHY_PHYPDWN (1 << 0) + +/* Timeout for Davinci USB module */ +#define DAVINCI_USB_TIMEOUT 0x3FFFFFF + +/* IO Expander I2C address and VBUS enable mask */ +#define IOEXP_I2C_ADDR 0x3A +#define IOEXP_VBUSEN_MASK 1 + +/* extern functions */ +extern void lpsc_on(unsigned int id); +extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern void enable_vbus(void); +#endif /* __DAVINCI_USB_H__ */ + diff --git a/drivers/usb/musb_core.c b/drivers/usb/musb_core.c new file mode 100644 index 0000000..ec57fc8 --- /dev/null +++ b/drivers/usb/musb_core.c @@ -0,0 +1,141 @@ +/* + * Mentor USB OTG Core functionality common for both Host and Device + * functionality. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> + +#include "musb_core.h" +struct musb_regs *musbr; + +/* + * program the mentor core to start (enable interrupts, dma, etc.) + */ +void musb_start(void) +{ + u8 devctl; + + /* disable all interrupts */ + writew(0, &musbr->intrtxe); + writew(0, &musbr->intrrxe); + writeb(0, &musbr->intrusbe); + writeb(0, &musbr->testmode); + + /* put into basic highspeed mode and start session */ + writeb(MUSB_POWER_HSENAB, &musbr->power); +#if defined(CONFIG_MUSB_HCD) + devctl = readb(&musbr->devctl); + writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl); +#endif +} + +/* + * This function configures the endpoint configuration. The musb hcd or musb + * device implementation can use this function to configure the endpoints + * and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints + * should not be more than the available FIFO size. + * + * epinfo - Pointer to EP configuration table + * cnt - Number of entries in the EP conf table. + */ +void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) +{ + u16 csr; + u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */ + u32 fifosize; + u8 idx; + + while (cnt--) { + /* prepare fifosize to write to register */ + fifosize = epinfo->epsize >> 3; + idx = ffs(fifosize) - 1; + + writeb(epinfo->epnum, &musbr->index); + if (epinfo->epdir) { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->txfifosz); + writew(fifoaddr >> 3, &musbr->txfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_TXCSR_TXPKTRDY) + writew(csr | MUSB_TXCSR_FLUSHFIFO, + &musbr->txcsr); + } else { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->rxfifosz); + writew(fifoaddr >> 3, &musbr->rxfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->rxcsr); + writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_RXCSR_RXPKTRDY) + writew(csr | MUSB_RXCSR_FLUSHFIFO, + &musbr->rxcsr); + } + fifoaddr += epinfo->epsize; + epinfo++; + } +} + +/* + * This function writes data to endpoint fifo + * + * ep - endpoint number + * length - number of bytes to write to FIFO + * fifo_data - Pointer to data buffer that contains the data to write + */ +void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* write the data to the fifo */ + while (length--) + writeb(*data++, &musbr->fifox[ep]); +} + +/* + * This function reads data from endpoint fifo + * + * ep - endpoint number + * length - number of bytes to read from FIFO + * fifo_data - pointer to data buffer into which data is read + */ +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* read the data to the fifo */ + while (length--) + *data++ = readb(&musbr->fifox[ep]); +} diff --git a/drivers/usb/musb_core.h b/drivers/usb/musb_core.h new file mode 100644 index 0000000..9f5ebe7 --- /dev/null +++ b/drivers/usb/musb_core.h @@ -0,0 +1,317 @@ +/****************************************************************** + * Copyright 2008 Mentor Graphics Corporation + * Copyright (C) 2008 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux is distributed in + * the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION + * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE + * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS + * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. + * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT + * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR + * GRAPHICS SUPPORT CUSTOMER. + ******************************************************************/ + +#ifndef __MUSB_HDRC_DEFS_H__ +#define __MUSB_HDRC_DEFS_H__ + +#include <usb.h> +#include <usb_defs.h> +#include <asm/io.h> + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* Mentor USB core register overlay structure */ +struct musb_regs { + /* common registers */ + u8 faddr; + u8 power; + u16 intrtx; + u16 intrrx; + u16 intrtxe; + u16 intrrxe; + u8 intrusb; + u8 intrusbe; + u16 frame; + u8 index; + u8 testmode; + /* indexed registers */ + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; + /* fifo */ + u32 fifox[16]; + /* OTG, dynamic FIFO, version & vendor registers */ + u8 devctl; + u8 reserved1; + u8 txfifosz; + u8 rxfifosz; + u16 txfifoadd; + u16 rxfifoadd; + u32 vcontrol; + u16 hwvers; + u16 reserved2[5]; + u8 epinfo; + u8 raminfo; + u8 linkinfo; + u8 vplen; + u8 hseof1; + u8 fseof1; + u8 lseof1; + u8 reserved3; + /* target address registers */ + struct musb_tar_regs { + u8 txfuncaddr; + u8 reserved0; + u8 txhubaddr; + u8 txhubport; + u8 rxfuncaddr; + u8 reserved1; + u8 rxhubaddr; + u8 rxhubport; + } tar[16]; +} __attribute((aligned(32))); + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 +#define MUSB_POWER_HSMODE_SHIFT 4 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_SPEED_HIGH 1 +#define MUSB_TYPE_SPEED_FULL 2 +#define MUSB_TYPE_SPEED_LOW 3 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_BULK 2 +#define MUSB_TYPE_PROTO_INTR 3 + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_MODE 0x2000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 +#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 +#define MUSB_S_RXCSR_H_DATATOGGLE 9 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + +/* Endpoint configuration information. Note: The value of endpoint fifo size + * element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other + * values are not supported + */ +struct musb_epinfo { + u8 epnum; /* endpoint number */ + u8 epdir; /* endpoint direction */ + u16 epsize; /* endpoint FIFO size */ +}; + +/* + * Platform specific MUSB configuration. Any platform using the musb + * functionality should create one instance of this structure in the + * platform specific file. + */ +struct musb_config { + struct musb_regs *regs; + u32 timeout; + u8 musb_speed; +}; + +/* externally defined data */ +extern struct musb_config musb_cfg; +extern struct musb_regs *musbr; + +/* exported functions */ +extern void musb_start(void); +extern void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt); +extern void write_fifo(u8 ep, u32 length, void *fifo_data); +extern void read_fifo(u8 ep, u32 length, void *fifo_data); + +/* extern functions */ +extern inline void musb_writew(u32 offset, u16 value); +extern inline void musb_writeb(u32 offset, u8 value); +extern inline u16 musb_readw(u32 offset); +extern inline u8 musb_readb(u32 offset); + +#endif /* __MUSB_HDRC_DEFS_H__ */ + diff --git a/drivers/usb/musb_hcd.c b/drivers/usb/musb_hcd.c new file mode 100644 index 0000000..352a0d4 --- /dev/null +++ b/drivers/usb/musb_hcd.c @@ -0,0 +1,792 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> +#include "musb_hcd.h" + +/* MSC control transfers */ +#define USB_MSC_BBB_RESET 0xFF +#define USB_MSC_BBB_GET_MAX_LUN 0xFE + +/* Endpoint configuration information */ +static struct musb_epinfo epinfo[3] = { + {MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ + {MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In - 512 Bytes */ + {MUSB_INTR_EP, 0, 64} /* EP2 - Interrupt IN - 64 Bytes */ +}; + +/* + * This function writes the data toggle value. + */ +static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) +{ + u16 toggle = usb_gettoggle(dev, ep, dir_out); + u16 csr; + + if (dir_out) { + if (!toggle) + writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); + else { + csr = readw(&musbr->txcsr); + csr |= MUSB_TXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->txcsr); + csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); + writew(csr, &musbr->txcsr); + } + } else { + if (!toggle) + writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); + else { + csr = readw(&musbr->rxcsr); + csr |= MUSB_RXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->rxcsr); + csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); + writew(csr, &musbr->rxcsr); + } + } +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall + * has occured, the RxStall is cleared and 1 is returned. If RxStall has + * not occured, 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ + u16 csr; + + /* For endpoint 0 */ + if (!ep) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_RXSTALL) { + csr &= ~MUSB_CSR0_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* For non-ep0 */ + if (dir_out) { /* is it tx ep */ + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_RXSTALL) { + csr &= ~MUSB_TXCSR_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* is it rx ep */ + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_RXSTALL) { + csr &= ~MUSB_RXCSR_H_RXSTALL; + writew(csr, &musbr->rxcsr); + return 1; + } + } + } + return 0; +} + +/* + * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout + * error and -2 for stall. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u16 csr; + int result = 1; + + while (result > 0) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_ERROR) { + csr &= ~MUSB_CSR0_H_ERROR; + writew(csr, &musbr->txcsr); + dev->status = USB_ST_CRC_ERR; + result = -1; + break; + } + + switch (bit_mask) { + case MUSB_CSR0_TXPKTRDY: + if (!(csr & MUSB_CSR0_TXPKTRDY)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + + case MUSB_CSR0_RXPKTRDY: + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + if (csr & MUSB_CSR0_RXPKTRDY) + result = 0; + break; + + case MUSB_CSR0_H_REQPKT: + if (!(csr & MUSB_CSR0_H_REQPKT)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + } + } + return result; +} + +/* + * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_txep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 1)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (csr & MUSB_TXCSR_TXPKTRDY); + return 1; +} + +/* + * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_rxep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 0)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (!(csr & MUSB_RXCSR_RXPKTRDY)); + return 1; +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ + int result; + u16 csr; + + /* write the control request to ep0 fifo */ + write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); + + /* enable transfer of setup packet */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); + writew(csr, &musbr->txcsr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + dev->act_len = 0; + return result; +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 rxlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *rxbuff = (u8 *)buffer; + u8 rxedlength; + int result; + + while (rxlen < len) { + /* Determine the next read length */ + nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); + + /* Set the ReqPkt bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); + if (result < 0) + return result; + + /* Actual number of bytes received by usb */ + rxedlength = readb(&musbr->rxcount); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = readw(&musbr->txcsr); + csr &= ~MUSB_CSR0_RXPKTRDY; + writew(csr, &musbr->txcsr); + + /* short packet? */ + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + rxlen += nextlen; + dev->act_len = rxlen; + } + return 0; +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *txbuff = (u8 *)buffer; + int result = 0; + + while (txlen < len) { + /* Determine the next write length */ + nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); + + /* Load the data to send in FIFO */ + write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + + /* Set TXPKTRDY bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, + &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + if (result < 0) + break; + + txlen += nextlen; + dev->act_len = txlen; + } + return result; +} + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | + MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + return result; +} + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit and ReqPkt bit */ + csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; + writew(csr, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = readw(&musbr->txcsr); + csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + return result; +} + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + return (dev->speed & USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : + ((dev->speed & USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : + MUSB_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while (dev->parent->parent != NULL) + if (get_dev_speed(dev->parent) != MUSB_TYPE_SPEED_HIGH) + dev = dev->parent; + else + break; + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + + /* configure the hub address and the port address */ + writeb(hub, &musbr->tar[ep].txhubaddr); + writeb((chid + 1), &musbr->tar[ep].txhubport); + writeb(hub, &musbr->tar[ep].rxhubaddr); + writeb((chid + 1), &musbr->tar[ep].rxhubport); +} + +/* + * do a control transfer + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + u16 csr; + u8 devspeed; + + /* select control endpoint */ + writeb(MUSB_CONTROL_EP, &musbr->index); + csr = readw(&musbr->txcsr); + + /* target addr and (for multipoint) hub addr/port */ + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + config_hub_port(dev, MUSB_CONTROL_EP); + writeb(devspeed << 6, &musbr->txtype); + } else { + writeb(musb_cfg.musb_speed << 6, &musbr->txtype); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return 0; + + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_MSC_BBB_GET_MAX_LUN: + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_CLEAR_FEATURE: + case USB_MSC_BBB_RESET: + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_DESCRIPTOR: + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + default: + /* unhandled control transfer */ + return -1; + } + + dev->status = 0; + dev->act_len = len; + return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select bulk endpoint */ + writeb(MUSB_BULK_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_BULK_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (dir_out) { /* bulk-out transfer */ + /* Program the TxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->txtype); + + /* Write maximum packet size to the TxMaxp register */ + writew(dev->epmaxpacketout[ep], &musbr->txmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? + (len-txlen) : dev->epmaxpacketout[ep]; + + /* Write the data to the FIFO */ + write_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Set the TxPktRdy bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); + + /* Wait until the TxPktRdy bit is cleared */ + if (!wait_until_txep_ready(dev, MUSB_BULK_EP)) { + readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + dev->act_len = txlen; + return 0; + } + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + } else { /* bulk-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_BULK_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* bulk transfer is complete */ + dev->status = 0; + dev->act_len = len; + return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(void) +{ + u8 power; + u32 timeout; + + if (musb_platform_init() == -1) + return -1; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + musb_configure_ep(&epinfo[0], + sizeof(epinfo) / sizeof(struct musb_epinfo)); + musb_start(); + + /* + * Wait until musb is enabled in host mode with a timeout. There + * should be a usb device connected. + */ + timeout = musb_cfg.timeout; + while (timeout--) + if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) + break; + + /* if musb core is not in host mode, then return */ + if (!timeout) + return -1; + + /* start usb bus reset */ + power = readb(&musbr->power); + writeb(power | MUSB_POWER_RESET, &musbr->power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_RESET; + writeb(power, &musbr->power); + + /* Determine if the connected device is a high/full/low speed device */ + musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? + MUSB_TYPE_SPEED_HIGH : + ((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? + MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); + return 0; +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ + /* Reset the USB module */ + musb_platform_deinit(); + writeb(0, &musbr->devctl); + return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt + * transfers are not supported. + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select interrupt endpoint */ + writeb(MUSB_INTR_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_INTR_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (!dir_out) { /* intrrupt-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + writeb(interval, &musbr->rxinterval); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_INTR_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_INTR_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* interrupt transfer is complete */ + dev->irq_status = 0; + dev->irq_act_len = len; + dev->irq_handle(dev); + dev->status = 0; + dev->act_len = len; + return 0; +} + + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ + device_t *dev; + struct usb_device *usb_kbd_dev; + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = device_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], + maxp > 8 ? 8 : maxp, ep->bInterval); +} +#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/musb_hcd.h b/drivers/usb/musb_hcd.h new file mode 100644 index 0000000..bb83311 --- /dev/null +++ b/drivers/usb/musb_hcd.h @@ -0,0 +1,51 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __MUSB_HCD_H__ +#define __MUSB_HCD_H__ + +#include "musb_core.h" +#ifdef CONFIG_USB_KEYBOARD +#include <devices.h> +extern unsigned char new[]; +#endif + +/* This defines the endpoint number used for control transfers */ +#define MUSB_CONTROL_EP 0 + +/* This defines the endpoint number used for bulk transfer */ +#define MUSB_BULK_EP 1 + +/* This defines the endpoint number used for interrupt transfer */ +#define MUSB_INTR_EP 2 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ + >> MUSB_POWER_HSMODE_SHIFT) + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); + +#endif /* __MUSB_HCD_H__ */ diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h new file mode 100644 index 0000000..b3c1d5d --- /dev/null +++ b/drivers/usb/usb_ehci.h @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 +#endif + +/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ +#define DeviceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) + +#define DeviceOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) + +#define InterfaceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +/* + * Register Space. + */ +struct ehci_hccr { + uint32_t cr_capbase; +#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) +#define HC_VERSION(p) (((p) >> 16) & 0xffff) + uint32_t cr_hcsparams; +#define HCS_PPC(p) ((p) & (1 << 4)) +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) + uint32_t cr_hccparams; + uint8_t cr_hcsp_portrt[8]; +} __attribute__ ((packed)); + +struct ehci_hcor { + uint32_t or_usbcmd; +#define CMD_PARK (1 << 11) /* enable "park" */ +#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ +#define CMD_ASE (1 << 5) /* async schedule enable */ +#define CMD_LRESET (1 << 7) /* partial reset */ +#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_PSE (1 << 4) /* periodic schedule enable */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ + uint32_t or_usbsts; +#define STD_ASS (1 << 15) +#define STS_HALT (1 << 12) + uint32_t or_usbintr; + uint32_t or_frindex; + uint32_t or_ctrldssegment; + uint32_t or_periodiclistbase; + uint32_t or_asynclistaddr; + uint32_t _reserved_[9]; + uint32_t or_configflag; +#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ + uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; + uint32_t or_systune; +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1 << 3) /* Stream disable */ +#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ +#define USBMODE_CM_HC (3 << 0) /* host controller mode */ +#define USBMODE_CM_IDLE (0 << 0) /* idle state */ + +/* Interface descriptor */ +struct usb_linux_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)); + +/* Configuration descriptor information.. */ +struct usb_linux_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; +} __attribute__ ((packed)); + +#if defined CONFIG_EHCI_DESC_BIG_ENDIAN +#define ehci_readl(x) (*((volatile u32 *)(x))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) +#else +#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ + cpu_to_le32(((volatile u32)b))) +#endif + +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#define hc32_to_cpu(x) be32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_be32((x)) +#else +#define hc32_to_cpu(x) le32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_le32((x)) +#endif + +#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ +#define EHCI_PS_PO (1 << 13) /* RW port owner */ +#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ +#define EHCI_PS_LS (3 << 10) /* RO line status */ +#define EHCI_PS_PR (1 << 8) /* RW port reset */ +#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ +#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ +#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ +#define EHCI_PS_OCA (1 << 4) /* RO over current active */ +#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ +#define EHCI_PS_PE (1 << 2) /* RW port enable */ +#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ +#define EHCI_PS_CS (1 << 0) /* RO connect status */ +#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) + +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) + +/* + * Schedule Interface Space. + * + * IMPORTANT: Software must ensure that no interface data structure + * reachable by the EHCI host controller spans a 4K page boundary! + * + * Periodic transfers (i.e. isochronous and interrupt transfers) are + * not supported. + */ + +/* Queue Element Transfer Descriptor (qTD). */ +struct qTD { + uint32_t qt_next; +#define QT_NEXT_TERMINATE 1 + uint32_t qt_altnext; + uint32_t qt_token; + uint32_t qt_buffer[5]; +}; + +/* Queue Head (QH). */ +struct QH { + uint32_t qh_link; +#define QH_LINK_TERMINATE 1 +#define QH_LINK_TYPE_ITD 0 +#define QH_LINK_TYPE_QH 2 +#define QH_LINK_TYPE_SITD 4 +#define QH_LINK_TYPE_FSTN 6 + uint32_t qh_endpt1; + uint32_t qh_endpt2; + uint32_t qh_curtd; + struct qTD qh_overlay; + /* + * Add dummy fill value to make the size of this struct + * aligned to 32 bytes + */ + uint8_t fill[16]; +}; + +/* Low level init functions */ +int ehci_hcd_init(void); +int ehci_hcd_stop(void); + +#endif /* USB_EHCI_H */ diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c new file mode 100644 index 0000000..813f64a --- /dev/null +++ b/drivers/usb/usb_ehci_core.c @@ -0,0 +1,880 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <asm/byteorder.h> +#include <usb.h> +#include <asm/io.h> +#include <malloc.h> +#include "usb_ehci.h" + +int rootdev; +struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ +volatile struct ehci_hcor *hcor; + +static uint16_t portreset; +static struct QH qh_list __attribute__((aligned(32))); + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_linux_config_descriptor config; + struct usb_linux_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)) descriptor = { + { + 0x8, /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, /* wHubCharacteristics */ + 0xff, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + {}, /* Device removable */ + {} /* at most 7 ports! XXX */ + }, + { + 0x12, /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + 0x0002, /* bcdUSB: v2.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ + 64, /* bMaxPacketSize: 64 bytes */ + 0x0000, /* idVendor */ + 0x0000, /* idProduct */ + 0x0001, /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + cpu_to_le16(0x19), + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: + * UE_DIR_IN | EHCI_INTR_ENDPT + */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, 0, /* wMaxPacketSize */ + 255 /* bInterval */ + }, +}; + +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + +#if defined(CONFIG_EHCI_DCACHE) +/* + * Routines to handle (flush/invalidate) the dcache for the QH and qTD + * structures and data buffers. This is needed on platforms using this + * EHCI support with dcache enabled. + */ +static void flush_invalidate(u32 addr, int size, int flush) +{ + if (flush) + flush_dcache_range(addr, addr + size); + else + invalidate_dcache_range(addr, addr + size); +} + +static void cache_qtd(struct qTD *qtd, int flush) +{ + u32 *ptr = (u32 *)qtd->qt_buffer[0]; + int len = (qtd->qt_token & 0x7fff0000) >> 16; + + flush_invalidate((u32)qtd, sizeof(struct qTD), flush); + if (ptr && len) + flush_invalidate((u32)ptr, len, flush); +} + + +static inline struct QH *qh_addr(struct QH *qh) +{ + return (struct QH *)((u32)qh & 0xffffffe0); +} + +static void cache_qh(struct QH *qh, int flush) +{ + struct qTD *qtd; + struct qTD *next; + static struct qTD *first_qtd; + + /* + * Walk the QH list and flush/invalidate all entries + */ + while (1) { + flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush); + if ((u32)qh & QH_LINK_TYPE_QH) + break; + qh = qh_addr(qh); + qh = (struct QH *)qh->qh_link; + } + qh = qh_addr(qh); + + /* + * Save first qTD pointer, needed for invalidating pass on this QH + */ + if (flush) + first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay & + 0xffffffe0); + else + qtd = first_qtd; + + /* + * Walk the qTD list and flush/invalidate all entries + */ + while (1) { + if (qtd == NULL) + break; + cache_qtd(qtd, flush); + next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0); + if (next == qtd) + break; + qtd = next; + } +} + +static inline void ehci_flush_dcache(struct QH *qh) +{ + cache_qh(qh, 1); +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ + cache_qh(qh, 0); +} +#else /* CONFIG_EHCI_DCACHE */ +/* + * + */ +static inline void ehci_flush_dcache(struct QH *qh) +{ +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ +} +#endif /* CONFIG_EHCI_DCACHE */ + +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) +{ + uint32_t result; + do { + result = ehci_readl(ptr); + if (result == ~(uint32_t)0) + return -1; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -1; +} + +static void ehci_free(void *p, size_t sz) +{ + +} + +static int ehci_reset(void) +{ + uint32_t cmd; + uint32_t tmp; + uint32_t *reg_ptr; + int ret = 0; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_RESET; + ehci_writel(&hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); + if (ret < 0) { + printf("EHCI fail to reset\n"); + goto out; + } + + if (ehci_is_TDI()) { + reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); + } +out: + return ret; +} + +static void *ehci_alloc(size_t sz, size_t align) +{ + static struct QH qh __attribute__((aligned(32))); + static struct qTD td[3] __attribute__((aligned (32))); + static int ntds; + void *p; + + switch (sz) { + case sizeof(struct QH): + p = &qh; + ntds = 0; + break; + case sizeof(struct qTD): + if (ntds == 3) { + debug("out of TDs\n"); + return NULL; + } + p = &td[ntds]; + ntds++; + break; + default: + debug("unknown allocation size\n"); + return NULL; + } + + memset(p, sz, 0); + return p; +} + +static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) +{ + uint32_t addr, delta, next; + int idx; + + addr = (uint32_t) buf; + idx = 0; + while (idx < 5) { + td->qt_buffer[idx] = cpu_to_hc32(addr); + next = (addr + 4096) & ~4095; + delta = next - addr; + if (delta >= sz) + break; + sz -= delta; + addr = next; + idx++; + } + + if (idx == 5) { + debug("out of buffer pointers (%u bytes left)\n", sz); + return -1; + } + + return 0; +} + +static int +ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + struct QH *qh; + struct qTD *td; + volatile struct qTD *vtd; + unsigned long ts; + uint32_t *tdp; + uint32_t endpt, token, usbsts; + uint32_t c, toggle; + uint32_t cmd; + int ret = 0; + + debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, + buffer, length, req); + if (req != NULL) + debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index)); + + qh = ehci_alloc(sizeof(struct QH), 32); + if (qh == NULL) { + debug("unable to allocate QH\n"); + return -1; + } + qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && + usb_pipeendpoint(pipe) == 0) ? 1 : 0; + endpt = (8 << 28) | + (c << 27) | + (usb_maxpacket(dev, pipe) << 16) | + (0 << 15) | + (1 << 14) | + (usb_pipespeed(pipe) << 12) | + (usb_pipeendpoint(pipe) << 8) | + (0 << 7) | (usb_pipedevice(pipe) << 0); + qh->qh_endpt1 = cpu_to_hc32(endpt); + endpt = (1 << 30) | + (dev->portnr << 23) | + (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); + qh->qh_endpt2 = cpu_to_hc32(endpt); + qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + + td = NULL; + tdp = &qh->qh_overlay.qt_next; + + toggle = + usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + + if (req != NULL) { + td = ehci_alloc(sizeof(struct qTD), 32); + if (td == NULL) { + debug("unable to allocate SETUP td\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (0 << 31) | + (sizeof(*req) << 16) | + (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { + debug("unable construct SETUP td\n"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_hc32((uint32_t) td); + tdp = &td->qt_next; + toggle = 1; + } + + if (length > 0 || req == NULL) { + td = ehci_alloc(sizeof(struct qTD), 32); + if (td == NULL) { + debug("unable to allocate DATA td\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (toggle << 31) | + (length << 16) | + ((req == NULL ? 1 : 0) << 15) | + (0 << 12) | + (3 << 10) | + ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(td, buffer, length) != 0) { + debug("unable construct DATA td\n"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_hc32((uint32_t) td); + tdp = &td->qt_next; + } + + if (req != NULL) { + td = ehci_alloc(sizeof(struct qTD), 32); + if (td == NULL) { + debug("unable to allocate ACK td\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (toggle << 31) | + (0 << 16) | + (1 << 15) | + (0 << 12) | + (3 << 10) | + ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + *tdp = cpu_to_hc32((uint32_t) td); + tdp = &td->qt_next; + } + + qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); + + /* Flush dcache */ + ehci_flush_dcache(&qh_list); + + usbsts = ehci_readl(&hcor->or_usbsts); + ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); + + /* Enable async. schedule. */ + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STD_ASS set\n"); + goto fail; + } + + /* Wait for TDs to be processed. */ + ts = get_timer(0); + vtd = td; + do { + /* Invalidate dcache */ + ehci_invalidate_dcache(&qh_list); + token = hc32_to_cpu(vtd->qt_token); + if (!(token & 0x80)) + break; + } while (get_timer(ts) < CONFIG_SYS_HZ); + + /* Disable async schedule. */ + cmd = ehci_readl(&hcor->or_usbcmd); + cmd &= ~CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STD_ASS reset\n"); + goto fail; + } + + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + + token = hc32_to_cpu(qh->qh_overlay.qt_token); + if (!(token & 0x80)) { + debug("TOKEN=%#x\n", token); + switch (token & 0xfc) { + case 0: + toggle = token >> 31; + usb_settoggle(dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), toggle); + dev->status = 0; + break; + case 0x40: + dev->status = USB_ST_STALLED; + break; + case 0xa0: + case 0x20: + dev->status = USB_ST_BUF_ERR; + break; + case 0x50: + case 0x10: + dev->status = USB_ST_BABBLE_DET; + break; + default: + dev->status = USB_ST_CRC_ERR; + break; + } + dev->act_len = length - ((token >> 16) & 0x7fff); + } else { + dev->act_len = 0; + debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", + dev->devnum, ehci_readl(&hcor->or_usbsts), + ehci_readl(&hcor->or_portsc[0]), + ehci_readl(&hcor->or_portsc[1])); + } + + return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; + +fail: + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); + while (td != (void *)QT_NEXT_TERMINATE) { + qh->qh_overlay.qt_next = td->qt_next; + ehci_free(td, sizeof(*td)); + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); + } + ehci_free(qh, sizeof(*qh)); + return -1; +} + +static inline int min3(int a, int b, int c) +{ + + if (b < a) + a = b; + if (c < a) + a = c; + return a; +} + +int +ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + u16 typeReq; + void *srcptr = NULL; + int len, srclen; + uint32_t reg; + uint32_t *status_reg; + + if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + printf("The request port(%d) is not configured\n", + le16_to_cpu(req->index) - 1); + return -1; + } + status_reg = (uint32_t *)&hcor->or_portsc[ + le16_to_cpu(req->index) - 1]; + srclen = 0; + + debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->index)); + + typeReq = req->request << 8 | req->requesttype; + + switch (le16_to_cpu(typeReq)) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + debug("USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = 0x12; + break; + case USB_DT_CONFIG: + debug("USB_DT_CONFIG config\n"); + srcptr = &descriptor.config; + srclen = 0x19; + break; + case USB_DT_STRING: + debug("USB_DT_STRING config\n"); + switch (le16_to_cpu(req->value) & 0xff) { + case 0: /* Language */ + srcptr = "\4\3\1\0"; + srclen = 4; + break; + case 1: /* Vendor */ + srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product */ + srcptr = "\52\3E\0H\0C\0I\0 " + "\0H\0o\0s\0t\0 " + "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + break; + default: + debug("unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); + goto unknown; + } + break; + default: + debug("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_HUB: + debug("USB_DT_HUB config\n"); + srcptr = &descriptor.hub; + srclen = 0x8; + break; + default: + debug("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + debug("USB_REQ_SET_ADDRESS\n"); + rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + debug("USB_REQ_SET_CONFIGURATION\n"); + /* Nothing to do */ + break; + case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + memset(tmpbuf, 0, 4); + reg = ehci_readl(status_reg); + if (reg & EHCI_PS_CS) + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + if (reg & EHCI_PS_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if (reg & EHCI_PS_SUSP) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if (reg & EHCI_PS_OCA) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & EHCI_PS_PR && + (portreset & (1 << le16_to_cpu(req->index)))) { + int ret; + /* force reset to complete */ + reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); + ehci_writel(status_reg, reg); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); + if (!ret) + tmpbuf[0] |= USB_PORT_STAT_RESET; + else + printf("port(%d) reset error\n", + le16_to_cpu(req->index) - 1); + } + if (reg & EHCI_PS_PP) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + + if (ehci_is_TDI()) { + switch ((reg >> 26) & 3) { + case 0: + break; + case 1: + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + break; + case 2: + default: + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + break; + } + } else { + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + } + + if (reg & EHCI_PS_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & EHCI_PS_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & EHCI_PS_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (portreset & (1 << le16_to_cpu(req->index))) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + + srcptr = tmpbuf; + srclen = 4; + break; + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= EHCI_PS_PE; + ehci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } + break; + case USB_PORT_FEAT_RESET: + if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && + !ehci_is_TDI() && + EHCI_PS_IS_LOWSPEED(reg)) { + /* Low speed device, give up ownership. */ + debug("port %d low speed --> companion\n", + req->index - 1); + reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); + break; + } else { + reg |= EHCI_PS_PR; + reg &= ~EHCI_PS_PE; + ehci_writel(status_reg, reg); + /* + * caller must wait, then call GetPortStatus + * usb 2.0 specification say 50 ms resets on + * root + */ + wait_ms(50); + portreset |= 1 << le16_to_cpu(req->index); + } + break; + default: + debug("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + /* unblock posted writes */ + ehci_readl(&hcor->or_usbcmd); + break; + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~EHCI_PS_PE; + break; + case USB_PORT_FEAT_C_ENABLE: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) + reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); + case USB_PORT_FEAT_C_CONNECTION: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; + break; + case USB_PORT_FEAT_OVER_CURRENT: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; + break; + case USB_PORT_FEAT_C_RESET: + portreset &= ~(1 << le16_to_cpu(req->index)); + break; + default: + debug("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + ehci_writel(status_reg, reg); + /* unblock posted write */ + ehci_readl(&hcor->or_usbcmd); + break; + default: + debug("Unknown request\n"); + goto unknown; + } + + wait_ms(1); + len = min3(srclen, le16_to_cpu(req->length), length); + if (srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + else + debug("Len is 0\n"); + + dev->act_len = len; + dev->status = 0; + return 0; + +unknown: + debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n", + req->requesttype, req->request, le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->length)); + + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return -1; +} + +int usb_lowlevel_stop(void) +{ + return ehci_hcd_stop(); +} + +int usb_lowlevel_init(void) +{ + uint32_t reg; + uint32_t cmd; + + if (ehci_hcd_init() != 0) + return -1; + + /* EHCI spec section 4.1 */ + if (ehci_reset() != 0) + return -1; + +#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) + if (ehci_hcd_init() != 0) + return -1; +#endif + + /* Set head of reclaim list */ + memset(&qh_list, 0, sizeof(qh_list)); + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); + + /* Set async. queue head pointer. */ + ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); + + reg = ehci_readl(&hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); + printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); + /* Port Indicators */ + if (HCS_INDICATOR(reg)) + descriptor.hub.wHubCharacteristics |= 0x80; + /* Port Power Control */ + if (HCS_PPC(reg)) + descriptor.hub.wHubCharacteristics |= 0x01; + + /* Start the host controller. */ + cmd = ehci_readl(&hcor->or_usbcmd); + /* Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't */ + cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + cmd |= CMD_RUN; + ehci_writel(&hcor->or_usbcmd, cmd); + + /* take control over the ports */ + cmd = ehci_readl(&hcor->or_configflag); + cmd |= FLAG_CF; + ehci_writel(&hcor->or_configflag, cmd); + /* unblock posted write */ + cmd = ehci_readl(&hcor->or_usbcmd); + wait_ms(5); + reg = HC_VERSION(ehci_readl(&hccr->cr_capbase)); + printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); + + rootdev = 0; + + return 0; +} + +int +submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length) +{ + + if (usb_pipetype(pipe) != PIPE_BULK) { + debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +int +submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *setup) +{ + + if (usb_pipetype(pipe) != PIPE_CONTROL) { + debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + + if (usb_pipedevice(pipe) == rootdev) { + if (rootdev == 0) + dev->speed = USB_SPEED_HIGH; + return ehci_submit_root(dev, pipe, buffer, length, setup); + } + return ehci_submit_async(dev, pipe, buffer, length, setup); +} + +int +submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, int interval) +{ + + debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", + dev, pipe, buffer, length, interval); + return -1; +} diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/usb_ehci_core.h new file mode 100644 index 0000000..39e5c5e --- /dev/null +++ b/drivers/usb/usb_ehci_core.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i Skåne AB + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_CORE_H +#define USB_EHCI_CORE_H + +extern int rootdev; +extern struct ehci_hccr *hccr; +extern volatile struct ehci_hcor *hcor; + +#endif diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c new file mode 100644 index 0000000..81d5d21 --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <pci.h> +#include <usb.h> +#include <mpc83xx.h> +#include <asm/io.h> +#include <asm/bitops.h> + +#include "usb_ehci.h" +#include "usb_ehci_fsl.h" +#include "usb_ehci_core.h" + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + * + * Excerpts from linux ehci fsl driver. + */ +int ehci_hcd_init(void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + uint32_t addr, temp; + + addr = (uint32_t)&(im->usb[0]); + hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + /* Configure clock */ + clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); + + /* Confgure interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp + | REFSEL_16MHZ | UTMI_PHY_EN); + + /* Wait for clock to stabilize */ + do { + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + udelay(1000); + } while (!(temp & PHY_CLK_VALID)); + + /* Set to Host mode */ + temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST); + + out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB); + out_be32((void *)(addr + FSL_SOC_USB_SNOOP2), + 0x80000000 | SNOOP_SIZE_2GB); + + /* Init phy */ + /* TODO: handle different phys? */ + out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI); + + /* Enable interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c); + out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040); + out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001); + + /* Enable interface. */ + temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL)); + out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN); + + temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE)); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/usb_ehci_fsl.h b/drivers/usb/usb_ehci_fsl.h new file mode 100644 index 0000000..c429af1 --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2005 freescale semiconductor + * Copyright (c) 2005 MontaVista Software + * Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _EHCI_FSL_H +#define _EHCI_FSL_H + +/* Global offsets */ +#define FSL_SKIP_PCI 0x100 + +/* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_ULPIVP 0x170 +#define FSL_SOC_USB_PORTSC1 0x184 +#define PORT_PTS_MSK (3 << 30) +#define PORT_PTS_UTMI (0 << 30) +#define PORT_PTS_ULPI (2 << 30) +#define PORT_PTS_SERIAL (3 << 30) +#define PORT_PTS_PTW (1 << 28) + +/* USBMODE Register bits */ +#define CM_IDLE (0 << 0) +#define CM_RESERVED (1 << 0) +#define CM_DEVICE (2 << 0) +#define CM_HOST (3 << 0) +#define USBMODE_RESERVED_2 (0 << 2) +#define SLOM (1 << 3) +#define SDIS (1 << 4) + +/* CONTROL Register bits */ +#define ULPI_INT_EN (1 << 0) +#define WU_INT_EN (1 << 1) +#define USB_EN (1 << 2) +#define LSF_EN (1 << 3) +#define KEEP_OTG_ON (1 << 4) +#define OTG_PORT (1 << 5) +#define REFSEL_12MHZ (0 << 6) +#define REFSEL_16MHZ (1 << 6) +#define REFSEL_48MHZ (2 << 6) +#define PLL_RESET (1 << 8) +#define UTMI_PHY_EN (1 << 9) +#define PHY_CLK_SEL_UTMI (0 << 10) +#define PHY_CLK_SEL_ULPI (1 << 10) +#define CLKIN_SEL_USB_CLK (0 << 11) +#define CLKIN_SEL_USB_CLK2 (1 << 11) +#define CLKIN_SEL_SYS_CLK (2 << 11) +#define CLKIN_SEL_SYS_CLK2 (3 << 11) +#define RESERVED_18 (0 << 13) +#define RESERVED_17 (0 << 14) +#define RESERVED_16 (0 << 15) +#define WU_INT (1 << 16) +#define PHY_CLK_VALID (1 << 17) + +#define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ +#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ +#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#define SNOOP_SIZE_2GB 0x1e + +/* System Clock Control Register */ +#define MPC83XX_SCCR_USB_MASK 0x00f00000 +#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 +#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 +#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 + +#endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/usb_ehci_ixp.c b/drivers/usb/usb_ehci_ixp.c new file mode 100644 index 0000000..25c18c1 --- /dev/null +++ b/drivers/usb/usb_ehci_ixp.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2008, Michael Trimarchi <trimarchimichael@yahoo.it> + * + * Author: Michael Trimarchi <trimarchimichael@yahoo.it> + * This code is based on ehci freescale driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <usb.h> +#include "usb_ehci.h" +#include "usb_ehci_core.h" +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + hccr = (struct ehci_hccr *)(0xcd000100); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + printf("IXP4XX init hccr %x and hcor %x hc_length %d\n", + (uint32_t)hccr, (uint32_t)hcor, + (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/drivers/usb/usb_ehci_pci.c b/drivers/usb/usb_ehci_pci.c new file mode 100644 index 0000000..3e7143c --- /dev/null +++ b/drivers/usb/usb_ehci_pci.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <pci.h> +#include <usb.h> +#include "usb_ehci.h" +#include "usb_ehci_core.h" + +#ifdef CONFIG_PCI_EHCI_DEVICE +static struct pci_device_id ehci_pci_ids[] = { + /* Please add supported PCI EHCI controller ids here */ + {0, 0} +}; +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + pci_dev_t pdev; + uint32_t addr; + + pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); + if (pdev == -1) { + printf("EHCI host controller not found\n"); + return -1; + } + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr); + hccr = (struct ehci_hccr *)addr; + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(void) +{ + return 0; +} diff --git a/include/configs/VCMA9.h b/include/configs/VCMA9.h index 83d0d56..d9bcf6b 100644 --- a/include/configs/VCMA9.h +++ b/include/configs/VCMA9.h @@ -36,7 +36,6 @@ #define CONFIG_ARM920T 1 /* This is an ARM920T Core */ #define CONFIG_S3C2410 1 /* in a SAMSUNG S3C2410 SoC */ #define CONFIG_VCMA9 1 /* on a MPL VCMA9 Board */ -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ /* input clock of PLL */ #define CONFIG_SYS_CLK_FREQ 12000000/* VCMA9 has 12MHz input clock */ diff --git a/include/configs/afeb9260.h b/include/configs/afeb9260.h index e996bbd..9eed342 100644 --- a/include/configs/afeb9260.h +++ b/include/configs/afeb9260.h @@ -114,7 +114,6 @@ /* USB */ #define CONFIG_USB_OHCI_NEW 1 -#define LITTLEENDIAN 1 #define CONFIG_DOS_PARTITION 1 #define CONFIG_SYS_USB_OHCI_CPU_INIT 1 #define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00500000 /* AT91SAM9260_UHP_BASE */ diff --git a/include/configs/at91cap9adk.h b/include/configs/at91cap9adk.h index f1c5526..01da99b 100644 --- a/include/configs/at91cap9adk.h +++ b/include/configs/at91cap9adk.h @@ -131,7 +131,6 @@ /* USB */ #define CONFIG_USB_OHCI_NEW 1 -#define LITTLEENDIAN 1 #define CONFIG_DOS_PARTITION 1 #define CONFIG_SYS_USB_OHCI_CPU_INIT 1 #define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00700000 /* AT91_BASE_UHP */ diff --git a/include/configs/at91sam9260ek.h b/include/configs/at91sam9260ek.h index 4501cae..2f1a41f 100644 --- a/include/configs/at91sam9260ek.h +++ b/include/configs/at91sam9260ek.h @@ -116,7 +116,6 @@ /* USB */ #define CONFIG_USB_OHCI_NEW 1 -#define LITTLEENDIAN 1 #define CONFIG_DOS_PARTITION 1 #define CONFIG_SYS_USB_OHCI_CPU_INIT 1 #define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00500000 /* AT91SAM9260_UHP_BASE */ diff --git a/include/configs/at91sam9261ek.h b/include/configs/at91sam9261ek.h index 668fe3b..ebecfa4 100644 --- a/include/configs/at91sam9261ek.h +++ b/include/configs/at91sam9261ek.h @@ -129,7 +129,6 @@ /* USB */ #define CONFIG_USB_OHCI_NEW 1 -#define LITTLEENDIAN 1 #define CONFIG_DOS_PARTITION 1 #define CONFIG_SYS_USB_OHCI_CPU_INIT 1 #define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00500000 /* AT91SAM9261_UHP_BASE */ diff --git a/include/configs/at91sam9263ek.h b/include/configs/at91sam9263ek.h index c6603ff..09b871a 100644 --- a/include/configs/at91sam9263ek.h +++ b/include/configs/at91sam9263ek.h @@ -136,7 +136,6 @@ /* USB */ #define CONFIG_USB_OHCI_NEW 1 -#define LITTLEENDIAN 1 #define CONFIG_DOS_PARTITION 1 #define CONFIG_SYS_USB_OHCI_CPU_INIT 1 #define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00a00000 /* AT91SAM9263_UHP_BASE */ diff --git a/include/configs/davinci_dvevm.h b/include/configs/davinci_dvevm.h index a727f56..667c0d8 100644 --- a/include/configs/davinci_dvevm.h +++ b/include/configs/davinci_dvevm.h @@ -171,6 +171,8 @@ #define CONFIG_SYS_LONGHELP #define CONFIG_CRC32_VERIFY #define CONFIG_MX_CYCLIC +#define CONFIG_MUSB_HCD +#define CONFIG_USB_DAVINCI /*===================*/ /* Linux Information */ /*===================*/ @@ -203,6 +205,22 @@ #else #error "Either CONFIG_SYS_USE_NAND or CONFIG_SYS_USE_NOR _MUST_ be defined !!!" #endif +/*==========================*/ +/* USB MSC support (if any) */ +/*==========================*/ +#ifdef CONFIG_USB_DAVINCI +#define CONFIG_CMD_USB +#ifdef CONFIG_MUSB_HCD +#define CONFIG_USB_STORAGE +#define CONFIG_CMD_STORAGE +#define CONFIG_CMD_FAT +#define CONFIG_DOS_PARTITION +#endif +#ifdef CONFIG_USB_KEYBOARD +#define CONFIG_SYS_USB_EVENT_POLL +#define CONFIG_PREBOOT "usb start" +#endif +#endif /*=======================*/ /* KGDB support (if any) */ /*=======================*/ diff --git a/include/configs/delta.h b/include/configs/delta.h index fd97b74..abb2676 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -131,8 +131,6 @@ #define CONFIG_SYS_USB_OHCI_SLOT_NAME "delta" #define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 3 -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ - #define CONFIG_BOOTDELAY -1 #define CONFIG_ETHADDR 08:00:3e:26:0a:5b #define CONFIG_NETMASK 255.255.0.0 diff --git a/include/configs/mp2usb.h b/include/configs/mp2usb.h index fb10616..9ac7e9a 100644 --- a/include/configs/mp2usb.h +++ b/include/configs/mp2usb.h @@ -216,7 +216,6 @@ #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print Buffer Size */ #define CONFIG_SYS_DEVICE_DEREGISTER /* needs device_deregister */ -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ #define CONFIG_SYS_HZ 1000 #define CONFIG_SYS_HZ_CLOCK (AT91C_MASTER_CLOCK/2) /* AT91C_TC0_CMR is implicitly set to */ diff --git a/include/configs/sh7785lcr.h b/include/configs/sh7785lcr.h index 1b59059..ebca448 100644 --- a/include/configs/sh7785lcr.h +++ b/include/configs/sh7785lcr.h @@ -123,7 +123,6 @@ #undef CONFIG_SYS_DIRECT_FLASH_TFTP /* R8A66597 */ -#define LITTLEENDIAN /* for include/usb.h */ #define CONFIG_USB_R8A66597_HCD #define CONFIG_R8A66597_BASE_ADDR SH7785LCR_USB_BASE #define CONFIG_R8A66597_XTAL 0x0000 /* 12MHz */ diff --git a/include/configs/smdk6400.h b/include/configs/smdk6400.h index 57c82d1..c61667f 100644 --- a/include/configs/smdk6400.h +++ b/include/configs/smdk6400.h @@ -293,7 +293,6 @@ #define CONFIG_SYS_USB_OHCI_SLOT_NAME "s3c6400" #define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 3 #define CONFIG_SYS_USB_OHCI_CPU_INIT 1 -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ #define CONFIG_USB_STORAGE 1 #endif diff --git a/include/configs/trab.h b/include/configs/trab.h index 562cd60..0a7a73d 100644 --- a/include/configs/trab.h +++ b/include/configs/trab.h @@ -44,7 +44,6 @@ #define CONFIG_S3C2400 1 /* in a SAMSUNG S3C2400 SoC */ #define CONFIG_TRAB 1 /* on a TRAB Board */ #undef CONFIG_TRAB_50MHZ /* run the CPU at 50 MHz */ -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ /* automatic software updates (see board/trab/auto_update.c) */ #define CONFIG_AUTO_UPDATE 1 diff --git a/include/configs/trizepsiv.h b/include/configs/trizepsiv.h index b2065ee..0a8e994 100644 --- a/include/configs/trizepsiv.h +++ b/include/configs/trizepsiv.h @@ -42,8 +42,6 @@ */ #define CONFIG_PXA27X 1 /* This is an PXA27x CPU */ -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ - #define CONFIG_MMC 1 #define BOARD_LATE_INIT 1 diff --git a/include/usb.h b/include/usb.h index 510df95..7c47098 100644 --- a/include/usb.h +++ b/include/usb.h @@ -138,7 +138,7 @@ enum { struct usb_device { int devnum; /* Device number on USB bus */ - int slow; /* Slow device? */ + int speed; /* full/low/high */ char mf[32]; /* manufacturer */ char prod[32]; /* product */ char serial[32]; /* serial number */ @@ -171,6 +171,7 @@ struct usb_device { unsigned long status; int act_len; /* transfered bytes */ int maxchild; /* Number of ports if hub */ + int portnr; struct usb_device *parent; struct usb_device *children[USB_MAXCHILDREN]; }; @@ -180,8 +181,9 @@ struct usb_device { */ #if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \ - defined(CONFIG_USB_OHCI_NEW) || defined(CONFIG_USB_SL811HS) || \ - defined(CONFIG_USB_ISP116X_HCD) || defined(CONFIG_USB_R8A66597_HCD) + defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \ + defined(CONFIG_USB_SL811HS) || defined(CONFIG_USB_ISP116X_HCD) || \ + defined(CONFIG_USB_R8A66597_HCD) || defined(CONFIG_USB_DAVINCI) int usb_lowlevel_init(void); int usb_lowlevel_stop(void); @@ -263,13 +265,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); ((x_ & 0xFF000000UL) >> 24)); \ }) -#ifdef LITTLEENDIAN +#ifdef __LITTLE_ENDIAN # define swap_16(x) (x) # define swap_32(x) (x) #else # define swap_16(x) __swap_16(x) # define swap_32(x) __swap_32(x) -#endif /* LITTLEENDIAN */ +#endif /* * Calling this entity a "pipe" is glorifying it. A USB pipe @@ -279,7 +281,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); * - endpoint number (4 bits) * - current Data0/1 state (1 bit) * - direction (1 bit) - * - speed (1 bit) + * - speed (2 bits) * - max packet size (2 bits: 8, 16, 32 or 64) * - pipe type (2 bits: control, interrupt, bulk, isochronous) * @@ -296,7 +298,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); * - device: bits 8-14 * - endpoint: bits 15-18 * - Data0/1: bit 19 - * - speed: bit 26 (0 = Full, 1 = Low Speed) + * - speed: bit 26 (0 = Full, 1 = Low Speed, 2 = High) * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, * 10 = control, 11 = bulk) * @@ -308,8 +310,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); /* Create various pipes... */ #define create_pipe(dev,endpoint) \ (((dev)->devnum << 8) | (endpoint << 15) | \ - ((dev)->slow << 26) | (dev)->maxpacketsize) -#define default_pipe(dev) ((dev)->slow << 26) + ((dev)->speed << 26) | (dev)->maxpacketsize) +#define default_pipe(dev) ((dev)->speed << 26) #define usb_sndctrlpipe(dev, endpoint) ((PIPE_CONTROL << 30) | \ create_pipe(dev, endpoint)) @@ -359,7 +361,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); #define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) #define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) #define usb_pipedata(pipe) (((pipe) >> 19) & 1) -#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipespeed(pipe) (((pipe) >> 26) & 3) +#define usb_pipeslow(pipe) (usb_pipespeed(pipe) == USB_SPEED_LOW) #define usb_pipetype(pipe) (((pipe) >> 30) & 3) #define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) #define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) diff --git a/include/usb_defs.h b/include/usb_defs.h index 353019f..8032e57 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -80,6 +80,12 @@ #define USB_DIR_OUT 0 #define USB_DIR_IN 0x80 +/* USB device speeds */ +#define USB_SPEED_FULL 0x0 /* 12Mbps */ +#define USB_SPEED_LOW 0x1 /* 1.5Mbps */ +#define USB_SPEED_HIGH 0x2 /* 480Mbps */ +#define USB_SPEED_RESERVED 0x3 + /* Descriptor types */ #define USB_DT_DEVICE 0x01 #define USB_DT_CONFIG 0x02 @@ -202,6 +208,7 @@ #define USB_PORT_FEAT_RESET 4 #define USB_PORT_FEAT_POWER 8 #define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_HIGHSPEED 10 #define USB_PORT_FEAT_C_CONNECTION 16 #define USB_PORT_FEAT_C_ENABLE 17 #define USB_PORT_FEAT_C_SUSPEND 18 @@ -216,6 +223,9 @@ #define USB_PORT_STAT_RESET 0x0010 #define USB_PORT_STAT_POWER 0x0100 #define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 /* support for EHCI */ +#define USB_PORT_STAT_SPEED \ + (USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED) /* wPortChange bits */ #define USB_PORT_STAT_C_CONNECTION 0x0001 |