diff options
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r-- | drivers/usb/musb/Makefile | 47 | ||||
-rw-r--r-- | drivers/usb/musb/davinci.c | 106 | ||||
-rw-r--r-- | drivers/usb/musb/davinci.h | 86 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 141 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 316 | ||||
-rw-r--r-- | drivers/usb/musb/musb_hcd.c | 792 | ||||
-rw-r--r-- | drivers/usb/musb/musb_hcd.h | 51 |
7 files changed, 1539 insertions, 0 deletions
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile new file mode 100644 index 0000000..09e0a5f --- /dev/null +++ b/drivers/usb/musb/Makefile @@ -0,0 +1,47 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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 $(TOPDIR)/config.mk + +LIB := $(obj)libusb_musb.a + +COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o +COBJS-$(CONFIG_USB_DAVINCI) += davinci.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c new file mode 100644 index 0000000..a7648fc --- /dev/null +++ b/drivers/usb/musb/davinci.c @@ -0,0 +1,106 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> +#include <asm/io.h> +#include "davinci.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/musb/davinci.h b/drivers/usb/musb/davinci.h new file mode 100644 index 0000000..f6751bf --- /dev/null +++ b/drivers/usb/musb/davinci.h @@ -0,0 +1,86 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __DAVINCI_USB_H__ +#define __DAVINCI_USB_H__ + +#include <asm/arch/hardware.h> +#include "musb_core.h" + +/* Base address of DAVINCI usb0 wrapper */ +#define DAVINCI_USB0_BASE 0x01C64000 + +/* Base address of DAVINCI musb core */ +#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400) + +/* + * Davinci platform USB wrapper register overlay. Note: Only the required + * registers are included in this structure. It can be expanded as required. + */ +struct davinci_usb_regs { + u32 version; + u32 ctrlr; + u32 reserved[0x20]; + u32 intclrr; + u32 intmskr; + u32 intmsksetr; +}; + +#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ +#define DAVINCI_USB_USBINT_SHIFT 16 +#define DAVINCI_USB_TXINT_SHIFT 0 +#define DAVINCI_USB_RXINT_SHIFT 8 +#define DAVINCI_INTR_DRVVBUS 0x0100 + +#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DAVINCI_USB_TXINT_MASK \ + (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) +#define DAVINCI_USB_RXINT_MASK \ + (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +/* Integrated highspeed/otg PHY */ +#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) +#define USBPHY_PHYCLKGD (1 << 8) +#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ +#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ +#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ +#define USBPHY_CLKO1SEL (1 << 3) +#define USBPHY_OSCPDWN (1 << 2) +#define USBPHY_PHYPDWN (1 << 0) + +/* Timeout for Davinci USB module */ +#define DAVINCI_USB_TIMEOUT 0x3FFFFFF + +/* IO Expander I2C address and VBUS enable mask */ +#define IOEXP_I2C_ADDR 0x3A +#define IOEXP_VBUSEN_MASK 1 + +/* extern functions */ +extern void lpsc_on(unsigned int id); +extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern void enable_vbus(void); +#endif /* __DAVINCI_USB_H__ */ diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c new file mode 100644 index 0000000..ec57fc8 --- /dev/null +++ b/drivers/usb/musb/musb_core.c @@ -0,0 +1,141 @@ +/* + * Mentor USB OTG Core functionality common for both Host and Device + * functionality. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> + +#include "musb_core.h" +struct musb_regs *musbr; + +/* + * program the mentor core to start (enable interrupts, dma, etc.) + */ +void musb_start(void) +{ + u8 devctl; + + /* disable all interrupts */ + writew(0, &musbr->intrtxe); + writew(0, &musbr->intrrxe); + writeb(0, &musbr->intrusbe); + writeb(0, &musbr->testmode); + + /* put into basic highspeed mode and start session */ + writeb(MUSB_POWER_HSENAB, &musbr->power); +#if defined(CONFIG_MUSB_HCD) + devctl = readb(&musbr->devctl); + writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl); +#endif +} + +/* + * This function configures the endpoint configuration. The musb hcd or musb + * device implementation can use this function to configure the endpoints + * and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints + * should not be more than the available FIFO size. + * + * epinfo - Pointer to EP configuration table + * cnt - Number of entries in the EP conf table. + */ +void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt) +{ + u16 csr; + u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */ + u32 fifosize; + u8 idx; + + while (cnt--) { + /* prepare fifosize to write to register */ + fifosize = epinfo->epsize >> 3; + idx = ffs(fifosize) - 1; + + writeb(epinfo->epnum, &musbr->index); + if (epinfo->epdir) { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->txfifosz); + writew(fifoaddr >> 3, &musbr->txfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_TXCSR_TXPKTRDY) + writew(csr | MUSB_TXCSR_FLUSHFIFO, + &musbr->txcsr); + } else { + /* Configure fifo size and fifo base address */ + writeb(idx, &musbr->rxfifosz); + writew(fifoaddr >> 3, &musbr->rxfifoadd); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + csr = readw(&musbr->rxcsr); + writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_RXCSR_RXPKTRDY) + writew(csr | MUSB_RXCSR_FLUSHFIFO, + &musbr->rxcsr); + } + fifoaddr += epinfo->epsize; + epinfo++; + } +} + +/* + * This function writes data to endpoint fifo + * + * ep - endpoint number + * length - number of bytes to write to FIFO + * fifo_data - Pointer to data buffer that contains the data to write + */ +void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* write the data to the fifo */ + while (length--) + writeb(*data++, &musbr->fifox[ep]); +} + +/* + * This function reads data from endpoint fifo + * + * ep - endpoint number + * length - number of bytes to read from FIFO + * fifo_data - pointer to data buffer into which data is read + */ +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* read the data to the fifo */ + while (length--) + *data++ = readb(&musbr->fifox[ep]); +} diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h new file mode 100644 index 0000000..2597c5f --- /dev/null +++ b/drivers/usb/musb/musb_core.h @@ -0,0 +1,316 @@ +/****************************************************************** + * Copyright 2008 Mentor Graphics Corporation + * Copyright (C) 2008 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux is distributed in + * the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION + * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE + * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS + * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. + * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT + * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR + * GRAPHICS SUPPORT CUSTOMER. + ******************************************************************/ + +#ifndef __MUSB_HDRC_DEFS_H__ +#define __MUSB_HDRC_DEFS_H__ + +#include <usb.h> +#include <usb_defs.h> +#include <asm/io.h> + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* Mentor USB core register overlay structure */ +struct musb_regs { + /* common registers */ + u8 faddr; + u8 power; + u16 intrtx; + u16 intrrx; + u16 intrtxe; + u16 intrrxe; + u8 intrusb; + u8 intrusbe; + u16 frame; + u8 index; + u8 testmode; + /* indexed registers */ + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; + /* fifo */ + u32 fifox[16]; + /* OTG, dynamic FIFO, version & vendor registers */ + u8 devctl; + u8 reserved1; + u8 txfifosz; + u8 rxfifosz; + u16 txfifoadd; + u16 rxfifoadd; + u32 vcontrol; + u16 hwvers; + u16 reserved2[5]; + u8 epinfo; + u8 raminfo; + u8 linkinfo; + u8 vplen; + u8 hseof1; + u8 fseof1; + u8 lseof1; + u8 reserved3; + /* target address registers */ + struct musb_tar_regs { + u8 txfuncaddr; + u8 reserved0; + u8 txhubaddr; + u8 txhubport; + u8 rxfuncaddr; + u8 reserved1; + u8 rxhubaddr; + u8 rxhubport; + } tar[16]; +} __attribute((aligned(32))); + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 +#define MUSB_POWER_HSMODE_SHIFT 4 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_SPEED_HIGH 1 +#define MUSB_TYPE_SPEED_FULL 2 +#define MUSB_TYPE_SPEED_LOW 3 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_BULK 2 +#define MUSB_TYPE_PROTO_INTR 3 + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_MODE 0x2000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 +#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 +#define MUSB_S_RXCSR_H_DATATOGGLE 9 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + +/* Endpoint configuration information. Note: The value of endpoint fifo size + * element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other + * values are not supported + */ +struct musb_epinfo { + u8 epnum; /* endpoint number */ + u8 epdir; /* endpoint direction */ + u16 epsize; /* endpoint FIFO size */ +}; + +/* + * Platform specific MUSB configuration. Any platform using the musb + * functionality should create one instance of this structure in the + * platform specific file. + */ +struct musb_config { + struct musb_regs *regs; + u32 timeout; + u8 musb_speed; +}; + +/* externally defined data */ +extern struct musb_config musb_cfg; +extern struct musb_regs *musbr; + +/* exported functions */ +extern void musb_start(void); +extern void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt); +extern void write_fifo(u8 ep, u32 length, void *fifo_data); +extern void read_fifo(u8 ep, u32 length, void *fifo_data); + +/* extern functions */ +extern inline void musb_writew(u32 offset, u16 value); +extern inline void musb_writeb(u32 offset, u8 value); +extern inline u16 musb_readw(u32 offset); +extern inline u8 musb_readb(u32 offset); + +#endif /* __MUSB_HDRC_DEFS_H__ */ diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c new file mode 100644 index 0000000..352a0d4 --- /dev/null +++ b/drivers/usb/musb/musb_hcd.c @@ -0,0 +1,792 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> +#include "musb_hcd.h" + +/* MSC control transfers */ +#define USB_MSC_BBB_RESET 0xFF +#define USB_MSC_BBB_GET_MAX_LUN 0xFE + +/* Endpoint configuration information */ +static struct musb_epinfo epinfo[3] = { + {MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ + {MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In - 512 Bytes */ + {MUSB_INTR_EP, 0, 64} /* EP2 - Interrupt IN - 64 Bytes */ +}; + +/* + * This function writes the data toggle value. + */ +static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) +{ + u16 toggle = usb_gettoggle(dev, ep, dir_out); + u16 csr; + + if (dir_out) { + if (!toggle) + writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); + else { + csr = readw(&musbr->txcsr); + csr |= MUSB_TXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->txcsr); + csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); + writew(csr, &musbr->txcsr); + } + } else { + if (!toggle) + writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); + else { + csr = readw(&musbr->rxcsr); + csr |= MUSB_RXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->rxcsr); + csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); + writew(csr, &musbr->rxcsr); + } + } +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall + * has occured, the RxStall is cleared and 1 is returned. If RxStall has + * not occured, 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ + u16 csr; + + /* For endpoint 0 */ + if (!ep) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_RXSTALL) { + csr &= ~MUSB_CSR0_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* For non-ep0 */ + if (dir_out) { /* is it tx ep */ + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_RXSTALL) { + csr &= ~MUSB_TXCSR_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* is it rx ep */ + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_RXSTALL) { + csr &= ~MUSB_RXCSR_H_RXSTALL; + writew(csr, &musbr->rxcsr); + return 1; + } + } + } + return 0; +} + +/* + * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout + * error and -2 for stall. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u16 csr; + int result = 1; + + while (result > 0) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_ERROR) { + csr &= ~MUSB_CSR0_H_ERROR; + writew(csr, &musbr->txcsr); + dev->status = USB_ST_CRC_ERR; + result = -1; + break; + } + + switch (bit_mask) { + case MUSB_CSR0_TXPKTRDY: + if (!(csr & MUSB_CSR0_TXPKTRDY)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + + case MUSB_CSR0_RXPKTRDY: + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + if (csr & MUSB_CSR0_RXPKTRDY) + result = 0; + break; + + case MUSB_CSR0_H_REQPKT: + if (!(csr & MUSB_CSR0_H_REQPKT)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + } + } + return result; +} + +/* + * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_txep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 1)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (csr & MUSB_TXCSR_TXPKTRDY); + return 1; +} + +/* + * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_rxep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + + do { + if (check_stall(ep, 0)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + } while (!(csr & MUSB_RXCSR_RXPKTRDY)); + return 1; +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ + int result; + u16 csr; + + /* write the control request to ep0 fifo */ + write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); + + /* enable transfer of setup packet */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); + writew(csr, &musbr->txcsr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + dev->act_len = 0; + return result; +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 rxlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *rxbuff = (u8 *)buffer; + u8 rxedlength; + int result; + + while (rxlen < len) { + /* Determine the next read length */ + nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); + + /* Set the ReqPkt bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); + if (result < 0) + return result; + + /* Actual number of bytes received by usb */ + rxedlength = readb(&musbr->rxcount); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = readw(&musbr->txcsr); + csr &= ~MUSB_CSR0_RXPKTRDY; + writew(csr, &musbr->txcsr); + + /* short packet? */ + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + rxlen += nextlen; + dev->act_len = rxlen; + } + return 0; +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *txbuff = (u8 *)buffer; + int result = 0; + + while (txlen < len) { + /* Determine the next write length */ + nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); + + /* Load the data to send in FIFO */ + write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + + /* Set TXPKTRDY bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, + &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + if (result < 0) + break; + + txlen += nextlen; + dev->act_len = txlen; + } + return result; +} + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | + MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + return result; +} + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit and ReqPkt bit */ + csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; + writew(csr, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = readw(&musbr->txcsr); + csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + return result; +} + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + return (dev->speed & USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : + ((dev->speed & USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : + MUSB_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while (dev->parent->parent != NULL) + if (get_dev_speed(dev->parent) != MUSB_TYPE_SPEED_HIGH) + dev = dev->parent; + else + break; + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + + /* configure the hub address and the port address */ + writeb(hub, &musbr->tar[ep].txhubaddr); + writeb((chid + 1), &musbr->tar[ep].txhubport); + writeb(hub, &musbr->tar[ep].rxhubaddr); + writeb((chid + 1), &musbr->tar[ep].rxhubport); +} + +/* + * do a control transfer + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + u16 csr; + u8 devspeed; + + /* select control endpoint */ + writeb(MUSB_CONTROL_EP, &musbr->index); + csr = readw(&musbr->txcsr); + + /* target addr and (for multipoint) hub addr/port */ + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + config_hub_port(dev, MUSB_CONTROL_EP); + writeb(devspeed << 6, &musbr->txtype); + } else { + writeb(musb_cfg.musb_speed << 6, &musbr->txtype); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return 0; + + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_MSC_BBB_GET_MAX_LUN: + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_CLEAR_FEATURE: + case USB_MSC_BBB_RESET: + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_DESCRIPTOR: + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + default: + /* unhandled control transfer */ + return -1; + } + + dev->status = 0; + dev->act_len = len; + return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select bulk endpoint */ + writeb(MUSB_BULK_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_BULK_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (dir_out) { /* bulk-out transfer */ + /* Program the TxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->txtype); + + /* Write maximum packet size to the TxMaxp register */ + writew(dev->epmaxpacketout[ep], &musbr->txmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? + (len-txlen) : dev->epmaxpacketout[ep]; + + /* Write the data to the FIFO */ + write_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Set the TxPktRdy bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); + + /* Wait until the TxPktRdy bit is cleared */ + if (!wait_until_txep_ready(dev, MUSB_BULK_EP)) { + readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + dev->act_len = txlen; + return 0; + } + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + } else { /* bulk-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_BULK_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* bulk transfer is complete */ + dev->status = 0; + dev->act_len = len; + return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(void) +{ + u8 power; + u32 timeout; + + if (musb_platform_init() == -1) + return -1; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + musb_configure_ep(&epinfo[0], + sizeof(epinfo) / sizeof(struct musb_epinfo)); + musb_start(); + + /* + * Wait until musb is enabled in host mode with a timeout. There + * should be a usb device connected. + */ + timeout = musb_cfg.timeout; + while (timeout--) + if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) + break; + + /* if musb core is not in host mode, then return */ + if (!timeout) + return -1; + + /* start usb bus reset */ + power = readb(&musbr->power); + writeb(power | MUSB_POWER_RESET, &musbr->power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_RESET; + writeb(power, &musbr->power); + + /* Determine if the connected device is a high/full/low speed device */ + musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? + MUSB_TYPE_SPEED_HIGH : + ((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? + MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); + return 0; +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ + /* Reset the USB module */ + musb_platform_deinit(); + writeb(0, &musbr->devctl); + return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt + * transfers are not supported. + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select interrupt endpoint */ + writeb(MUSB_INTR_EP, &musbr->index); + + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_INTR_EP); + } else { + if (dir_out) { + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); + } + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (!dir_out) { /* intrrupt-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + writeb(interval, &musbr->rxinterval); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (!wait_until_rxep_ready(dev, MUSB_INTR_EP)) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_INTR_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* interrupt transfer is complete */ + dev->irq_status = 0; + dev->irq_act_len = len; + dev->irq_handle(dev); + dev->status = 0; + dev->act_len = len; + return 0; +} + + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ + device_t *dev; + struct usb_device *usb_kbd_dev; + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = device_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], + maxp > 8 ? 8 : maxp, ep->bInterval); +} +#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/musb/musb_hcd.h b/drivers/usb/musb/musb_hcd.h new file mode 100644 index 0000000..bb83311 --- /dev/null +++ b/drivers/usb/musb/musb_hcd.h @@ -0,0 +1,51 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __MUSB_HCD_H__ +#define __MUSB_HCD_H__ + +#include "musb_core.h" +#ifdef CONFIG_USB_KEYBOARD +#include <devices.h> +extern unsigned char new[]; +#endif + +/* This defines the endpoint number used for control transfers */ +#define MUSB_CONTROL_EP 0 + +/* This defines the endpoint number used for bulk transfer */ +#define MUSB_BULK_EP 1 + +/* This defines the endpoint number used for interrupt transfer */ +#define MUSB_INTR_EP 2 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ + >> MUSB_POWER_HSMODE_SHIFT) + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); + +#endif /* __MUSB_HCD_H__ */ |