From a0cb3fc31e58996a1c5732715ac04159d4d284fd Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 10 Dec 2008 15:52:06 +0100 Subject: [PATCH 01/25] USB storage cleanup patch Cleanup usb storage Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- common/usb_storage.c | 710 ++++++++++++++++++++++++------------------- 1 file changed, 394 insertions(+), 316 deletions(-) diff --git a/common/usb_storage.c b/common/usb_storage.c index 94f659fd31..973d82f7d6 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -63,9 +63,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 @@ -110,7 +110,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 +126,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 +164,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 +184,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 +194,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 +204,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 +217,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;i0) + 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 +327,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 +342,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 +354,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 +370,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 +397,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 +438,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 +474,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 +495,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 +514,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 +580,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 +633,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 +675,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 +699,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 +762,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 +796,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 +859,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 +988,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;idevnum==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 +1089,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 +1162,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 +1198,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 +1227,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 && @@ -1175,17 +1244,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 +1267,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 +1303,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; } From 3e126484df7868e341545cce740b24b62b0cd3b7 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 28 Nov 2008 13:19:19 +0100 Subject: [PATCH 02/25] Prepare USB layer for ehci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare USB layer for ehci support Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- common/cmd_usb.c | 2 +- common/usb.c | 2 +- include/usb.h | 19 +++++++++++-------- include/usb_defs.h | 10 ++++++++++ 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 423a29f33e..c95f61bd5e 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -310,7 +310,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", + dev->speed ? "1.5MBit/s" : "12MBit/s", 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 ee181528c8..313845234f 100644 --- a/common/usb.c +++ b/common/usb.c @@ -1136,7 +1136,7 @@ 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; + usb->speed = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; dev->children[port] = usb; usb->parent = dev; diff --git a/include/usb.h b/include/usb.h index 510df95d62..cd6f37a197 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(CONFI_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \ + defined(CONFIG_USB_SL811HS) || defined(CONFIG_USB_ISP116X_HCD) || \ + defined(CONFIG_USB_R8A66597_HCD) int usb_lowlevel_init(void); int usb_lowlevel_stop(void); @@ -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 353019fc16..8032e57125 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 From aaf098cfeed04595d4c5100ffd39095d79edbf90 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 28 Nov 2008 13:20:46 +0100 Subject: [PATCH 03/25] USB ehci core support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add USB ehci core support Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci.h | 121 +++++++ drivers/usb/usb_ehci_core.c | 634 ++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_core.h | 29 ++ 4 files changed, 785 insertions(+) create mode 100644 drivers/usb/usb_ehci.h create mode 100644 drivers/usb/usb_ehci_core.c create mode 100644 drivers/usb/usb_ehci_core.h diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 856f51a75f..fbc6521a90 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -28,6 +28,7 @@ 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 diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h new file mode 100644 index 0000000000..ebffb44d1e --- /dev/null +++ b/drivers/usb/usb_ehci.h @@ -0,0 +1,121 @@ +/*- + * 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 + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +/* (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 { + uint8_t cr_caplength; + uint16_t cr_hciversion; + uint32_t cr_hcsparams; + uint32_t cr_hccparams; + uint8_t cr_hcsp_portrt[8]; +}; + +struct ehci_hcor { + uint32_t or_usbcmd; + uint32_t or_usbsts; + 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; + uint32_t or_portsc[2]; + uint32_t or_systune; +}; + +#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect */ +#define EHCI_PS_PTC 0x000f0000 /* RW port test control */ +#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */ +#define EHCI_PS_PO 0x00002000 /* RW port owner */ +#define EHCI_PS_PP 0x00001000 /* RW,RO port power */ +#define EHCI_PS_LS 0x00000c00 /* RO line status */ +#define EHCI_PS_PR 0x00000100 /* RW port reset */ +#define EHCI_PS_SUSP 0x00000080 /* RW suspend */ +#define EHCI_PS_FPR 0x00000040 /* RW force port resume */ +#define EHCI_PS_OCC 0x00000020 /* RWC over current change */ +#define EHCI_PS_OCA 0x00000010 /* RO over current active */ +#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */ +#define EHCI_PS_PE 0x00000004 /* RW port enable */ +#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */ +#define EHCI_PS_CS 0x00000001 /* 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) == 0x00000400) + +/* + * 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; +}; + +/* Low level intit 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 0000000000..765b5ddf75 --- /dev/null +++ b/drivers/usb/usb_ehci_core.c @@ -0,0 +1,634 @@ +/*- + * 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 + */ + +#include +#include +#include +#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))); + +struct usb_device_descriptor device = { + sizeof(struct usb_device_descriptor), /* 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 */ +}; + +struct usb_config_descriptor config = { + sizeof(struct usb_config_descriptor), + 2, /* bDescriptorType: UDESC_CONFIG */ + sizeof(struct usb_config_descriptor) + + sizeof(struct usb_interface_descriptor) + + sizeof(struct usb_endpoint_descriptor), + 0, + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ +}; + +struct usb_interface_descriptor interface = { + sizeof(struct usb_interface_descriptor), /* 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 */ +}; + +struct usb_endpoint_descriptor endpoint = { +sizeof(struct usb_endpoint_descriptor), /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: UE_DIR_IN | EHCI_INTR_ENDPT */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, 0, /* wMaxPacketSize */ + 255 /* bInterval */ +}; + +struct usb_hub_descriptor hub = { + sizeof(struct usb_hub_descriptor), /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, 0, /* wHubCharacteristics */ + 0xff, /* bPwrOn2PwrGood */ + {}, /* bHubCntrCurrent */ + {} /* at most 7 ports! XXX */ +}; + +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"); + return NULL; + } + p = &td[ntds]; + ntds++; + break; + default: + debug("unknown allocation size"); + return NULL; + } + + memset(p, sz, 0); + return p; +} + +static void ehci_free(void *p, size_t sz) +{ +} + +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_le32(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)", 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; + + debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p", dev, pipe, + buffer, length, req); + if (req != NULL) + debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->index)); + + qh = ehci_alloc(sizeof(struct QH), 32); + if (qh == NULL) { + debug("unable to allocate QH"); + return -1; + } + qh->qh_link = cpu_to_le32((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_le32(endpt); + endpt = (1 << 30) | + (dev->portnr << 23) | + (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); + qh->qh_endpt2 = cpu_to_le32(endpt); + qh->qh_overlay.qt_next = cpu_to_le32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_le32(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"); + goto fail; + } + td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + token = (0 << 31) | + (sizeof(*req) << 16) | + (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); + td->qt_token = cpu_to_le32(token); + if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { + debug("unable construct SETUP td"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_le32((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"); + goto fail; + } + td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_le32(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_le32(token); + if (ehci_td_buffer(td, buffer, length) != 0) { + debug("unable construct DATA td"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_le32((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"); + goto fail; + } + td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_le32(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_le32(token); + *tdp = cpu_to_le32((uint32_t) td); + tdp = &td->qt_next; + } + + qh_list.qh_link = cpu_to_le32((uint32_t) qh | QH_LINK_TYPE_QH); + + usbsts = le32_to_cpu(hcor->or_usbsts); + hcor->or_usbsts = cpu_to_le32(usbsts & 0x3f); + + /* Enable async. schedule. */ + hcor->or_usbcmd |= cpu_to_le32(0x20); + while ((hcor->or_usbsts & cpu_to_le32(0x8000)) == 0) + udelay(1); + + /* Wait for TDs to be processed. */ + ts = get_timer(0); + vtd = td; + do { + token = le32_to_cpu(vtd->qt_token); + if (!(token & 0x80)) + break; + } while (get_timer(ts) < CONFIG_SYS_HZ); + + /* Disable async schedule. */ + hcor->or_usbcmd &= ~cpu_to_le32(0x20); + while ((hcor->or_usbsts & cpu_to_le32(0x8000)) != 0) + udelay(1); + + qh_list.qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + + token = le32_to_cpu(qh->qh_overlay.qt_token); + if (!(token & 0x80)) { + debug("TOKEN=%#x", 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", + dev->devnum, le32_to_cpu(hcor->or_usbsts), + le32_to_cpu(hcor->or_portsc[0]), + le32_to_cpu(hcor->or_portsc[1])); + } + + return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; + +fail: + td = (void *)le32_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 *)le32_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; +} + +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; + int len, srclen; + uint32_t reg; + + srclen = 0; + srcptr = NULL; + + debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u", + 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 (typeReq) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + srcptr = &device; + srclen = sizeof(struct usb_device_descriptor); + break; + case USB_DT_CONFIG: + srcptr = &config; + srclen = sizeof(config) + + sizeof(struct usb_interface_descriptor) + + sizeof(struct usb_hub_descriptor); + break; + case USB_DT_STRING: + 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: + goto unknown; + } + break; + default: + debug("unknown value %x", 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: + srcptr = &hub; + srclen = sizeof(hub); + break; + default: + debug("unknown value %x", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + /* 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 DeviceRequest | USB_REQ_GET_STATUS: + memset(tmpbuf, 0, 4); + reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) + - 1]); + 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) + tmpbuf[0] |= USB_PORT_STAT_RESET; + if (reg & EHCI_PS_PP) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + 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 DeviceOutRequest | USB_REQ_SET_FEATURE: + reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_POWER: + reg |= EHCI_PS_PP; + break; + case USB_PORT_FEAT_RESET: + if (EHCI_PS_IS_LOWSPEED(reg)) { + /* Low speed device, give up ownership. */ + reg |= EHCI_PS_PO; + break; + } + /* Start reset sequence. */ + reg &= ~EHCI_PS_PE; + reg |= EHCI_PS_PR; + hcor->or_portsc[le16_to_cpu(req->index) - 1] = + cpu_to_le32(reg); + /* Wait for reset to complete. */ + udelay(500000); + /* Terminate reset sequence. */ + reg &= ~EHCI_PS_PR; + /* TODO: is it only fsl chip that requires this + * manual setting of port enable? + */ + reg |= EHCI_PS_PE; + hcor->or_portsc[le16_to_cpu(req->index) - 1] = + cpu_to_le32(reg); + /* Wait for HC to complete reset. */ + udelay(2000); + reg = + le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) + - 1]); + reg &= ~EHCI_PS_CLEAR; + if ((reg & EHCI_PS_PE) == 0) { + /* Not a high speed device, give up + * ownership. */ + reg |= EHCI_PS_PO; + break; + } + portreset |= 1 << le16_to_cpu(req->index); + break; + default: + debug("unknown feature %x", le16_to_cpu(req->value)); + goto unknown; + } + hcor->or_portsc[le16_to_cpu(req->index) - 1] = + cpu_to_le32(reg); + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~EHCI_PS_PE; + break; + case USB_PORT_FEAT_C_CONNECTION: + reg |= EHCI_PS_CSC; + break; + case USB_PORT_FEAT_C_RESET: + portreset &= ~(1 << le16_to_cpu(req->index)); + break; + default: + debug("unknown feature %x", le16_to_cpu(req->value)); + goto unknown; + } + hcor->or_portsc[le16_to_cpu(req->index) - 1] = + cpu_to_le32(reg); + break; + default: + debug("Unknown request"); + goto unknown; + } + + len = min3(srclen, le16_to_cpu(req->length), length); + if (srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + dev->act_len = len; + dev->status = 0; + return 0; + +unknown: + debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x", + 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; + + if (ehci_hcd_init() != 0) + return -1; + + /* Set head of reclaim list */ + memset(&qh_list, 0, sizeof(qh_list)); + qh_list.qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh_list.qh_endpt1 = cpu_to_le32((1 << 15) | (USB_SPEED_HIGH << 12)); + qh_list.qh_curtd = cpu_to_le32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_next = cpu_to_le32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + qh_list.qh_overlay.qt_token = cpu_to_le32(0x40); + + /* Set async. queue head pointer. */ + hcor->or_asynclistaddr = cpu_to_le32((uint32_t)&qh_list); + + reg = le32_to_cpu(hccr->cr_hcsparams); + hub.bNbrPorts = reg & 0xf; + if (reg & 0x10000) /* Port Indicators */ + hub.wHubCharacteristics |= 0x80; + if (reg & 0x10) /* Port Power Control */ + hub.wHubCharacteristics |= 0x01; + + /* take control over the ports */ + hcor->or_configflag |= cpu_to_le32(1); + + /* Start the host controller. */ + hcor->or_usbcmd |= cpu_to_le32(1); + + 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 0000000000..88e91b9f5d --- /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 From 6b92487dcf9afe83a3570153d66940fdb293be76 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 28 Nov 2008 13:22:09 +0100 Subject: [PATCH 04/25] USB ehci freescale support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add USB ehci freescale support Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci_fsl.c | 99 ++++++++++++++++++++++++++++++++++++++ drivers/usb/usb_ehci_fsl.h | 86 +++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 drivers/usb/usb_ehci_fsl.c create mode 100644 drivers/usb/usb_ehci_fsl.h diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index fbc6521a90..6ba154b80d 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o 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 # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c new file mode 100644 index 0000000000..9ca6c562a7 --- /dev/null +++ b/drivers/usb/usb_ehci_fsl.c @@ -0,0 +1,99 @@ +/* + * (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 +#include +#include +#include +#include +#include + +#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 + hccr->cr_caplength); + + /* 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 0000000000..c429af1253 --- /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 */ From db63299b1dd2894ade542278210bccd046de6435 Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 10 Dec 2008 17:55:19 +0100 Subject: [PATCH 05/25] [PATCH] Fix EHCI usb. I start to test on a IXP465 board and I find some errors in the code. This patch fix: - descriptor initizialization (config, interface and endpoint must be one next-to the other when the USB_DT_CONFIG message is send. - FIX little/endian bigendian (introduce the CONFIG_EHCI_DESC_BIG_ENDIAN and the CONFIG_EHCI_MMIO_BIG_ENDIAN) - Introduce the linux version of the usb_config_descriptor and usb_interface descriptor. This descriptor does't contains u-boot extension. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci.h | 50 +++++- drivers/usb/usb_ehci_core.c | 343 +++++++++++++++++++----------------- drivers/usb/usb_ehci_core.h | 2 +- 3 files changed, 234 insertions(+), 161 deletions(-) diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index ebffb44d1e..3e7a2ab008 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -24,6 +24,7 @@ /* (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) @@ -32,6 +33,7 @@ #define EndpointRequest \ ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + #define EndpointOutRequest \ ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) @@ -39,8 +41,9 @@ * Register Space. */ struct ehci_hccr { - uint8_t cr_caplength; - uint16_t cr_hciversion; + uint32_t cr_capbase; +#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) +#define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; @@ -48,7 +51,9 @@ struct ehci_hccr { struct ehci_hcor { uint32_t or_usbcmd; +#define CMD_ASE (1 << 5) uint32_t or_usbsts; +#define STD_ASS (1 << 15) uint32_t or_usbintr; uint32_t or_frindex; uint32_t or_ctrldssegment; @@ -60,6 +65,47 @@ struct ehci_hcor { uint32_t or_systune; }; +/* 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) (x) +#define ehci_writel(a, b) (a) = (b) +#else +#define ehci_readl(x) cpu_to_le32((x)) +#define ehci_writel(a, b) (a) = cpu_to_le32((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 0x00400000 /* RW wake on over current */ #define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect */ #define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect */ diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 765b5ddf75..869bea3e79 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. - * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -18,10 +18,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ - +#define DEBUG #include +#include #include #include +#include #include "usb_ehci.h" int rootdev; @@ -31,67 +33,75 @@ volatile struct ehci_hcor *hcor; static uint16_t portreset; static struct QH qh_list __attribute__((aligned(32))); -struct usb_device_descriptor device = { - sizeof(struct usb_device_descriptor), /* 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 */ +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 */ + }, }; -struct usb_config_descriptor config = { - sizeof(struct usb_config_descriptor), - 2, /* bDescriptorType: UDESC_CONFIG */ - sizeof(struct usb_config_descriptor) + - sizeof(struct usb_interface_descriptor) + - sizeof(struct usb_endpoint_descriptor), - 0, - 1, /* bNumInterface */ - 1, /* bConfigurationValue */ - 0, /* iConfiguration */ - 0x40, /* bmAttributes: UC_SELF_POWER */ - 0 /* bMaxPower */ -}; - -struct usb_interface_descriptor interface = { - sizeof(struct usb_interface_descriptor), /* 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 */ -}; - -struct usb_endpoint_descriptor endpoint = { -sizeof(struct usb_endpoint_descriptor), /* bLength */ - 5, /* bDescriptorType: UDESC_ENDPOINT */ - 0x81, /* bEndpointAddress: UE_DIR_IN | EHCI_INTR_ENDPT */ - 3, /* bmAttributes: UE_INTERRUPT */ - 8, 0, /* wMaxPacketSize */ - 255 /* bInterval */ -}; - -struct usb_hub_descriptor hub = { - sizeof(struct usb_hub_descriptor), /* bDescLength */ - 0x29, /* bDescriptorType: hub descriptor */ - 2, /* bNrPorts -- runtime modified */ - 0, 0, /* wHubCharacteristics */ - 0xff, /* bPwrOn2PwrGood */ - {}, /* bHubCntrCurrent */ - {} /* at most 7 ports! XXX */ -}; +static void ehci_free (void *p, size_t sz) +{ +} static void *ehci_alloc(size_t sz, size_t align) { @@ -107,14 +117,14 @@ static void *ehci_alloc(size_t sz, size_t align) break; case sizeof(struct qTD): if (ntds == 3) { - debug("out of TDs"); + debug("out of TDs\n"); return NULL; } p = &td[ntds]; ntds++; break; default: - debug("unknown allocation size"); + debug("unknown allocation size\n"); return NULL; } @@ -122,10 +132,6 @@ static void *ehci_alloc(size_t sz, size_t align) return p; } -static void ehci_free(void *p, size_t sz) -{ -} - static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) { uint32_t addr, delta, next; @@ -134,7 +140,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) addr = (uint32_t) buf; idx = 0; while (idx < 5) { - td->qt_buffer[idx] = cpu_to_le32(addr); + td->qt_buffer[idx] = cpu_to_hc32(addr); next = (addr + 4096) & ~4095; delta = next - addr; if (delta >= sz) @@ -145,7 +151,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) } if (idx == 5) { - debug("out of buffer pointers (%u bytes left)", sz); + debug("out of buffer pointers (%u bytes left)\n", sz); return -1; } @@ -163,22 +169,23 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t c, toggle; + uint32_t cmd; - debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p", dev, pipe, + 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", + 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), le16_to_cpu(req->index)); + le16_to_cpu(req->index)); qh = ehci_alloc(sizeof(struct QH), 32); if (qh == NULL) { - debug("unable to allocate QH"); + debug("unable to allocate QH\n"); return -1; } - qh->qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + 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) | @@ -189,13 +196,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); - qh->qh_endpt1 = cpu_to_le32(endpt); + 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_le32(endpt); - qh->qh_overlay.qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - qh->qh_overlay.qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + 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; @@ -206,21 +213,21 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, if (req != NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { - debug("unable to allocate SETUP td"); + debug("unable to allocate SETUP td\n"); goto fail; } - td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + 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_le32(token); + td->qt_token = cpu_to_hc32(token); if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { - debug("unable construct SETUP td"); + debug("unable construct SETUP td\n"); ehci_free(td, sizeof(*td)); goto fail; } - *tdp = cpu_to_le32((uint32_t) td); + *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; toggle = 1; } @@ -228,75 +235,80 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, if (length > 0 || req == NULL) { td = ehci_alloc(sizeof(struct qTD), 32); if (td == NULL) { - debug("unable to allocate DATA td"); + debug("unable to allocate DATA td\n"); goto fail; } - td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + 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_le32(token); + td->qt_token = cpu_to_hc32(token); if (ehci_td_buffer(td, buffer, length) != 0) { - debug("unable construct DATA td"); + debug("unable construct DATA td\n"); ehci_free(td, sizeof(*td)); goto fail; } - *tdp = cpu_to_le32((uint32_t) td); + *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"); + debug("unable to allocate ACK td\n"); goto fail; } - td->qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); + 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_le32(token); - *tdp = cpu_to_le32((uint32_t) td); + td->qt_token = cpu_to_hc32(token); + *tdp = cpu_to_hc32((uint32_t) td); tdp = &td->qt_next; } - qh_list.qh_link = cpu_to_le32((uint32_t) qh | QH_LINK_TYPE_QH); + qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); - usbsts = le32_to_cpu(hcor->or_usbsts); - hcor->or_usbsts = cpu_to_le32(usbsts & 0x3f); + usbsts = ehci_readl(hcor->or_usbsts); + ehci_writel(hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ - hcor->or_usbcmd |= cpu_to_le32(0x20); - while ((hcor->or_usbsts & cpu_to_le32(0x8000)) == 0) + cmd = ehci_readl(hcor->or_usbcmd); + hcor->or_usbcmd |= CMD_ASE; + ehci_writel(hcor->or_usbcmd, cmd); + + while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0) udelay(1); /* Wait for TDs to be processed. */ ts = get_timer(0); vtd = td; do { - token = le32_to_cpu(vtd->qt_token); + token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; } while (get_timer(ts) < CONFIG_SYS_HZ); /* Disable async schedule. */ - hcor->or_usbcmd &= ~cpu_to_le32(0x20); - while ((hcor->or_usbsts & cpu_to_le32(0x8000)) != 0) + cmd = ehci_readl(hcor->or_usbcmd); + cmd &= ~CMD_ASE; + ehci_writel(hcor->or_usbcmd, cmd); + while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0) udelay(1); - qh_list.qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - token = le32_to_cpu(qh->qh_overlay.qt_token); + token = hc32_to_cpu(qh->qh_overlay.qt_token); if (!(token & 0x80)) { - debug("TOKEN=%#x", token); + debug("TOKEN=%#x\n", token); switch (token & 0xfc) { case 0: toggle = token >> 31; @@ -322,20 +334,20 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, dev->act_len = length - ((token >> 16) & 0x7fff); } else { dev->act_len = 0; - debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x", - dev->devnum, le32_to_cpu(hcor->or_usbsts), - le32_to_cpu(hcor->or_portsc[0]), - le32_to_cpu(hcor->or_portsc[1])); + 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 *)le32_to_cpu(qh->qh_overlay.qt_next); + 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 *)le32_to_cpu(qh->qh_overlay.qt_next); + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); } ehci_free(qh, sizeof(*qh)); return -1; @@ -351,39 +363,40 @@ static inline int min3(int a, int b, int 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; + void *srcptr = NULL; int len, srclen; uint32_t reg; srclen = 0; - srcptr = NULL; - debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u", + 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 (typeReq) { + switch (le16_to_cpu(typeReq)) { case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (le16_to_cpu(req->value) >> 8) { case USB_DT_DEVICE: - srcptr = &device; - srclen = sizeof(struct usb_device_descriptor); + debug("USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = 0x12; break; case USB_DT_CONFIG: - srcptr = &config; - srclen = sizeof(config) + - sizeof(struct usb_interface_descriptor) + - sizeof(struct usb_hub_descriptor); + 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"; @@ -400,29 +413,34 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srclen = 42; break; default: + debug("unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); goto unknown; } break; default: - debug("unknown value %x", le16_to_cpu(req->value)); + 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: - srcptr = &hub; - srclen = sizeof(hub); + debug("USB_DT_HUB config\n"); + srcptr = &descriptor.hub; + srclen = 0x8; break; default: - debug("unknown value %x", le16_to_cpu(req->value)); + 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): @@ -431,9 +449,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srcptr = tmpbuf; srclen = 2; break; - case DeviceRequest | USB_REQ_GET_STATUS: + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) + reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; @@ -460,14 +478,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srcptr = tmpbuf; srclen = 4; break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_POWER: reg |= EHCI_PS_PP; break; case USB_PORT_FEAT_RESET: + debug("USB FEAT RESET\n"); if (EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ reg |= EHCI_PS_PO; @@ -476,8 +495,8 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* Start reset sequence. */ reg &= ~EHCI_PS_PE; reg |= EHCI_PS_PR; - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[ + le16_to_cpu(req->index) - 1], reg); /* Wait for reset to complete. */ udelay(500000); /* Terminate reset sequence. */ @@ -486,12 +505,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, * manual setting of port enable? */ reg |= EHCI_PS_PE; - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[ + le16_to_cpu(req->index) - 1], reg); /* Wait for HC to complete reset. */ udelay(2000); reg = - le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) + ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; if ((reg & EHCI_PS_PE) == 0) { @@ -503,14 +522,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, portreset |= 1 << le16_to_cpu(req->index); break; default: - debug("unknown feature %x", le16_to_cpu(req->value)); + debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - reg = le32_to_cpu(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: @@ -523,26 +541,29 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, portreset &= ~(1 << le16_to_cpu(req->index)); break; default: - debug("unknown feature %x", le16_to_cpu(req->value)); + debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - hcor->or_portsc[le16_to_cpu(req->index) - 1] = - cpu_to_le32(reg); + ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; default: - debug("Unknown request"); + 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", + 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)); @@ -559,34 +580,40 @@ int usb_lowlevel_stop(void) int usb_lowlevel_init(void) { uint32_t reg; + uint32_t cmd; if (ehci_hcd_init() != 0) return -1; /* Set head of reclaim list */ memset(&qh_list, 0, sizeof(qh_list)); - qh_list.qh_link = cpu_to_le32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - qh_list.qh_endpt1 = cpu_to_le32((1 << 15) | (USB_SPEED_HIGH << 12)); - qh_list.qh_curtd = cpu_to_le32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_next = cpu_to_le32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_altnext = cpu_to_le32(QT_NEXT_TERMINATE); - qh_list.qh_overlay.qt_token = cpu_to_le32(0x40); + 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. */ - hcor->or_asynclistaddr = cpu_to_le32((uint32_t)&qh_list); + ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list); - reg = le32_to_cpu(hccr->cr_hcsparams); - hub.bNbrPorts = reg & 0xf; + reg = ehci_readl(hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = reg & 0xf; + printf("NbrPorts %x\n", descriptor.hub.bNbrPorts); if (reg & 0x10000) /* Port Indicators */ - hub.wHubCharacteristics |= 0x80; + descriptor.hub.wHubCharacteristics |= 0x80; if (reg & 0x10) /* Port Power Control */ - hub.wHubCharacteristics |= 0x01; + descriptor.hub.wHubCharacteristics |= 0x01; /* take control over the ports */ - hcor->or_configflag |= cpu_to_le32(1); + cmd = ehci_readl(hcor->or_configflag); + cmd |= 1; + ehci_writel(hcor->or_configflag, cmd); /* Start the host controller. */ - hcor->or_usbcmd |= cpu_to_le32(1); + cmd = ehci_readl(hcor->or_configflag); + cmd |= 1; + ehci_writel(hcor->or_usbcmd, cmd); rootdev = 0; diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/usb_ehci_core.h index 88e91b9f5d..39e5c5e58c 100644 --- a/drivers/usb/usb_ehci_core.h +++ b/drivers/usb/usb_ehci_core.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. - * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB * All rights reserved. * * This program is free software; you can redistribute it and/or From 51ab142b8b546d5e627b2c8c36d0adae222565f7 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 11 Dec 2008 13:43:55 +0100 Subject: [PATCH 06/25] [PATCH] This patch add varius fix to the ehci. - fix ehci_readl, ehci_writel - introduce new define in ehci.h - introduce the handshake function for waiting on a register - fix usb_ehci_fsl with the new HC_LENGTH macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci.h | 27 +++++-- drivers/usb/usb_ehci_core.c | 145 +++++++++++++++++++++++++++--------- drivers/usb/usb_ehci_fsl.c | 3 +- include/usb.h | 2 +- 4 files changed, 133 insertions(+), 44 deletions(-) diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 3e7a2ab008..90b137ad01 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -45,15 +45,24 @@ struct ehci_hccr { #define HC_LENGTH(p) (((p) >> 0) & 0x00ff) #define HC_VERSION(p) (((p) >> 16) & 0xffff) uint32_t cr_hcsparams; +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) uint32_t cr_hccparams; uint8_t cr_hcsp_portrt[8]; }; struct ehci_hcor { uint32_t or_usbcmd; -#define CMD_ASE (1 << 5) +#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; @@ -61,10 +70,17 @@ struct ehci_hcor { 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[2]; uint32_t or_systune; }; +#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; @@ -91,11 +107,12 @@ struct usb_linux_config_descriptor { } __attribute__ ((packed)); #if defined CONFIG_EHCI_DESC_BIG_ENDIAN -#define ehci_readl(x) (x) -#define ehci_writel(a, b) (a) = (b) +#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((x)) -#define ehci_writel(a, b) (a) = cpu_to_le32((b)) +#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 diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 869bea3e79..ec50874f71 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -99,8 +99,55 @@ static struct descriptor { }, }; -static void ehci_free (void *p, size_t sz) +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) { + uint32_t result; + do { + result = ehci_readl(ptr); + debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result); + if (result == ~(uint32_t)0) + return -1; + result &= mask; + if (result == done) + return 0; + wait_ms(1); + msec--; + } while (msec > 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(&hcor->or_usbcmd, CMD_RESET, 0, 250); + if (ret < 0) { + printf("EHCI fail to reset\n"); + goto out; + } + +#if defined CONFIG_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); +#endif +out: + return ret; } static void *ehci_alloc(size_t sz, size_t align) @@ -170,6 +217,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; + uint32_t sts; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -277,16 +325,19 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); - usbsts = ehci_readl(hcor->or_usbsts); - ehci_writel(hcor->or_usbsts, (usbsts & 0x3f)); + usbsts = ehci_readl(&hcor->or_usbsts); + ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ - cmd = ehci_readl(hcor->or_usbcmd); - hcor->or_usbcmd |= CMD_ASE; - ehci_writel(hcor->or_usbcmd, cmd); + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&hcor->or_usbcmd, cmd); - while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0) - udelay(1); + sts = ehci_readl(&hcor->or_usbsts); + while ((sts & STD_ASS) == 0) { + sts = ehci_readl(&hcor->or_usbsts); + udelay(10); + } /* Wait for TDs to be processed. */ ts = get_timer(0); @@ -298,11 +349,15 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } while (get_timer(ts) < CONFIG_SYS_HZ); /* Disable async schedule. */ - cmd = ehci_readl(hcor->or_usbcmd); + cmd = ehci_readl(&hcor->or_usbcmd); cmd &= ~CMD_ASE; - ehci_writel(hcor->or_usbcmd, cmd); - while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0) - udelay(1); + ehci_writel(&hcor->or_usbcmd, cmd); + + sts = ehci_readl(&hcor->or_usbsts); + while ((sts & STD_ASS) != 0) { + sts = ehci_readl(&hcor->or_usbsts); + udelay(10); + } qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -335,9 +390,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } 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])); + 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; @@ -451,7 +506,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) + reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; @@ -479,9 +534,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, srclen = 4; break; case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= EHCI_PS_PE; + break; case USB_PORT_FEAT_POWER: reg |= EHCI_PS_PP; break; @@ -495,22 +553,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* Start reset sequence. */ reg &= ~EHCI_PS_PE; reg |= EHCI_PS_PR; - ehci_writel(hcor->or_portsc[ + ehci_writel(&hcor->or_portsc[ le16_to_cpu(req->index) - 1], reg); /* Wait for reset to complete. */ - udelay(500000); + wait_ms(500); /* Terminate reset sequence. */ reg &= ~EHCI_PS_PR; /* TODO: is it only fsl chip that requires this * manual setting of port enable? */ reg |= EHCI_PS_PE; - ehci_writel(hcor->or_portsc[ + ehci_writel(&hcor->or_portsc[ le16_to_cpu(req->index) - 1], reg); /* Wait for HC to complete reset. */ - udelay(2000); + wait_ms(10); reg = - ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) + ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; if ((reg & EHCI_PS_PE) == 0) { @@ -525,10 +583,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); + reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: @@ -537,6 +595,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, case USB_PORT_FEAT_C_CONNECTION: reg |= EHCI_PS_CSC; break; + case USB_PORT_FEAT_OVER_CURRENT: + reg |= EHCI_PS_OCC; + break; case USB_PORT_FEAT_C_RESET: portreset &= ~(1 << le16_to_cpu(req->index)); break; @@ -544,7 +605,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); break; default: debug("Unknown request\n"); @@ -585,6 +646,10 @@ int usb_lowlevel_init(void) if (ehci_hcd_init() != 0) return -1; + /* EHCI spec section 4.1 */ + if (ehci_reset() != 0) + return -1; + /* 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); @@ -595,25 +660,31 @@ int usb_lowlevel_init(void) qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40); /* Set async. queue head pointer. */ - ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list); + ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); - reg = ehci_readl(hccr->cr_hcsparams); - descriptor.hub.bNbrPorts = reg & 0xf; - printf("NbrPorts %x\n", descriptor.hub.bNbrPorts); + reg = ehci_readl(&hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); + printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); if (reg & 0x10000) /* Port Indicators */ descriptor.hub.wHubCharacteristics |= 0x80; if (reg & 0x10) /* Port Power Control */ descriptor.hub.wHubCharacteristics |= 0x01; - /* take control over the ports */ - cmd = ehci_readl(hcor->or_configflag); - cmd |= 1; - ehci_writel(hcor->or_configflag, cmd); - /* Start the host controller. */ - cmd = ehci_readl(hcor->or_configflag); - cmd |= 1; - ehci_writel(hcor->or_usbcmd, cmd); + 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 writes */ + cmd = ehci_readl(&hcor->or_usbcmd); + wait_ms(5); rootdev = 0; diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c index 9ca6c562a7..81d5d21657 100644 --- a/drivers/usb/usb_ehci_fsl.c +++ b/drivers/usb/usb_ehci_fsl.c @@ -43,7 +43,8 @@ int ehci_hcd_init(void) addr = (uint32_t)&(im->usb[0]); hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); - hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength); + 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, diff --git a/include/usb.h b/include/usb.h index cd6f37a197..b8f2fa256a 100644 --- a/include/usb.h +++ b/include/usb.h @@ -181,7 +181,7 @@ struct usb_device { */ #if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \ - defined(CONFI_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \ + defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \ defined(CONFIG_USB_SL811HS) || defined(CONFIG_USB_ISP116X_HCD) || \ defined(CONFIG_USB_R8A66597_HCD) From c0d722fe7ee1cb452dfd9246419188b3f6d9c4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remy=20B=C3=B6hmer?= Date: Sat, 13 Dec 2008 22:51:58 +0100 Subject: [PATCH 07/25] EHCI fix code and ixp4xx test. USB ehci configuration parameter: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #define CONFIG_CMD_USB 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_USB_EHCI #define CONFIG_USB_EHCI_IXP4XX 1 #define CONFIG_EHCI_IS_TDI 1 #define CONFIG_EHCI_DESC_BIG_ENDIAN 1 #define CONFIG_EHCI_MMIO_BIG_ENDIAN 1 #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 #define CONFIG_LEGACY_USB_INIT_SEQ 1 2 USB Device(s) found scanning bus for storage devices... 0 Storage Device(s) found => usb tree Device Tree: 1 Hub (1.5MBit/s, 0mA) | u-boot EHCI Host Controller | |+-2 Mass Storage (12MBit/s, 100mA) Sony Storage Media 0C07040930296 => Signed-off-by: Michael Trimarchi Signed-off-by: Remy Böhmer --- drivers/usb/usb_ehci.h | 51 ++++++++------- drivers/usb/usb_ehci_core.c | 127 ++++++++++++++++++++++-------------- 2 files changed, 106 insertions(+), 72 deletions(-) diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 90b137ad01..b72498b7e6 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -21,6 +22,10 @@ #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) @@ -45,10 +50,12 @@ struct ehci_hccr { #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; @@ -71,9 +78,9 @@ struct ehci_hcor { uint32_t _reserved_[9]; uint32_t or_configflag; #define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ - uint32_t or_portsc[2]; + 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 */ @@ -123,26 +130,24 @@ struct usb_linux_config_descriptor { #define cpu_to_hc32(x) cpu_to_le32((x)) #endif -#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current */ -#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect */ -#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect */ -#define EHCI_PS_PTC 0x000f0000 /* RW port test control */ -#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */ -#define EHCI_PS_PO 0x00002000 /* RW port owner */ -#define EHCI_PS_PP 0x00001000 /* RW,RO port power */ -#define EHCI_PS_LS 0x00000c00 /* RO line status */ -#define EHCI_PS_PR 0x00000100 /* RW port reset */ -#define EHCI_PS_SUSP 0x00000080 /* RW suspend */ -#define EHCI_PS_FPR 0x00000040 /* RW force port resume */ -#define EHCI_PS_OCC 0x00000020 /* RWC over current change */ -#define EHCI_PS_OCA 0x00000010 /* RO over current active */ -#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */ -#define EHCI_PS_PE 0x00000004 /* RW port enable */ -#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */ -#define EHCI_PS_CS 0x00000001 /* RO connect status */ +#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) == 0x00000400) +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) /* * Schedule Interface Space. @@ -177,8 +182,8 @@ struct QH { struct qTD qh_overlay; }; -/* Low level intit functions */ - +/* 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 index ec50874f71..07e9a6b8a0 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -1,6 +1,8 @@ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i SkÃ¥ne AB + * Copyright (c) 2008, Michael Trimarchi + * * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -18,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ -#define DEBUG #include #include #include @@ -99,6 +100,12 @@ static struct descriptor { }, }; +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) { uint32_t result; @@ -131,17 +138,17 @@ static int ehci_reset(void) cmd = ehci_readl(&hcor->or_usbcmd); cmd |= CMD_RESET; ehci_writel(&hcor->or_usbcmd, cmd); - ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250); if (ret < 0) { printf("EHCI fail to reset\n"); goto out; } -#if defined CONFIG_EHCI_IS_TDI +#if defined(CONFIG_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 +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) tmp |= USBMODE_BE; #endif ehci_writel(reg_ptr, tmp); @@ -427,7 +434,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, 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", @@ -506,8 +521,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): memset(tmpbuf, 0, 4); - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - - 1]); + reg = ehci_readl(status_reg); if (reg & EHCI_PS_CS) tmpbuf[0] |= USB_PORT_STAT_CONNECTION; if (reg & EHCI_PS_PE) @@ -516,8 +530,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[0] |= USB_PORT_STAT_SUSPEND; if (reg & EHCI_PS_OCA) tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; - if (reg & EHCI_PS_PR) - tmpbuf[0] |= USB_PORT_STAT_RESET; + 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); + 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; tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; @@ -530,73 +555,71 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, 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(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); + 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: - reg |= EHCI_PS_PP; + if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } break; case USB_PORT_FEAT_RESET: - debug("USB FEAT RESET\n"); - if (EHCI_PS_IS_LOWSPEED(reg)) { + 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); } - /* Start reset sequence. */ - reg &= ~EHCI_PS_PE; - reg |= EHCI_PS_PR; - ehci_writel(&hcor->or_portsc[ - le16_to_cpu(req->index) - 1], reg); - /* Wait for reset to complete. */ - wait_ms(500); - /* Terminate reset sequence. */ - reg &= ~EHCI_PS_PR; - /* TODO: is it only fsl chip that requires this - * manual setting of port enable? - */ - reg |= EHCI_PS_PE; - ehci_writel(&hcor->or_portsc[ - le16_to_cpu(req->index) - 1], reg); - /* Wait for HC to complete reset. */ - wait_ms(10); - reg = - ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - - 1]); - reg &= ~EHCI_PS_CLEAR; - if ((reg & EHCI_PS_PE) == 0) { - /* Not a high speed device, give up - * ownership. */ - reg |= EHCI_PS_PO; - break; - } - portreset |= 1 << le16_to_cpu(req->index); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + /* unblock posted writes */ + ehci_readl(&hcor->or_usbcmd); break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]); - reg &= ~EHCI_PS_CLEAR; + 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 |= EHCI_PS_CSC; + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; break; case USB_PORT_FEAT_OVER_CURRENT: - reg |= EHCI_PS_OCC; + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: portreset &= ~(1 << le16_to_cpu(req->index)); @@ -605,7 +628,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; } - ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); + ehci_writel(status_reg, reg); + /* unblock posted write */ + ehci_readl(&hcor->or_usbcmd); break; default: debug("Unknown request\n"); @@ -665,9 +690,11 @@ int usb_lowlevel_init(void) reg = ehci_readl(&hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); - if (reg & 0x10000) /* Port Indicators */ + /* Port Indicators */ + if (HCS_INDICATOR(reg)) descriptor.hub.wHubCharacteristics |= 0x80; - if (reg & 0x10) /* Port Power Control */ + /* Port Power Control */ + if (HCS_PPC(reg)) descriptor.hub.wHubCharacteristics |= 0x01; /* Start the host controller. */ @@ -682,9 +709,11 @@ int usb_lowlevel_init(void) cmd = ehci_readl(&hcor->or_configflag); cmd |= FLAG_CF; ehci_writel(&hcor->or_configflag, cmd); - /* unblock posted writes */ + /* 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; From 366523c26b6320af171459b19e6e0e9e3baa83ca Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Thu, 18 Dec 2008 10:05:37 +0100 Subject: [PATCH 08/25] USB change speed USB changes the speed according to the port status Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- common/usb.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/common/usb.c b/common/usb.c index 313845234f..335012798c 100644 --- a/common/usb.c +++ b/common/usb.c @@ -1061,10 +1061,12 @@ 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<speed = (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; From 1eb734fed3b79a5e6106dad16e88041894fdab30 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 12:15:35 +0530 Subject: [PATCH 09/25] usb : usb_kbd : Populating 'priv' member of USB keyboard device_t structure This patch populates the 'priv' field of the USB keyboard device_t structure. The 'priv' field is populated with the address of the 'struct usb_device' structure that represents the USB device. The 'priv' field can then be used in the 'usb_event_poll' function to determine the USB device that requires to be polled. An example of its usage in 'usb_event_poll' function is as below. device_t *dev; struct usb_device *usb_kbd_dev; dev = device_get_by_name("usbkbd"); usb_kbd_dev = (struct usb_device *)dev->priv; iface = &usb_kbd_dev->config.if_desc[0]; Signed-off-by: Thomas Abraham Signed-off-by: Remy Bohmer --- common/usb_kbd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 89e6ee7e5d..e0d006c322 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 */ From 14e4111cdab7e7738ff6a203d445e4d8377f058f Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Thu, 1 Jan 2009 19:48:07 -0500 Subject: [PATCH 10/25] usb_storage: do not reset SanDisk Corporation U3 Cruzer Micro USB thumb drive The SanDisk Corporation U3 Cruzer Micro 1/4GB Flash Drive 000016244373FFB4 does not like to be reset, so check for it. Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Remy Bohmer --- common/usb_storage.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/usb_storage.c b/common/usb_storage.c index 973d82f7d6..9ed629708a 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -1235,6 +1235,13 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, /* 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 From 8fea2914ac974029b65926ef8247d908f84d202d Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 31 Dec 2008 10:32:41 +0100 Subject: [PATCH 11/25] Add initial support for USB ehci pci Add USB ehci pci support. This patch doesn't include any pci_ids and it is not tested on real hardware. Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci_pci.c | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 drivers/usb/usb_ehci_pci.c diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6ba154b80d..26189b69a5 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -35,6 +35,7 @@ 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 # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/usb_ehci_pci.c b/drivers/usb/usb_ehci_pci.c new file mode 100644 index 0000000000..3e7143cd91 --- /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 +#include +#include +#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; +} From 1ed9f9adc88218841dfeb60b9094a5a548bff009 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 31 Dec 2008 10:33:22 +0100 Subject: [PATCH 12/25] USB ehci remove infinite loop and use handshake function USB ehci code cleanup. Use handshake instead of infinite while loop to check the STD_ASS status Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 49 +++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 07e9a6b8a0..97367076e2 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -28,7 +28,7 @@ #include "usb_ehci.h" int rootdev; -struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ +struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ volatile struct ehci_hcor *hcor; static uint16_t portreset; @@ -106,20 +106,19 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif -static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec) +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; do { result = ehci_readl(ptr); - debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result); if (result == ~(uint32_t)0) return -1; result &= mask; if (result == done) return 0; - wait_ms(1); - msec--; - } while (msec > 0); + udelay(1); + usec--; + } while (usec > 0); return -1; } @@ -138,21 +137,21 @@ static int ehci_reset(void) 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); + ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); if (ret < 0) { printf("EHCI fail to reset\n"); goto out; } -#if defined(CONFIG_EHCI_IS_TDI) - reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); - tmp = ehci_readl(reg_ptr); - tmp |= USBMODE_CM_HC; + 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); + tmp |= USBMODE_BE; #endif + ehci_writel(reg_ptr, tmp); + } out: return ret; } @@ -224,7 +223,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t endpt, token, usbsts; uint32_t c, toggle; uint32_t cmd; - uint32_t sts; + int ret = 0; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -340,10 +339,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, cmd |= CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); - sts = ehci_readl(&hcor->or_usbsts); - while ((sts & STD_ASS) == 0) { - sts = ehci_readl(&hcor->or_usbsts); - udelay(10); + 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. */ @@ -360,10 +360,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, cmd &= ~CMD_ASE; ehci_writel(&hcor->or_usbcmd, cmd); - sts = ehci_readl(&hcor->or_usbsts); - while ((sts & STD_ASS) != 0) { - sts = ehci_readl(&hcor->or_usbsts); - udelay(10); + 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); @@ -536,7 +537,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, /* 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); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); if (!ret) tmpbuf[0] |= USB_PORT_STAT_RESET; else From 7b6e31eb17e3ff76238a60803fc531517d516223 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Wed, 31 Dec 2008 10:33:56 +0100 Subject: [PATCH 13/25] USB ehci ixp4xx support Add USB ehci ixp4xx host controller. Test on ixdp465 board. Signed-off-by: Michael Trimarchi Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/usb_ehci_ixp.c | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 drivers/usb/usb_ehci_ixp.c diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 26189b69a5..0f94963502 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -36,6 +36,7 @@ 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 # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/usb_ehci_ixp.c b/drivers/usb/usb_ehci_ixp.c new file mode 100644 index 0000000000..25c18c104e --- /dev/null +++ b/drivers/usb/usb_ehci_ixp.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2008, Michael Trimarchi + * + * Author: Michael Trimarchi + * 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 +#include +#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; +} From c7d703f3f3c3d6b020bda4cf633f8a6167c3cd2a Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 1 Jan 2009 18:27:27 -0500 Subject: [PATCH 14/25] usb.h: use standard __LITTLE_ENDIAN from Linux headers Rather than forcing people to define a custom "LITTLEENDIAN", just use the __LITTLE_ENDIAN one from the Linux byteorder headers that every arch is already setting up. Signed-off-by: Mike Frysinger Signed-off-by: Remy Bohmer --- common/usb_storage.c | 2 -- cpu/arm920t/s3c24x0/usb_ohci.c | 4 +--- cpu/mips/au1x00_usb_ohci.c | 6 ++---- include/configs/VCMA9.h | 1 - include/configs/afeb9260.h | 1 - include/configs/at91cap9adk.h | 1 - include/configs/at91sam9260ek.h | 1 - include/configs/at91sam9261ek.h | 1 - include/configs/at91sam9263ek.h | 1 - include/configs/delta.h | 2 -- include/configs/mp2usb.h | 1 - include/configs/sh7785lcr.h | 1 - include/configs/smdk6400.h | 1 - include/configs/trab.h | 1 - include/configs/trizepsiv.h | 2 -- include/usb.h | 4 ++-- 16 files changed, 5 insertions(+), 25 deletions(-) diff --git a/common/usb_storage.c b/common/usb_storage.c index 9ed629708a..51f078948a 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! */ diff --git a/cpu/arm920t/s3c24x0/usb_ohci.c b/cpu/arm920t/s3c24x0/usb_ohci.c index 641f270490..9cbd477677 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 17489da8d4..ea02efbb0d 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/include/configs/VCMA9.h b/include/configs/VCMA9.h index 83d0d56c1e..d9bcf6b7ae 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 e996bbd327..9eed3423cc 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 f1c5526d67..01da99b82c 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 4501cae3c8..2f1a41f646 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 668fe3b08b..ebecfa4099 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 c6603ff1f8..09b871a5e9 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/delta.h b/include/configs/delta.h index fd97b746f3..abb2676bcc 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 fb10616c30..9ac7e9afb7 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 1b59059451..ebca448eaf 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 57c82d1a16..c61667fd0c 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 562cd6093f..0a7a73d30c 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 b2065ee48b..0a8e994123 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 b8f2fa256a..d20efb1b04 100644 --- a/include/usb.h +++ b/include/usb.h @@ -265,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 From a142896934c755e679ba87e227a8e449f39b0012 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:03 +0530 Subject: [PATCH 15/25] usb : musb : Adding host controller driver for Mentor USB controller Adding Mentor USB core functionality and Mentor USB Host controller functionality for Mentor USB OTG controller (musbhdrc). Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/musb_core.c | 141 +++++++ drivers/usb/musb_core.h | 317 ++++++++++++++++ drivers/usb/musb_hcd.c | 792 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb_hcd.h | 51 +++ 5 files changed, 1302 insertions(+) create mode 100644 drivers/usb/musb_core.c create mode 100644 drivers/usb/musb_core.h create mode 100644 drivers/usb/musb_hcd.c create mode 100644 drivers/usb/musb_hcd.h diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 0f94963502..098d7b62e1 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -37,6 +37,7 @@ 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 # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/musb_core.c b/drivers/usb/musb_core.c new file mode 100644 index 0000000000..ec57fc811c --- /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 + +#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 0000000000..9f5ebe75ef --- /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 +#include +#include + +#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 0000000000..352a0d4eb0 --- /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 +#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 0000000000..bb83311b01 --- /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 +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__ */ From a9d39ebe91ecdd5ac0a0cf56ea162a19773db8da Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:09 +0530 Subject: [PATCH 16/25] usb : musb : Adding USB VBUS enable functionality for DM644x DVEVM Adding USB VBUS enable functionality for DM644x DVEVM (TI DaVinci) platform. Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- board/davinci/dvevm/dvevm.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/board/davinci/dvevm/dvevm.c b/board/davinci/dvevm/dvevm.c index abf60b35b0..5dd081c275 100644 --- a/board/davinci/dvevm/dvevm.c +++ b/board/davinci/dvevm/dvevm.c @@ -101,3 +101,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 + From e142e9f35f8ec61e74bf8019428b003f5070c33b Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:13 +0530 Subject: [PATCH 17/25] usb : musb : Adding DM6446 (TI DaVinci) platform specific USB support Adding DM6446 (TI DaVinci) platform specific USB functionality for USB Phy and VBUS initialization. Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + drivers/usb/davinci_usb.c | 106 ++++++++++++++++++++++++++++++++++++++ drivers/usb/davinci_usb.h | 87 +++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 drivers/usb/davinci_usb.c create mode 100644 drivers/usb/davinci_usb.h diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 098d7b62e1..37dcf24030 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -38,6 +38,7 @@ 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 # device ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/davinci_usb.c b/drivers/usb/davinci_usb.c new file mode 100644 index 0000000000..e66f660fee --- /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 +#include +#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 0000000000..d270861afa --- /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 +#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__ */ + From 538ef967715322f64ee08efa2296d9682111b014 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:16 +0530 Subject: [PATCH 18/25] usb : musb : Enabling DM6446 (TI DaVinci) USB module power Enabling DM6446 (TI DaVinci) USB module power and MUSB low-level controller hook up to USB core layer. Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- board/davinci/dvevm/dvevm.c | 1 + include/usb.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/board/davinci/dvevm/dvevm.c b/board/davinci/dvevm/dvevm.c index 5dd081c275..bf36f73608 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 */ diff --git a/include/usb.h b/include/usb.h index d20efb1b04..7c47098d8a 100644 --- a/include/usb.h +++ b/include/usb.h @@ -183,7 +183,7 @@ struct usb_device { #if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \ 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_R8A66597_HCD) || defined(CONFIG_USB_DAVINCI) int usb_lowlevel_init(void); int usb_lowlevel_stop(void); From 20cc06611ea33fc0a67a5e56e6476379d2de3091 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 4 Jan 2009 09:41:20 +0530 Subject: [PATCH 19/25] usb : musb : Enabling USB MSC support for DM6446 (TI DaVinci) platform Enabling USB MSC support for DM6446 (TI DaVinci) platform in the configuration file. Signed-off-by: Ravi Babu Signed-off-by: Swaminathan S Signed-off-by: Thomas Abraham Signed-off-by: Ajay Kumar Gupta Signed-off-by: Remy Bohmer --- include/configs/davinci_dvevm.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/configs/davinci_dvevm.h b/include/configs/davinci_dvevm.h index a727f5625e..667c0d882e 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) */ /*=======================*/ From 597eb28bd9691266b7b804364cda577cdb51d106 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:01 +0100 Subject: [PATCH 20/25] USB: Fix speed detection on EHCI cntr with root hub transaction translators This patch fixes an issue that the speed of USB devices was not detected correctly on some EHCI controllers. This will be used on the upcoming VCT EHCI support. Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 97367076e2..28962fa026 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -546,7 +546,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, } if (reg & EHCI_PS_PP) tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; - tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 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; From 832e61418eedfea172bd2fdfd0ea0d199cc70a9d Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:10 +0100 Subject: [PATCH 21/25] USB: Add config option to call ehci_hcd_init() again after EHCI reset This patch adds the config option CONFIG_EHCI_HCD_INIT_AFTER_RESET to call ehci_hcd_init() again after ehci_reset() is executed. This is needed for the upcoming VCT EHCI support which needs to re-init the hcd part again after the EHCI CMD_RESET is executed. Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 28962fa026..2f38f33880 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -691,6 +691,11 @@ int usb_lowlevel_init(void) 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); From 4e0ea0efc1e501186aca8577a4042fc6fa641602 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:28 +0100 Subject: [PATCH 22/25] USB: Add EHCI support for VCT EHCI controller Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 37dcf24030..b306a65a98 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -39,6 +39,7 @@ 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 From daa2dafb450a8073a4e42fd46cd4e995b208e4cb Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Wed, 21 Jan 2009 17:12:19 +0100 Subject: [PATCH 23/25] USB: Add dcache support to the EHCI driver This patch adds 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 (like the MIPS VCT board port). Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- drivers/usb/usb_ehci.h | 5 ++ drivers/usb/usb_ehci_core.c | 98 +++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index b72498b7e6..b3c1d5d728 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -180,6 +180,11 @@ struct QH { 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 */ diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 2f38f33880..813f64abd3 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -106,6 +106,99 @@ static struct descriptor { #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; @@ -331,6 +424,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, 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)); @@ -350,6 +446,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, 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; From f1c1f540243438246aefb703fdcf16957e3a72e1 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 22 Jan 2009 10:11:21 +0100 Subject: [PATCH 24/25] USB: Add high-speed (480Mb/s) to all USB related outputs With this patch the USB related connection speed output ("usb tree" command and debug output) is now high-speed enabled. This patch also fixes a compilation warning when debugging is enabled. Signed-off-by: Stefan Roese Signed-off-by: Remy Bohmer --- common/cmd_usb.c | 12 +++++++++++- common/usb.c | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/common/cmd_usb.c b/common/cmd_usb.c index c95f61bd5e..a18e16e100 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->speed ? "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 335012798c..5f46831528 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"); @@ -1041,6 +1041,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) { @@ -1064,8 +1074,7 @@ static int hub_port_reset(struct usb_device *dev, int port, 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", @@ -1111,9 +1120,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); From 1a448db77b10153703bc5e4ad13dd55d88beb1d6 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Sun, 18 Jan 2009 23:04:27 -0500 Subject: [PATCH 25/25] usb_scan_devices: fix output with no devices We should check the return of usb_new_device() so that if no USB device is found, we print out the right message rather than always saying "new usb device found". Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Remy Bohmer --- common/usb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/usb.c b/common/usb.c index 5f46831528..87fca70706 100644 --- a/common/usb.c +++ b/common/usb.c @@ -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();