diff options
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r-- | drivers/usb/musb/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 8 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 40 | ||||
-rw-r--r-- | drivers/usb/musb/musb_debug.h | 205 | ||||
-rw-r--r-- | drivers/usb/musb/musb_udc.c | 963 | ||||
-rw-r--r-- | drivers/usb/musb/omap3.c | 129 | ||||
-rw-r--r-- | drivers/usb/musb/omap3.h | 48 |
7 files changed, 1393 insertions, 2 deletions
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 09e0a5f..f2ccd9f 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -26,7 +26,9 @@ include $(TOPDIR)/config.mk LIB := $(obj)libusb_musb.a COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o +COBJS-$(CONFIG_MUSB_UDC) += musb_udc.o musb_core.o COBJS-$(CONFIG_USB_DAVINCI) += davinci.o +COBJS-$(CONFIG_USB_OMAP3) += omap3.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index ec57fc8..22f3dba 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -32,7 +32,9 @@ struct musb_regs *musbr; */ void musb_start(void) { +#if defined(CONFIG_MUSB_HCD) u8 devctl; +#endif /* disable all interrupts */ writew(0, &musbr->intrtxe); @@ -74,9 +76,10 @@ void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) /* Configure fifo size and fifo base address */ writeb(idx, &musbr->txfifosz); writew(fifoaddr >> 3, &musbr->txfifoadd); + + csr = readw(&musbr->txcsr); #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 */ @@ -87,9 +90,10 @@ void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) /* Configure fifo size and fifo base address */ writeb(idx, &musbr->rxfifosz); writew(fifoaddr >> 3, &musbr->rxfifoadd); + + csr = readw(&musbr->rxcsr); #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 */ diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index f9da3f0..15c7f49 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -40,6 +40,36 @@ #define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ +/* EP0 */ +struct musb_ep0_regs { + u16 reserved4; + u16 csr0; + u16 reserved5; + u16 reserved6; + u16 count0; + u8 host_type0; + u8 host_naklimit0; + u8 reserved7; + u8 reserved8; + u8 reserved9; + u8 configdata; +}; + +/* EP 1-15 */ +struct musb_epN_regs { + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; +}; + /* Mentor USB core register overlay structure */ struct musb_regs { /* common registers */ @@ -97,6 +127,16 @@ struct musb_regs { u8 rxhubaddr; u8 rxhubport; } tar[16]; + /* + * end point registers + * ep0 elements are valid when array index is 0 + * otherwise epN is valid + */ + union musb_ep_regs { + struct musb_ep0_regs ep0; + struct musb_epN_regs epN; + } ep[16]; + } __attribute__((aligned(32))); /* diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h new file mode 100644 index 0000000..62380ff --- /dev/null +++ b/drivers/usb/musb/musb_debug.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix <Tom.Rix@windriver.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 + */ + +/* Define MUSB_DEBUG before including this file to get debug macros */ +#ifdef MUSB_DEBUG + +#define MUSB_FLAGS_PRINT(v, x, y) \ + if (((v) & MUSB_##x##_##y)) \ + serial_printf("\t\t"#y"\n") + +static inline void musb_print_pwr(u8 b) +{ + serial_printf("\tpower 0x%2.2x\n", b); + MUSB_FLAGS_PRINT(b, POWER, ISOUPDATE); + MUSB_FLAGS_PRINT(b, POWER, SOFTCONN); + MUSB_FLAGS_PRINT(b, POWER, HSENAB); + MUSB_FLAGS_PRINT(b, POWER, HSMODE); + MUSB_FLAGS_PRINT(b, POWER, RESET); + MUSB_FLAGS_PRINT(b, POWER, RESUME); + MUSB_FLAGS_PRINT(b, POWER, SUSPENDM); + MUSB_FLAGS_PRINT(b, POWER, ENSUSPEND); +} + +static inline void musb_print_csr0(u16 w) +{ + serial_printf("\tcsr0 0x%4.4x\n", w); + MUSB_FLAGS_PRINT(w, CSR0, FLUSHFIFO); + MUSB_FLAGS_PRINT(w, CSR0_P, SVDSETUPEND); + MUSB_FLAGS_PRINT(w, CSR0_P, SVDRXPKTRDY); + MUSB_FLAGS_PRINT(w, CSR0_P, SENDSTALL); + MUSB_FLAGS_PRINT(w, CSR0_P, SETUPEND); + MUSB_FLAGS_PRINT(w, CSR0_P, DATAEND); + MUSB_FLAGS_PRINT(w, CSR0_P, SENTSTALL); + MUSB_FLAGS_PRINT(w, CSR0, TXPKTRDY); + MUSB_FLAGS_PRINT(w, CSR0, RXPKTRDY); +} + +static inline void musb_print_intrusb(u8 b) +{ + serial_printf("\tintrusb 0x%2.2x\n", b); + MUSB_FLAGS_PRINT(b, INTR, VBUSERROR); + MUSB_FLAGS_PRINT(b, INTR, SESSREQ); + MUSB_FLAGS_PRINT(b, INTR, DISCONNECT); + MUSB_FLAGS_PRINT(b, INTR, CONNECT); + MUSB_FLAGS_PRINT(b, INTR, SOF); + MUSB_FLAGS_PRINT(b, INTR, RESUME); + MUSB_FLAGS_PRINT(b, INTR, SUSPEND); + + if (b & MUSB_INTR_BABBLE) + serial_printf("\t\tMUSB_INTR_RESET or MUSB_INTR_BABBLE\n"); + +} + +static inline void musb_print_intrtx(u16 w) +{ + serial_printf("\tintrtx 0x%4.4x\n", w); +} + +static inline void musb_print_intrrx(u16 w) +{ + serial_printf("\tintrx 0x%4.4x\n", w); +} + +static inline void musb_print_devctl(u8 b) +{ + serial_printf("\tdevctl 0x%2.2x\n", b); + if (b & MUSB_DEVCTL_BDEVICE) + serial_printf("\t\tB device\n"); + else + serial_printf("\t\tA device\n"); + if (b & MUSB_DEVCTL_FSDEV) + serial_printf("\t\tFast Device -(host mode)\n"); + if (b & MUSB_DEVCTL_LSDEV) + serial_printf("\t\tSlow Device -(host mode)\n"); + if (b & MUSB_DEVCTL_HM) + serial_printf("\t\tHost mode\n"); + else + serial_printf("\t\tPeripherial mode\n"); + if (b & MUSB_DEVCTL_HR) + serial_printf("\t\tHost request started(B device)\n"); + else + serial_printf("\t\tHost request finished(B device)\n"); + if (b & MUSB_DEVCTL_BDEVICE) { + if (b & MUSB_DEVCTL_SESSION) + serial_printf("\t\tStart of session(B device)\n"); + else + serial_printf("\t\tEnd of session(B device)\n"); + } else { + if (b & MUSB_DEVCTL_SESSION) + serial_printf("\t\tStart of session(A device)\n"); + else + serial_printf("\t\tEnd of session(A device)\n"); + } +} + +static inline void musb_print_config(u8 b) +{ + serial_printf("\tconfig 0x%2.2x\n", b); + if (b & MUSB_CONFIGDATA_MPRXE) + serial_printf("\t\tAuto combine rx bulk packets\n"); + if (b & MUSB_CONFIGDATA_MPTXE) + serial_printf("\t\tAuto split tx bulk packets\n"); + if (b & MUSB_CONFIGDATA_BIGENDIAN) + serial_printf("\t\tBig Endian ordering\n"); + else + serial_printf("\t\tLittle Endian ordering\n"); + if (b & MUSB_CONFIGDATA_HBRXE) + serial_printf("\t\tHigh speed rx iso endpoint\n"); + if (b & MUSB_CONFIGDATA_HBTXE) + serial_printf("\t\tHigh speed tx iso endpoint\n"); + if (b & MUSB_CONFIGDATA_DYNFIFO) + serial_printf("\t\tDynamic fifo sizing\n"); + if (b & MUSB_CONFIGDATA_SOFTCONE) + serial_printf("\t\tSoft Connect\n"); + if (b & MUSB_CONFIGDATA_UTMIDW) + serial_printf("\t\t16 bit data width\n"); + else + serial_printf("\t\t8 bit data width\n"); +} + +static inline void musb_print_rxmaxp(u16 w) +{ + serial_printf("\trxmaxp 0x%4.4x\n", w); +} + +static inline void musb_print_rxcsr(u16 w) +{ + serial_printf("\trxcsr 0x%4.4x\n", w); + MUSB_FLAGS_PRINT(w, RXCSR, AUTOCLEAR); + MUSB_FLAGS_PRINT(w, RXCSR, DMAENAB); + MUSB_FLAGS_PRINT(w, RXCSR, DISNYET); + MUSB_FLAGS_PRINT(w, RXCSR, PID_ERR); + MUSB_FLAGS_PRINT(w, RXCSR, DMAMODE); + MUSB_FLAGS_PRINT(w, RXCSR, CLRDATATOG); + MUSB_FLAGS_PRINT(w, RXCSR, FLUSHFIFO); + MUSB_FLAGS_PRINT(w, RXCSR, DATAERROR); + MUSB_FLAGS_PRINT(w, RXCSR, FIFOFULL); + MUSB_FLAGS_PRINT(w, RXCSR, RXPKTRDY); + MUSB_FLAGS_PRINT(w, RXCSR_P, SENTSTALL); + MUSB_FLAGS_PRINT(w, RXCSR_P, SENDSTALL); + MUSB_FLAGS_PRINT(w, RXCSR_P, OVERRUN); + + if (w & MUSB_RXCSR_P_ISO) + serial_printf("\t\tiso mode\n"); + else + serial_printf("\t\tbulk mode\n"); + +} + +static inline void musb_print_txmaxp(u16 w) +{ + serial_printf("\ttxmaxp 0x%4.4x\n", w); +} + +static inline void musb_print_txcsr(u16 w) +{ + serial_printf("\ttxcsr 0x%4.4x\n", w); + MUSB_FLAGS_PRINT(w, TXCSR, TXPKTRDY); + MUSB_FLAGS_PRINT(w, TXCSR, FIFONOTEMPTY); + MUSB_FLAGS_PRINT(w, TXCSR, FLUSHFIFO); + MUSB_FLAGS_PRINT(w, TXCSR, CLRDATATOG); + MUSB_FLAGS_PRINT(w, TXCSR_P, UNDERRUN); + MUSB_FLAGS_PRINT(w, TXCSR_P, SENTSTALL); + MUSB_FLAGS_PRINT(w, TXCSR_P, SENDSTALL); + + if (w & MUSB_TXCSR_MODE) + serial_printf("\t\tTX mode\n"); + else + serial_printf("\t\tRX mode\n"); +} + +#else + +/* stubs */ + +#define musb_print_pwr(b) +#define musb_print_csr0(w) +#define musb_print_intrusb(b) +#define musb_print_intrtx(w) +#define musb_print_intrrx(w) +#define musb_print_devctl(b) +#define musb_print_config(b) +#define musb_print_rxmaxp(w) +#define musb_print_rxcsr(w) +#define musb_print_txmaxp(w) +#define musb_print_txcsr(w) + +#endif /* MUSB_DEBUG */ diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c new file mode 100644 index 0000000..fc43cf4 --- /dev/null +++ b/drivers/usb/musb/musb_udc.c @@ -0,0 +1,963 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix <Tom.Rix@windriver.com> + * + * This file is a rewrite of the usb device part of + * repository git.omapzoom.org/repo/u-boot.git, branch master, + * file cpu/omap3/fastboot.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------- + * + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix@windriver.com> + * + * ------------------------------------------------------------------------- + * + * The details of connecting the device to the uboot usb device subsystem + * came from the old omap3 repository www.sakoman.net/u-boot-omap3.git, + * branch omap3-dev-usb, file drivers/usb/usbdcore_musb.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------- + * + * (C) Copyright 2008 Texas Instruments Incorporated. + * + * Based on + * u-boot OMAP1510 USB drivers (drivers/usbdcore_omap1510.c) + * twl4030 init based on linux (drivers/i2c/chips/twl4030_usb.c) + * + * Author: Diego Dompe (diego.dompe@ridgerun.com) + * Atin Malaviya (atin.malaviya@gmail.com) + * + * ------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <usb/musb_udc.h> +#include "../gadget/ep0.h" +#include "musb_core.h" +#if defined(CONFIG_USB_OMAP3) +#include "omap3.h" +#elif defined(CONFIG_USB_DAVINCI) +#include "davinci.h" +#endif + +/* Define MUSB_DEBUG for debugging */ +/* #define MUSB_DEBUG */ +#include "musb_debug.h" + +#define MAX_ENDPOINT 15 + +#define GET_ENDPOINT(dev,ep) \ +(((struct usb_device_instance *)(dev))->bus->endpoint_array + ep) + +#define SET_EP0_STATE(s) \ +do { \ + if ((0 <= (s)) && (SET_ADDRESS >= (s))) { \ + if ((s) != ep0_state) { \ + if ((debug_setup) && (debug_level > 1)) \ + serial_printf("INFO : Changing state " \ + "from %s to %s in %s at " \ + "line %d\n", \ + ep0_state_strings[ep0_state],\ + ep0_state_strings[s], \ + __PRETTY_FUNCTION__, \ + __LINE__); \ + ep0_state = s; \ + } \ + } else { \ + if (debug_level > 0) \ + serial_printf("Error at %s %d with setting " \ + "state %d is invalid\n", \ + __PRETTY_FUNCTION__, __LINE__, s); \ + } \ +} while (0) + +/* static implies these initialized to 0 or NULL */ +static int debug_setup; +static int debug_level; +static struct musb_epinfo epinfo[MAX_ENDPOINT * 2]; +static enum ep0_state_enum { + IDLE = 0, + TX, + RX, + SET_ADDRESS +} ep0_state = IDLE; +static char *ep0_state_strings[4] = { + "IDLE", + "TX", + "RX", + "SET_ADDRESS", +}; + +static struct urb *ep0_urb; +struct usb_endpoint_instance *ep0_endpoint; +static struct usb_device_instance *udc_device; +static int enabled; + +#ifdef MUSB_DEBUG +static void musb_db_regs(void) +{ + u8 b; + u16 w; + + b = readb(&musbr->faddr); + serial_printf("\tfaddr 0x%2.2x\n", b); + + b = readb(&musbr->power); + musb_print_pwr(b); + + w = readw(&musbr->ep[0].ep0.csr0); + musb_print_csr0(w); + + b = readb(&musbr->devctl); + musb_print_devctl(b); + + b = readb(&musbr->ep[0].ep0.configdata); + musb_print_config(b); + + w = readw(&musbr->frame); + serial_printf("\tframe 0x%4.4x\n", w); + + b = readb(&musbr->index); + serial_printf("\tindex 0x%2.2x\n", b); + + w = readw(&musbr->ep[1].epN.rxmaxp); + musb_print_rxmaxp(w); + + w = readw(&musbr->ep[1].epN.rxcsr); + musb_print_rxcsr(w); + + w = readw(&musbr->ep[1].epN.txmaxp); + musb_print_txmaxp(w); + + w = readw(&musbr->ep[1].epN.txcsr); + musb_print_txcsr(w); +} +#else +#define musb_db_regs() +#endif /* DEBUG_MUSB */ + +static void musb_peri_softconnect(void) +{ + u8 power, devctl; + u8 intrusb; + u16 intrrx, intrtx; + + /* Power off MUSB */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_SOFTCONN; + writeb(power, &musbr->power); + + /* Read intr to clear */ + intrusb = readb(&musbr->intrusb); + intrrx = readw(&musbr->intrrx); + intrtx = readw(&musbr->intrtx); + + udelay(1000 * 1000); /* 1 sec */ + + /* Power on MUSB */ + power = readb(&musbr->power); + power |= MUSB_POWER_SOFTCONN; + /* + * The usb device interface is usb 1.1 + * Disable 2.0 high speed by clearring the hsenable bit. + */ + power &= ~MUSB_POWER_HSENAB; + writeb(power, &musbr->power); + + /* Check if device is in b-peripheral mode */ + devctl = readb(&musbr->devctl); + if (!(devctl & MUSB_DEVCTL_BDEVICE) || + (devctl & MUSB_DEVCTL_HM)) { + serial_printf("ERROR : Unsupport USB mode\n"); + serial_printf("Check that mini-B USB cable is attached " + "to the device\n"); + } + + if (debug_setup && (debug_level > 1)) + musb_db_regs(); +} + +static void musb_peri_reset(void) +{ + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s reset\n", __PRETTY_FUNCTION__); + + if (ep0_endpoint) + ep0_endpoint->endpoint_address = 0xff; + + /* Sync sw and hw addresses */ + writeb(udc_device->address, &musbr->faddr); + + SET_EP0_STATE(IDLE); +} + +static void musb_peri_resume(void) +{ + /* noop */ +} + +static void musb_peri_ep0_stall(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_P_SENDSTALL; + writew(csr0, &musbr->ep[0].ep0.csr0); + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s stall\n", __PRETTY_FUNCTION__); +} + +static void musb_peri_ep0_ack_req(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_P_SVDRXPKTRDY; + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_ep0_tx_ready(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_TXPKTRDY; + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_ep0_tx_ready_and_last(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_P_DATAEND); + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_peri_ep0_last(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_P_DATAEND; + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_peri_ep0_set_address(void) +{ + u8 faddr; + writeb(udc_device->address, &musbr->faddr); + + /* Verify */ + faddr = readb(&musbr->faddr); + if (udc_device->address == faddr) { + SET_EP0_STATE(IDLE); + usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0); + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s Address set to %d\n", + __PRETTY_FUNCTION__, udc_device->address); + } else { + if (debug_level > 0) + serial_printf("ERROR : %s Address missmatch " + "sw %d vs hw %d\n", + __PRETTY_FUNCTION__, + udc_device->address, faddr); + } +} + +static void musb_peri_rx_ack(unsigned int ep) +{ + u16 peri_rxcsr; + + peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr); + peri_rxcsr &= ~MUSB_RXCSR_RXPKTRDY; + writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr); +} + +static void musb_peri_tx_ready(unsigned int ep) +{ + u16 peri_txcsr; + + peri_txcsr = readw(&musbr->ep[ep].epN.txcsr); + peri_txcsr |= MUSB_TXCSR_TXPKTRDY; + writew(peri_txcsr, &musbr->ep[ep].epN.txcsr); +} + +static void musb_peri_ep0_zero_data_request(int err) +{ + musb_peri_ep0_ack_req(); + + if (err) { + musb_peri_ep0_stall(); + SET_EP0_STATE(IDLE); + } else { + + musb_peri_ep0_last(); + + /* USBD state */ + switch (ep0_urb->device_request.bRequest) { + case USB_REQ_SET_ADDRESS: + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s received set " + "address\n", __PRETTY_FUNCTION__); + break; + + case USB_REQ_SET_CONFIGURATION: + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s Configured\n", + __PRETTY_FUNCTION__); + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); + break; + } + + /* EP0 state */ + if (USB_REQ_SET_ADDRESS == ep0_urb->device_request.bRequest) { + SET_EP0_STATE(SET_ADDRESS); + } else { + SET_EP0_STATE(IDLE); + } + } +} + +static void musb_peri_ep0_rx_data_request(void) +{ + /* + * This is the completion of the data OUT / RX + * + * Host is sending data to ep0 that is not + * part of setup. This comes from the cdc_recv_setup + * op that is device specific. + * + */ + musb_peri_ep0_ack_req(); + + ep0_endpoint->rcv_urb = ep0_urb; + ep0_urb->actual_length = 0; + SET_EP0_STATE(RX); +} + +static void musb_peri_ep0_tx_data_request(int err) +{ + if (err) { + musb_peri_ep0_stall(); + SET_EP0_STATE(IDLE); + } else { + musb_peri_ep0_ack_req(); + + ep0_endpoint->tx_urb = ep0_urb; + ep0_endpoint->sent = 0; + SET_EP0_STATE(TX); + } +} + +static void musb_peri_ep0_idle(void) +{ + u16 count0; + int err; + u16 csr0; + + /* + * Verify addresses + * A lot of confusion can be caused if the address + * in software, udc layer, does not agree with the + * hardware. Since the setting of the hardware address + * must be set after the set address request, the + * usb state machine is out of sync for a few frame. + * It is a good idea to run this check when changes + * are made to the state machine. + */ + if ((debug_level > 0) && + (ep0_state != SET_ADDRESS)) { + u8 faddr; + + faddr = readb(&musbr->faddr); + if (udc_device->address != faddr) { + serial_printf("ERROR : %s addresses do not" + "match sw %d vs hw %d\n", + __PRETTY_FUNCTION__, + udc_device->address, faddr); + udelay(1000 * 1000); + hang(); + } + } + + csr0 = readw(&musbr->ep[0].ep0.csr0); + + if (!(MUSB_CSR0_RXPKTRDY & csr0)) + goto end; + + count0 = readw(&musbr->ep[0].ep0.count0); + if (count0 == 0) + goto end; + + if (count0 != 8) { + if ((debug_setup) && (debug_level > 1)) + serial_printf("WARN : %s SETUP incorrect size %d\n", + __PRETTY_FUNCTION__, count0); + musb_peri_ep0_stall(); + goto end; + } + + read_fifo(0, count0, &ep0_urb->device_request); + + if (debug_level > 2) + print_usb_device_request(&ep0_urb->device_request); + + if (ep0_urb->device_request.wLength == 0) { + err = ep0_recv_setup(ep0_urb); + + /* Zero data request */ + musb_peri_ep0_zero_data_request(err); + } else { + /* Is data coming or going ? */ + u8 reqType = ep0_urb->device_request.bmRequestType; + + if (USB_REQ_DEVICE2HOST == (reqType & USB_REQ_DIRECTION_MASK)) { + err = ep0_recv_setup(ep0_urb); + /* Device to host */ + musb_peri_ep0_tx_data_request(err); + } else { + /* + * Host to device + * + * The RX routine will call ep0_recv_setup + * when the data packet has arrived. + */ + musb_peri_ep0_rx_data_request(); + } + } + +end: + return; +} + +static void musb_peri_ep0_rx(void) +{ + /* + * This is the completion of the data OUT / RX + * + * Host is sending data to ep0 that is not + * part of setup. This comes from the cdc_recv_setup + * op that is device specific. + * + * Pass the data back to driver ep0_recv_setup which + * should give the cdc_recv_setup the chance to handle + * the rx + */ + u16 csr0; + u16 count0; + + if (debug_level > 3) { + if (0 != ep0_urb->actual_length) { + serial_printf("%s finished ? %d of %d\n", + __PRETTY_FUNCTION__, + ep0_urb->actual_length, + ep0_urb->device_request.wLength); + } + } + + if (ep0_urb->device_request.wLength == ep0_urb->actual_length) { + musb_peri_ep0_last(); + SET_EP0_STATE(IDLE); + ep0_recv_setup(ep0_urb); + return; + } + + csr0 = readw(&musbr->ep[0].ep0.csr0); + if (!(MUSB_CSR0_RXPKTRDY & csr0)) + return; + + count0 = readw(&musbr->ep[0].ep0.count0); + + if (count0) { + struct usb_endpoint_instance *endpoint; + u32 length; + u8 *data; + + endpoint = ep0_endpoint; + if (endpoint && endpoint->rcv_urb) { + struct urb *urb = endpoint->rcv_urb; + unsigned int remaining_space = urb->buffer_length - + urb->actual_length; + + if (remaining_space) { + int urb_bad = 0; /* urb is good */ + + if (count0 > remaining_space) + length = remaining_space; + else + length = count0; + + data = (u8 *) urb->buffer_data; + data += urb->actual_length; + + /* The common musb fifo reader */ + read_fifo(0, length, data); + + musb_peri_ep0_ack_req(); + + /* + * urb's actual_length is updated in + * usbd_rcv_complete + */ + usbd_rcv_complete(endpoint, length, urb_bad); + + } else { + if (debug_level > 0) + serial_printf("ERROR : %s no space in " + "rcv buffer\n", + __PRETTY_FUNCTION__); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s problem with " + "endpoint\n", + __PRETTY_FUNCTION__); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s with nothing to do\n", + __PRETTY_FUNCTION__); + } +} + +static void musb_peri_ep0_tx(void) +{ + u16 csr0; + int transfer_size = 0; + unsigned int p, pm; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + + /* Check for pending tx */ + if (csr0 & MUSB_CSR0_TXPKTRDY) + goto end; + + /* Check if this is the last packet sent */ + if (ep0_endpoint->sent >= ep0_urb->actual_length) { + SET_EP0_STATE(IDLE); + goto end; + } + + transfer_size = ep0_urb->actual_length - ep0_endpoint->sent; + /* Is the transfer size negative ? */ + if (transfer_size <= 0) { + if (debug_level > 0) + serial_printf("ERROR : %s problem with the" + " transfer size %d\n", + __PRETTY_FUNCTION__, + transfer_size); + SET_EP0_STATE(IDLE); + goto end; + } + + /* Truncate large transfers to the fifo size */ + if (transfer_size > ep0_endpoint->tx_packetSize) + transfer_size = ep0_endpoint->tx_packetSize; + + write_fifo(0, transfer_size, &ep0_urb->buffer[ep0_endpoint->sent]); + ep0_endpoint->sent += transfer_size; + + /* Done or more to send ? */ + if (ep0_endpoint->sent >= ep0_urb->actual_length) + musb_ep0_tx_ready_and_last(); + else + musb_ep0_tx_ready(); + + /* Wait a bit */ + pm = 10; + for (p = 0; p < pm; p++) { + csr0 = readw(&musbr->ep[0].ep0.csr0); + if (!(csr0 & MUSB_CSR0_TXPKTRDY)) + break; + + /* Double the delay. */ + udelay(1 << pm); + } + + if ((ep0_endpoint->sent >= ep0_urb->actual_length) && (p < pm)) + SET_EP0_STATE(IDLE); + +end: + return; +} + +static void musb_peri_ep0(void) +{ + u16 csr0; + + if (SET_ADDRESS == ep0_state) + return; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + + /* Error conditions */ + if (MUSB_CSR0_P_SENTSTALL & csr0) { + csr0 &= ~MUSB_CSR0_P_SENTSTALL; + writew(csr0, &musbr->ep[0].ep0.csr0); + SET_EP0_STATE(IDLE); + } + if (MUSB_CSR0_P_SETUPEND & csr0) { + csr0 |= MUSB_CSR0_P_SVDSETUPEND; + writew(csr0, &musbr->ep[0].ep0.csr0); + SET_EP0_STATE(IDLE); + if ((debug_setup) && (debug_level > 1)) + serial_printf("WARN: %s SETUPEND\n", + __PRETTY_FUNCTION__); + } + + /* Normal states */ + if (IDLE == ep0_state) + musb_peri_ep0_idle(); + + if (TX == ep0_state) + musb_peri_ep0_tx(); + + if (RX == ep0_state) + musb_peri_ep0_rx(); +} + +static void musb_peri_rx_ep(unsigned int ep) +{ + u16 peri_rxcount = readw(&musbr->ep[ep].epN.rxcount); + + if (peri_rxcount) { + struct usb_endpoint_instance *endpoint; + u32 length; + u8 *data; + + endpoint = GET_ENDPOINT(udc_device, ep); + if (endpoint && endpoint->rcv_urb) { + struct urb *urb = endpoint->rcv_urb; + unsigned int remaining_space = urb->buffer_length - + urb->actual_length; + + if (remaining_space) { + int urb_bad = 0; /* urb is good */ + + if (peri_rxcount > remaining_space) + length = remaining_space; + else + length = peri_rxcount; + + data = (u8 *) urb->buffer_data; + data += urb->actual_length; + + /* The common musb fifo reader */ + read_fifo(ep, length, data); + + musb_peri_rx_ack(ep); + + /* + * urb's actual_length is updated in + * usbd_rcv_complete + */ + usbd_rcv_complete(endpoint, length, urb_bad); + + } else { + if (debug_level > 0) + serial_printf("ERROR : %s %d no space " + "in rcv buffer\n", + __PRETTY_FUNCTION__, ep); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s %d problem with " + "endpoint\n", + __PRETTY_FUNCTION__, ep); + } + + } else { + if (debug_level > 0) + serial_printf("ERROR : %s %d with nothing to do\n", + __PRETTY_FUNCTION__, ep); + } +} + +static void musb_peri_rx(u16 intr) +{ + unsigned int ep; + + /* Check for EP0 */ + if (0x01 & intr) + musb_peri_ep0(); + + for (ep = 1; ep < 16; ep++) { + if ((1 << ep) & intr) + musb_peri_rx_ep(ep); + } +} + +static void musb_peri_tx(u16 intr) +{ + /* Check for EP0 */ + if (0x01 & intr) + musb_peri_ep0_tx(); + + /* + * Use this in the future when handling epN tx + * + * u8 ep; + * + * for (ep = 1; ep < 16; ep++) { + * if ((1 << ep) & intr) { + * / * handle tx for this endpoint * / + * } + * } + */ +} + +void udc_irq(void) +{ + /* This is a high freq called function */ + if (enabled) { + u8 intrusb; + + intrusb = readb(&musbr->intrusb); + + /* + * See drivers/usb/gadget/mpc8xx_udc.c for + * state diagram going from detached through + * configuration. + */ + if (MUSB_INTR_RESUME & intrusb) { + usbd_device_event_irq(udc_device, + DEVICE_BUS_ACTIVITY, 0); + musb_peri_resume(); + } + + musb_peri_ep0(); + + if (MUSB_INTR_RESET & intrusb) { + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + musb_peri_reset(); + } + + if (MUSB_INTR_DISCONNECT & intrusb) { + /* cable unplugged from hub/host */ + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + musb_peri_reset(); + usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0); + } + + if (MUSB_INTR_SOF & intrusb) { + usbd_device_event_irq(udc_device, + DEVICE_BUS_ACTIVITY, 0); + musb_peri_resume(); + } + + if (MUSB_INTR_SUSPEND & intrusb) { + usbd_device_event_irq(udc_device, + DEVICE_BUS_INACTIVE, 0); + } + + if (ep0_state != SET_ADDRESS) { + u16 intrrx, intrtx; + + intrrx = readw(&musbr->intrrx); + intrtx = readw(&musbr->intrtx); + + if (intrrx) + musb_peri_rx(intrrx); + + if (intrtx) + musb_peri_tx(intrtx); + } else { + if (MUSB_INTR_SOF & intrusb) { + u8 faddr; + faddr = readb(&musbr->faddr); + /* + * Setting of the address can fail. + * Normally it succeeds the second time. + */ + if (udc_device->address != faddr) + musb_peri_ep0_set_address(); + } + } + } +} + +void udc_set_nak(int ep_num) +{ + /* noop */ +} + +void udc_unset_nak(int ep_num) +{ + /* noop */ +} + +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + int ret = 0; + + /* Transmit only if the hardware is available */ + if (endpoint->tx_urb && endpoint->state == 0) { + unsigned int ep = endpoint->endpoint_address & + USB_ENDPOINT_NUMBER_MASK; + + u16 peri_txcsr = readw(&musbr->ep[ep].epN.txcsr); + + /* Error conditions */ + if (peri_txcsr & MUSB_TXCSR_P_UNDERRUN) { + peri_txcsr &= ~MUSB_TXCSR_P_UNDERRUN; + writew(peri_txcsr, &musbr->ep[ep].epN.txcsr); + } + + if (debug_level > 1) + musb_print_txcsr(peri_txcsr); + + /* Check if a packet is waiting to be sent */ + if (!(peri_txcsr & MUSB_TXCSR_TXPKTRDY)) { + u32 length; + u8 *data; + struct urb *urb = endpoint->tx_urb; + unsigned int remaining_packet = urb->actual_length - + endpoint->sent; + + if (endpoint->tx_packetSize < remaining_packet) + length = endpoint->tx_packetSize; + else + length = remaining_packet; + + data = (u8 *) urb->buffer; + data += endpoint->sent; + + /* common musb fifo function */ + write_fifo(ep, length, data); + + musb_peri_tx_ready(ep); + + endpoint->last = length; + /* usbd_tx_complete will take care of updating 'sent' */ + usbd_tx_complete(endpoint); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s Problem with urb %p " + "or ep state %d\n", + __PRETTY_FUNCTION__, + endpoint->tx_urb, endpoint->state); + } + + return ret; +} + +void udc_setup_ep(struct usb_device_instance *device, unsigned int id, + struct usb_endpoint_instance *endpoint) +{ + if (0 == id) { + /* EP0 */ + ep0_endpoint = endpoint; + ep0_endpoint->endpoint_address = 0xff; + ep0_urb = usbd_alloc_urb(device, endpoint); + } else if (MAX_ENDPOINT >= id) { + int ep_addr; + + /* Check the direction */ + ep_addr = endpoint->endpoint_address; + if (USB_DIR_IN == (ep_addr & USB_ENDPOINT_DIR_MASK)) { + /* IN */ + epinfo[(id * 2) + 1].epsize = endpoint->tx_packetSize; + } else { + /* OUT */ + epinfo[id * 2].epsize = endpoint->rcv_packetSize; + } + + musb_configure_ep(&epinfo[0], + sizeof(epinfo) / sizeof(struct musb_epinfo)); + } else { + if (debug_level > 0) + serial_printf("ERROR : %s endpoint request %d " + "exceeds maximum %d\n", + __PRETTY_FUNCTION__, id, MAX_ENDPOINT); + } +} + +void udc_connect(void) +{ + /* noop */ +} + +void udc_disconnect(void) +{ + /* noop */ +} + +void udc_enable(struct usb_device_instance *device) +{ + /* Save the device structure pointer */ + udc_device = device; + + enabled = 1; +} + +void udc_disable(void) +{ + enabled = 0; +} + +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* + * The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* Resets the address to 0 */ + usbd_device_event_irq(device, DEVICE_RESET, 0); + + udc_enable(device); +} + +int udc_init(void) +{ + int ret; + int ep_loop; + + ret = musb_platform_init(); + if (ret < 0) + goto end; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + + /* Initialize the endpoints */ + for (ep_loop = 0; ep_loop < MAX_ENDPOINT * 2; ep_loop++) { + epinfo[ep_loop].epnum = (ep_loop / 2) + 1; + epinfo[ep_loop].epdir = ep_loop % 2; /* OUT, IN */ + epinfo[ep_loop].epsize = 0; + } + + musb_peri_softconnect(); + + ret = 0; +end: + + return ret; +} diff --git a/drivers/usb/musb/omap3.c b/drivers/usb/musb/omap3.c new file mode 100644 index 0000000..3e502e7 --- /dev/null +++ b/drivers/usb/musb/omap3.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix <Tom.Rix@windriver.com> + * + * This is file is based on + * repository git.gitorious.org/u-boot-omap3/mainline.git, + * branch omap3-dev-usb, file drivers/usb/host/omap3530_usb.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2009 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 + */ + +#include <twl4030.h> +#include "omap3.h" + +static int platform_needs_initialization = 1; + +struct musb_config musb_cfg = { + (struct musb_regs *)MENTOR_USB0_BASE, + OMAP3_USB_TIMEOUT, + 0 +}; + +/* + * OMAP3 USB OTG registers. + */ +struct omap3_otg_regs { + u32 revision; + u32 sysconfig; + u32 sysstatus; + u32 interfsel; + u32 simenable; + u32 forcestdby; +}; + +static struct omap3_otg_regs *otg; + +#define OMAP3_OTG_SYSCONFIG_SMART_STANDBY_MODE 0x2000 +#define OMAP3_OTG_SYSCONFIG_NO_STANDBY_MODE 0x1000 +#define OMAP3_OTG_SYSCONFIG_SMART_IDLE_MODE 0x0010 +#define OMAP3_OTG_SYSCONFIG_NO_IDLE_MODE 0x0008 +#define OMAP3_OTG_SYSCONFIG_ENABLEWAKEUP 0x0004 +#define OMAP3_OTG_SYSCONFIG_SOFTRESET 0x0002 +#define OMAP3_OTG_SYSCONFIG_AUTOIDLE 0x0001 + +#define OMAP3_OTG_SYSSTATUS_RESETDONE 0x0001 + +#define OMAP3_OTG_INTERFSEL_OMAP 0x0001 + +#define OMAP3_OTG_FORCESTDBY_STANDBY 0x0001 + + +#ifdef DEBUG_MUSB_OMAP3 +static void musb_db_otg_regs(void) +{ + u32 l; + l = readl(&otg->revision); + serial_printf("OTG_REVISION 0x%x\n", l); + l = readl(&otg->sysconfig); + serial_printf("OTG_SYSCONFIG 0x%x\n", l); + l = readl(&otg->sysstatus); + serial_printf("OTG_SYSSTATUS 0x%x\n", l); + l = readl(&otg->interfsel); + serial_printf("OTG_INTERFSEL 0x%x\n", l); + l = readl(&otg->forcestdby); + serial_printf("OTG_FORCESTDBY 0x%x\n", l); +} +#endif + +int musb_platform_init(void) +{ + int ret = -1; + + if (platform_needs_initialization) { + u32 stdby; + + if (twl4030_usb_ulpi_init()) { + serial_printf("ERROR: %s Could not initialize PHY\n", + __PRETTY_FUNCTION__); + goto end; + } + + otg = (struct omap3_otg_regs *)OMAP3_OTG_BASE; + + /* Set OTG to always be on */ + writel(OMAP3_OTG_SYSCONFIG_NO_STANDBY_MODE | + OMAP3_OTG_SYSCONFIG_NO_IDLE_MODE, &otg->sysconfig); + + /* Set the interface */ + writel(OMAP3_OTG_INTERFSEL_OMAP, &otg->interfsel); + + /* Clear force standby */ + stdby = readl(&otg->forcestdby); + stdby &= ~OMAP3_OTG_FORCESTDBY_STANDBY; + writel(stdby, &otg->forcestdby); + + platform_needs_initialization = 0; + } + + ret = platform_needs_initialization; +end: + return ret; + +} + +void musb_platform_deinit(void) +{ + /* noop */ +} diff --git a/drivers/usb/musb/omap3.h b/drivers/usb/musb/omap3.h new file mode 100644 index 0000000..20fc9d2 --- /dev/null +++ b/drivers/usb/musb/omap3.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix <Tom.Rix@windriver.com> + * + * This file is based on the file drivers/usb/musb/davinci.h + * + * This is the unique part of its copyright: + * + * -------------------------------------------------------------------- + * + * Copyright (c) 2008 Texas Instruments + * Author: Thomas Abraham t-abraham@ti.com, 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 + */ +#ifndef _MUSB_OMAP3_H_ +#define _MUSB_OMAP3_H_ + +#include "musb_core.h" + +/* Base address of MUSB registers */ +#define MENTOR_USB0_BASE (OMAP34XX_CORE_L4_IO_BASE + 0xAB000) + +/* Base address of OTG registers */ +#define OMAP3_OTG_BASE (MENTOR_USB0_BASE + 0x400) + +/* Timeout for USB module */ +#define OMAP3_USB_TIMEOUT 0x3FFFFFF + +int musb_platform_init(void); + +#endif /* _MUSB_OMAP3_H */ + |