diff options
Diffstat (limited to 'drivers/usb/musbhdrc.c')
-rw-r--r-- | drivers/usb/musbhdrc.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/drivers/usb/musbhdrc.c b/drivers/usb/musbhdrc.c new file mode 100644 index 00000000000..ff907f9afe3 --- /dev/null +++ b/drivers/usb/musbhdrc.c @@ -0,0 +1,885 @@ +/* + * Mentor USB Core host controller driver. + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * + */ + +#include <common.h> + +/* Include usb hcd code if usb support is included in u-boot */ +#ifdef CONFIG_MUSB + +#include <usb.h> +#include "musbhdrc.h" + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); +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); + +/* The controller driver polls for changes in the state. This defines a timeout + for cases where the states do not change so the appropriate error can be + returned. */ +#define MUSB_USB_TIMEOUT 0x3FFFFF + +/* 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 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((musb_readb(MGC_O_HDRC_POWER) & MGC_M_POWER_HSMODE) >> 4) + +/* speed negotiated with the connected device */ +static u8 musb_speed; + +/* + * This function configures all the endpoint FIFOs. Endpoint 1 is used for bulk + * transfers and so the fifo size of EP1 Tx and Rx is set to 512 bytes. The + * other endpoints 2,3 & 4 are configured for default fifo size of 64 bytes but + * these endpoints are not used. + */ +void musb_configure_ep(void) +{ + /* Select Endpoint 1 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 1); + + /* Configure FIFO for endpoint 1 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x06); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x06); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0x08); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0x48); + + /* Select Endpoint 2 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 2); + + /* Configure FIFO for endpoint 2 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x03); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x03); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0x88); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0x90); + + /* Select Endpoint 3 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 3 ); + + /* Configure FIFO for endpoint 2 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x03); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x03); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0x98); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0xA0); + + /* Select Endpoint 3 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 4 ); + + /* Configure FIFO for endpoint 2 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x03); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x03); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0xA8); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0xB0); +} + +/* + * program the HDRC to start (enable interrupts, dma, etc.) + */ +void musb_start( void ) +{ + u8 devctl; + + /* disable all interrupts */ + musb_writew(MGC_O_HDRC_INTRTXE, 0x0000); + musb_writew(MGC_O_HDRC_INTRRXE, 0x0000); + musb_writeb(MGC_O_HDRC_INTRUSBE, 0x00); + musb_writeb(MGC_O_HDRC_TESTMODE, 0); + + /* put into basic highspeed mode and start session */ + musb_writeb(MGC_O_HDRC_POWER, (MGC_M_POWER_HSENAB)); + devctl = musb_readb(MGC_O_HDRC_DEVCTL); + devctl |= MGC_M_DEVCTL_SESSION; + musb_writeb(MGC_O_HDRC_DEVCTL, devctl); +} + +/* + * This function writes data to endpoint fifo + */ +static void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u32 address; + u8 *data = (u8*)fifo_data; + + /* select the endpoint index */ + musb_writeb(MGC_O_HDRC_INDEX, ep); + address = MUSB_FIFO_OFFSET(ep)+0x20; + + /* write the data to the fifo */ + while(length) { + musb_writeb(address, *data); + data++; + length--; + } +} + +/* + * This function reads data from endpoint fifo + */ +static void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u32 address; + u8 *data = (u8*)fifo_data; + + /* select the endpoint index */ + musb_writeb(MGC_O_HDRC_INDEX, ep); + address = MUSB_FIFO_OFFSET(ep)+0x20; + + /* read the data to the fifo */ + while(length) { + *data = musb_readb(address); + data++; + length--; + } +} + +/* + * This function performs all intializations required for setting up the + * bulk endpoint. + */ +static void setup_bulk_ep(u8 bulkep) +{ + u16 csr; + + /* select bulk endpoint */ + musb_writeb(MGC_O_HDRC_INDEX, bulkep); + + /* clear the data toggle bit of bluk endpoint */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + /* also, flush the Tx and Rx FIFO of endpoint 1 */ + if ((musb_readb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) & MGC_M_TXCSR_TXPKTRDY) == MGC_M_TXCSR_TXPKTRDY) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_FLUSHFIFO; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + } + + if ((musb_readb(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)) & MGC_M_RXCSR_RXPKTRDY) == MGC_M_RXCSR_RXPKTRDY) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_FLUSHFIFO; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + } +} + +/* + * 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 == 0) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + if ((csr & MGC_M_CSR0_H_RXSTALL ) == MGC_M_CSR0_H_RXSTALL) { + csr = csr & ~(MGC_M_CSR0_H_RXSTALL|MGC_M_CSR0_H_STATUSPKT|MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + return(1); + } + } else { /* For non-ep0 */ + if (dir_out == 1) { /* is it tx ep */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + if ((csr & MGC_M_TXCSR_H_RXSTALL) == MGC_M_TXCSR_H_RXSTALL) { + csr = csr & ~(MGC_M_CSR0_H_RXSTALL|MGC_M_CSR0_H_STATUSPKT|MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + return(1); + } + } else { /* is it rx ep */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + if ((csr & MGC_M_RXCSR_H_RXSTALL) == MGC_M_RXCSR_H_RXSTALL) { + csr = csr & ~(MGC_M_CSR0_H_RXSTALL|MGC_M_CSR0_H_STATUSPKT|MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + return(1); + } + } + } + + /* There is no RxStall at the endpoint */ + return(0); +} + + +/* + * waits until ep0 is ready. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u32 timeout; + + timeout = MUSB_USB_TIMEOUT; + do { + /* is there a stall */ + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + return( -2 ); + } + + switch(bit_mask) { + case MGC_M_CSR0_TXPKTRDY: + /* check if TXPKTRDY bit is cleared */ + if ((musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)) & MGC_M_CSR0_TXPKTRDY) != MGC_M_CSR0_TXPKTRDY) + return(0); + break; + + case MGC_M_CSR0_RXPKTRDY: + /* check if RXPKTRDY bit is set */ + if ((musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)) & MGC_M_CSR0_RXPKTRDY) == MGC_M_CSR0_RXPKTRDY) + return(0); + break; + + case MGC_M_CSR0_H_REQPKT: + /* check if the request has been sent */ + if ((musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)) & MGC_M_CSR0_H_REQPKT) != MGC_M_CSR0_H_REQPKT) + return(0); + break; + } + timeout--; + } + while(timeout > 0); + + /* timed-out */ + dev->status = USB_ST_CRC_ERR; + 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 = -1; + 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 = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_TXPKTRDY|MGC_M_CSR0_H_SETUPPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MGC_M_CSR0_TXPKTRDY); + dev->act_len = 0; + + /* control transfer setup phase completes */ + 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; + + do { + /* Determine the next read length */ + nextlen = (( len-rxlen) > maxpktsize) ? maxpktsize:(len-rxlen); + + /* Set the ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | MGC_M_CSR0_H_REQPKT; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + result = wait_until_ep0_ready(dev, MGC_M_CSR0_RXPKTRDY); + if (result < 0) + return(result); + + /* Actual number of bytes received by usb */ + rxedlength = musb_readb(MGC_INDEXED_OFFSET(MGC_O_HDRC_COUNT0)); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr & (~MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + + rxlen = rxlen + nextlen; + dev->act_len = rxlen; + } + while(rxlen < len); + + /* done reading the data */ + 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; + + do { + /* 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 = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_H_DIS_PING|MGC_M_CSR0_TXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + result = wait_until_ep0_ready(dev, MGC_M_CSR0_TXPKTRDY); + if ( result < 0 ) + return(result); + + txlen = txlen + nextlen; + dev->act_len = txlen; + } + while(txlen < len); + + /* done writing the data */ + return(0); +} + + +/* + * 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 = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_H_DIS_PING|MGC_M_CSR0_TXPKTRDY|MGC_M_CSR0_H_STATUSPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MGC_M_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_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_H_DIS_PING|MGC_M_CSR0_H_REQPKT|MGC_M_CSR0_H_STATUSPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + result = wait_until_ep0_ready(dev, MGC_M_CSR0_H_REQPKT); + if (result < 0) + return(result); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr & (~((MGC_M_CSR0_RXPKTRDY|MGC_M_CSR0_H_STATUSPKT))); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + return(0); +} + + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + if (dev->high == 1) + return(MGC_TYPE_SPEED_HIGH); + else + if (dev->slow == 1) + return(MGC_TYPE_SPEED_LOW); + else + return(MGC_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) != MGC_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 */ + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_TXHUBADDR), hub ); + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_TXHUBPORT), (chid+1) ); + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_RXHUBADDR), hub ); + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_RXHUBPORT), (chid+1) ); +} + +/* + * 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); + int ep = usb_pipeendpoint(pipe); + u16 csr; + u16 wIntrTxE; + u8 devspeed; + + /* select control endpoint */ + musb_writeb(MGC_O_HDRC_INDEX, MUSB_CONTROL_EP); + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + + /* disable interrupt in case we flush */ + wIntrTxE = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_INTRTXE)); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_INTRTXE), (wIntrTxE & ~(1 << ep))); + + /* endpoint 0: just flush */ + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), (csr|MGC_M_CSR0_FLUSHFIFO)); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), (csr|MGC_M_CSR0_FLUSHFIFO)); + + /* target addr and (for multipoint) hub addr/port */ + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_TXFUNCADDR), devnum); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_RXFUNCADDR), devnum); + + + /* configure the hub address and the port number as required */ + if (( musb_ishighspeed()) && (dev->parent != NULL)) { + devspeed = get_dev_speed(dev); + if (devspeed != MGC_TYPE_SPEED_HIGH) { + config_hub_port(dev, MUSB_CONTROL_EP); + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TYPE0), devspeed << 6); + } + } else { + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TYPE0), musb_speed << 6); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_TXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_TXHUBPORT), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_RXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_RXHUBPORT), 0); + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return(-1); + + if ((setup->request == 0x06) || /* GET_DESCRIPTOR */ + (setup->request == 0x08) || /* GET_CONFIGURATION */ + (setup->request == 0x0A) || /* GET_INTERFACE */ + (setup->request == 0x00)) { /* GET_STATUS */ + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return(-1); + + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return(-1); + } else { + if ((setup->request == 0x05) || /* SET_ADDRESS */ + (setup->request == 0x09) || /* SET_CONFIGURATION */ + (setup->request == 0x03) || /* SET_FEATURE */ + (setup->request == 0x03) || /* SET_FEATURE */ + (setup->request == 0x0B) || /* SET_INTERFACE */ + (setup->request == 0x01) || /* CLEAR_FEATURE */ + (setup->request == 0xFF)) { /* USB Mass Stroage Reset */ + + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return(-1); + } else { + if (setup->request == 0x07) { /* SET_DESCRIPTOR */ + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return(-1); + + /* control transfer in status phase */ + if ( ctrlreq_in_status_phase(dev) < 0 ) + return(-1); + } else { + /* unhandled control transfer */ + return(-1); + } + } + } + + /* end of control transfer */ + 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; + u32 timeout; + + /* select bulk endpoint */ + musb_writeb(MGC_O_HDRC_INDEX, MUSB_BULK_EP); + + /* write the address of the device */ + if ( dir_out == 1 ) + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_TXFUNCADDR), devnum); + else + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_RXFUNCADDR), devnum); + + /* configure the hub address and the port number as required */ + if ((musb_ishighspeed()) && (dev->parent != NULL)) { + devspeed = get_dev_speed(dev); + if (devspeed != MGC_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 == 1) { + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_TXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_TXHUBPORT), 0); + } else { + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_RXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_RXHUBPORT), 0); + } + devspeed = musb_speed; + } + + if (dir_out == 1) { /* bulk-out transfer */ + /* Write the old data toggle value */ + if (usb_gettoggle(dev, ep, dir_out) == 0) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = MGC_M_TXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + } else { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_H_WR_DATATOGGLE; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + csr = csr | (usb_gettoggle(dev, ep, dir_out)<<8); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + } + + /* Program the TxType register */ + type = (devspeed << MGC_S_TYPE_SPEED) | + (0x2 << MGC_S_TYPE_PROTO) | + (ep & 0xF); + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXTYPE), type); + + /* Write maximum packet size to the TxMaxp register */ + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXMAXP), dev->epmaxpacketout[ep]); + + while(txLen < len) { + nextLen = ((len-txLen) < dev->epmaxpacketout[ep]) ? (len-txLen):dev->epmaxpacketout[ep]; + + /* Write the data to the FIFO */ + write_fifo(1, nextLen, (void*)(((u8*)buffer)+txLen)); + + /* Set the TxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_TXPKTRDY; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + /* Wait until the TxPktRdy bit is cleared */ + timeout = MUSB_USB_TIMEOUT; + do { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + if ((csr & MGC_M_TXCSR_H_RXSTALL) == MGC_M_TXCSR_H_RXSTALL) { + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr & (~(MGC_M_TXCSR_H_RXSTALL))); + + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + + dev->status = USB_ST_STALLED; + dev->act_len = txLen; + return(0); + } + + if ((csr & MGC_M_TXCSR_H_ERROR) == MGC_M_TXCSR_H_ERROR) { + /* keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + return(0); + } + + /* maintain a timeout */ + if (timeout-- == 0) { + /* keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + + /* clear the TxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr & (~MGC_M_TXCSR_TXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + return(0); + } + } + while((csr & MGC_M_TXCSR_TXPKTRDY) == MGC_M_TXCSR_TXPKTRDY); + + txLen = txLen + nextLen; + #if 0 + if ((txLen == len) && (nextLen == dev->epmaxpacketout[ep])) { + /* Set the TxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_TXPKTRDY; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + /* Wait until the TxPktRdy bit is cleared */ + while(( musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR) & MGC_M_TXCSR_TXPKTRDY)) == MGC_M_TXCSR_TXPKTRDY); + } + #endif + } + + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + } else { /* bulk-in transfer */ + /* Write the old data toggle value */ + if (usb_gettoggle(dev, ep, dir_out) == 0) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = MGC_M_RXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + } else { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_H_WR_DATATOGGLE; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + csr = csr | (usb_gettoggle(dev, ep, dir_out)<<9); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + } + + /* Program the RxType register */ + type = (devspeed << MGC_S_TYPE_SPEED) | + (0x2 << MGC_S_TYPE_PROTO) | + (ep & 0xF); + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXTYPE), type); + + /* Write the maximum packet size to the RxMaxp register */ + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXMAXP), dev->epmaxpacketin[ep]); + + while(txLen < len) { + nextLen = ((len-txLen) < dev->epmaxpacketin[ep]) ? (len-txLen):dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_H_REQPKT; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + /* Wait until the RxPktRdy bit is cleared */ + timeout = MUSB_USB_TIMEOUT; + do { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + if ((csr & MGC_M_RXCSR_H_RXSTALL) == MGC_M_RXCSR_H_RXSTALL) { + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr & (~(MGC_M_RXCSR_H_RXSTALL))); + dev->status = USB_ST_STALLED; + dev->act_len = txLen; + return(0); + } + + if ((csr & MGC_M_RXCSR_H_ERROR) == MGC_M_RXCSR_H_ERROR) { + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + return(0); + } + + if (timeout-- == 0) { + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + + /* clear the ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr & (~MGC_M_RXCSR_H_REQPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + return(0); + } + } + while((csr & MGC_M_RXCSR_RXPKTRDY) != MGC_M_RXCSR_RXPKTRDY); + + /* Read the data from the FIFO */ + read_fifo(1, nextLen, (void*)(((u8*)buffer) + txLen)); + + /* Clear the RxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr & ~(MGC_M_RXCSR_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + txLen = txLen + nextLen; + } + + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + } + + /* 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 */ + musb_configure_ep(); + musb_start(); + + /* Wait until musb is enabled in host mode with a timeout. No hot + plug support. So there should be a usb device connected. */ + timeout = MUSB_USB_TIMEOUT*0x10; + do { + /* wait until the musb core moves into host mode */ + if (( musb_readb(MGC_O_HDRC_DEVCTL) & MGC_M_DEVCTL_HM) == MGC_M_DEVCTL_HM) + break; + + /* maintain a timeout */ + timeout--; + } + while( timeout > 0 ); + + /* if musb core is not in host mode, then return */ + if (timeout == 0) + return( -1 ); + + /* start usb bus reset */ + power = musb_readb(MGC_O_HDRC_POWER); + power = power | MGC_M_POWER_RESET; + musb_writeb(MGC_O_HDRC_POWER, power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = musb_readb(MGC_O_HDRC_POWER); + power = power & (~MGC_M_POWER_RESET); + musb_writeb(MGC_O_HDRC_POWER, power); + + /* Determine if the connected device is a high/full/low speed device */ + if ((musb_readb(MGC_O_HDRC_POWER) & MGC_M_POWER_HSMODE) == MGC_M_POWER_HSMODE) { + /* High speed device is connected */ + musb_speed = MGC_TYPE_SPEED_HIGH; + } else { + if ((musb_readb(MGC_O_HDRC_DEVCTL) & MGC_M_DEVCTL_FSDEV) == MGC_M_DEVCTL_FSDEV) { + /* Full speed device is connected */ + musb_speed = MGC_TYPE_SPEED_FULL; + } else { + /* Low speed device is connected */ + musb_speed = MGC_TYPE_SPEED_LOW; + } + } + + /* setup the bulk endpoint */ + setup_bulk_ep(MUSB_BULK_EP); + + /* usb low level intialization is complete */ + return(0); +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ + /* Reset the USB module */ + musb_platform_deinit(); + musb_writeb(MGC_O_HDRC_DEVCTL, 0); + + /* All done */ + 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 ) +{ + return(-1); +} + +#endif /* CONFIG_MUSB */ + + + |