Merge branch 'master' of git://git.denx.de/u-boot-usb
This commit is contained in:
commit
6b7243aa89
@ -52,6 +52,7 @@ int board_init(void)
|
||||
lpsc_on(DAVINCI_LPSC_UART0);
|
||||
lpsc_on(DAVINCI_LPSC_TIMER1);
|
||||
lpsc_on(DAVINCI_LPSC_GPIO);
|
||||
lpsc_on(DAVINCI_LPSC_USB);
|
||||
|
||||
#if !defined(CONFIG_SYS_USE_DSPLINK)
|
||||
/* Powerup the DSP */
|
||||
@ -101,3 +102,26 @@ int misc_init_r(void)
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_DAVINCI
|
||||
|
||||
/* IO Expander I2C address and USB VBUS enable mask */
|
||||
#define IOEXP_I2C_ADDR 0x3A
|
||||
#define IOEXP_VBUSEN_MASK 1
|
||||
|
||||
/*
|
||||
* This function enables USB VBUS by writting to IO expander using I2C.
|
||||
* Note that the I2C is already initialized at this stage. This
|
||||
* function is used by davinci specific USB wrapper code.
|
||||
*/
|
||||
void enable_vbus(void)
|
||||
{
|
||||
uchar data; /* IO Expander data to enable VBUS */
|
||||
|
||||
/* Write to IO expander to enable VBUS */
|
||||
i2c_read(IOEXP_I2C_ADDR, 0, 0, &data, 1);
|
||||
data &= ~IOEXP_VBUSEN_MASK;
|
||||
i2c_write(IOEXP_I2C_ADDR, 0, 0, &data, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -264,6 +264,16 @@ void usb_display_config(struct usb_device *dev)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static inline char *portspeed(int speed)
|
||||
{
|
||||
if (speed == USB_SPEED_HIGH)
|
||||
return "480 Mb/s";
|
||||
else if (speed == USB_SPEED_LOW)
|
||||
return "1.5 Mb/s";
|
||||
else
|
||||
return "12 Mb/s";
|
||||
}
|
||||
|
||||
/* shows the device tree recursively */
|
||||
void usb_show_tree_graph(struct usb_device *dev, char *pre)
|
||||
{
|
||||
@ -310,7 +320,7 @@ void usb_show_tree_graph(struct usb_device *dev, char *pre)
|
||||
pre[index] = 0;
|
||||
printf(" %s (%s, %dmA)\n", usb_get_class_desc(
|
||||
dev->config.if_desc[0].bInterfaceClass),
|
||||
dev->slow ? "1.5MBit/s" : "12MBit/s",
|
||||
portspeed(dev->speed),
|
||||
dev->config.MaxPower * 2);
|
||||
if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial))
|
||||
printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial);
|
||||
|
35
common/usb.c
35
common/usb.c
@ -681,7 +681,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
|
||||
err = usb_string_sub(dev, 0, 0, tbuf);
|
||||
if (err < 0) {
|
||||
USB_PRINTF("error getting string descriptor 0 " \
|
||||
"(error=%x)\n", dev->status);
|
||||
"(error=%lx)\n", dev->status);
|
||||
return -1;
|
||||
} else if (tbuf[0] < 4) {
|
||||
USB_PRINTF("string descriptor 0 too short\n");
|
||||
@ -939,8 +939,10 @@ void usb_scan_devices(void)
|
||||
dev_index = 0;
|
||||
/* device 0 is always present (root hub, so let it analyze) */
|
||||
dev = usb_alloc_new_device();
|
||||
usb_new_device(dev);
|
||||
printf("%d USB Device(s) found\n", dev_index);
|
||||
if (usb_new_device(dev))
|
||||
printf("No USB Device found\n");
|
||||
else
|
||||
printf("%d USB Device(s) found\n", dev_index);
|
||||
/* insert "driver" if possible */
|
||||
#ifdef CONFIG_USB_KEYBOARD
|
||||
drv_usb_kbd_init();
|
||||
@ -1041,6 +1043,16 @@ struct usb_hub_device *usb_hub_allocate(void)
|
||||
|
||||
#define MAX_TRIES 5
|
||||
|
||||
static inline char *portspeed(int portstatus)
|
||||
{
|
||||
if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
|
||||
return "480 Mb/s";
|
||||
else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
|
||||
return "1.5 Mb/s";
|
||||
else
|
||||
return "12 Mb/s";
|
||||
}
|
||||
|
||||
static int hub_port_reset(struct usb_device *dev, int port,
|
||||
unsigned short *portstat)
|
||||
{
|
||||
@ -1061,10 +1073,11 @@ static int hub_port_reset(struct usb_device *dev, int port,
|
||||
}
|
||||
portstatus = le16_to_cpu(portsts.wPortStatus);
|
||||
portchange = le16_to_cpu(portsts.wPortChange);
|
||||
|
||||
USB_HUB_PRINTF("portstatus %x, change %x, %s\n",
|
||||
portstatus, portchange,
|
||||
portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? \
|
||||
"Low Speed" : "High Speed");
|
||||
portspeed(portstatus));
|
||||
|
||||
USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \
|
||||
" USB_PORT_STAT_ENABLE %d\n",
|
||||
(portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0,
|
||||
@ -1109,9 +1122,7 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
|
||||
portstatus = le16_to_cpu(portsts.wPortStatus);
|
||||
portchange = le16_to_cpu(portsts.wPortChange);
|
||||
USB_HUB_PRINTF("portstatus %x, change %x, %s\n",
|
||||
portstatus, portchange,
|
||||
portstatus&(1 << USB_PORT_FEAT_LOWSPEED) ? \
|
||||
"Low Speed" : "High Speed");
|
||||
portstatus, portchange, portspeed(portstatus));
|
||||
|
||||
/* Clear the connection change status */
|
||||
usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION);
|
||||
@ -1136,7 +1147,13 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
|
||||
|
||||
/* Allocate a new device struct for it */
|
||||
usb = usb_alloc_new_device();
|
||||
usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
|
||||
|
||||
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
|
||||
usb->speed = USB_SPEED_HIGH;
|
||||
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
|
||||
usb->speed = USB_SPEED_LOW;
|
||||
else
|
||||
usb->speed = USB_SPEED_FULL;
|
||||
|
||||
dev->children[port] = usb;
|
||||
usb->parent = dev;
|
||||
|
@ -183,6 +183,7 @@ int drv_usb_kbd_init(void)
|
||||
usb_kbd_dev.puts = NULL;
|
||||
usb_kbd_dev.getc = usb_kbd_getc;
|
||||
usb_kbd_dev.tstc = usb_kbd_testc;
|
||||
usb_kbd_dev.priv = (void *)dev;
|
||||
error = device_register (&usb_kbd_dev);
|
||||
if(error==0) {
|
||||
/* check if this is the standard input device */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,9 +29,7 @@
|
||||
*/
|
||||
/*
|
||||
* IMPORTANT NOTES
|
||||
* 1 - you MUST define LITTLEENDIAN in the configuration file for the
|
||||
* board or this driver will NOT work!
|
||||
* 2 - this driver is intended for use with USB Mass Storage Devices
|
||||
* 1 - this driver is intended for use with USB Mass Storage Devices
|
||||
* (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes!
|
||||
*/
|
||||
|
||||
|
@ -27,9 +27,7 @@
|
||||
*/
|
||||
/*
|
||||
* IMPORTANT NOTES
|
||||
* 1 - you MUST define LITTLEENDIAN in the configuration file for the
|
||||
* board or this driver will NOT work!
|
||||
* 2 - this driver is intended for use with USB Mass Storage Devices
|
||||
* 1 - this driver is intended for use with USB Mass Storage Devices
|
||||
* (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes!
|
||||
*/
|
||||
|
||||
@ -56,7 +54,7 @@
|
||||
#define USBH_ENABLE_CE (1<<3)
|
||||
#define USBH_ENABLE_RD (1<<4)
|
||||
|
||||
#ifdef LITTLEENDIAN
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
|
||||
#else
|
||||
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE)
|
||||
|
@ -28,11 +28,18 @@ LIB := $(obj)libusb.a
|
||||
# core
|
||||
COBJS-y += usbdcore.o
|
||||
COBJS-$(CONFIG_USB_OHCI_NEW) += usb_ohci.o
|
||||
COBJS-$(CONFIG_USB_EHCI) += usb_ehci_core.o
|
||||
|
||||
# host
|
||||
COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
|
||||
COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
|
||||
COBJS-$(CONFIG_USB_SL811HS) += sl811_usb.o
|
||||
COBJS-$(CONFIG_USB_EHCI_FSL) += usb_ehci_fsl.o
|
||||
COBJS-$(CONFIG_USB_EHCI_PCI) += usb_ehci_pci.o
|
||||
COBJS-$(CONFIG_USB_EHCI_IXP4XX) += usb_ehci_ixp.o
|
||||
COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o
|
||||
COBJS-$(CONFIG_USB_DAVINCI) += davinci_usb.o
|
||||
COBJS-$(CONFIG_USB_EHCI_VCT) += usb_ehci_vct.o
|
||||
|
||||
# device
|
||||
ifdef CONFIG_USB_DEVICE
|
||||
|
106
drivers/usb/davinci_usb.c
Normal file
106
drivers/usb/davinci_usb.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* TI's Davinci platform specific USB wrapper functions.
|
||||
*
|
||||
* Copyright (c) 2008 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include "davinci_usb.h"
|
||||
|
||||
/* MUSB platform configuration */
|
||||
struct musb_config musb_cfg = {
|
||||
(struct musb_regs *)MENTOR_USB0_BASE,
|
||||
DAVINCI_USB_TIMEOUT,
|
||||
0
|
||||
};
|
||||
|
||||
/* MUSB module register overlay */
|
||||
struct davinci_usb_regs *dregs;
|
||||
|
||||
/*
|
||||
* Enable the USB phy
|
||||
*/
|
||||
static u8 phy_on(void)
|
||||
{
|
||||
u32 timeout;
|
||||
|
||||
/* Wait until the USB phy is turned on */
|
||||
writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR);
|
||||
timeout = musb_cfg.timeout;
|
||||
while (timeout--)
|
||||
if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD)
|
||||
return 1;
|
||||
|
||||
/* USB phy was not turned on */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the USB phy
|
||||
*/
|
||||
static void phy_off(void)
|
||||
{
|
||||
/* powerdown the on-chip PHY and its oscillator */
|
||||
writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs Davinci platform specific initialization for usb0.
|
||||
*/
|
||||
int musb_platform_init(void)
|
||||
{
|
||||
u32 revision;
|
||||
|
||||
/* enable USB VBUS */
|
||||
enable_vbus();
|
||||
|
||||
/* start the on-chip USB phy and its pll */
|
||||
if (!phy_on())
|
||||
return -1;
|
||||
|
||||
/* reset the controller */
|
||||
dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE;
|
||||
writel(1, &dregs->ctrlr);
|
||||
udelay(5000);
|
||||
|
||||
/* Returns zero if e.g. not clocked */
|
||||
revision = readl(&dregs->version);
|
||||
if (!revision)
|
||||
return -1;
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK |
|
||||
DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs Davinci platform specific deinitialization for usb0.
|
||||
*/
|
||||
void musb_platform_deinit(void)
|
||||
{
|
||||
/* Turn of the phy */
|
||||
phy_off();
|
||||
|
||||
/* flush any interrupts */
|
||||
writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK |
|
||||
DAVINCI_USB_RXINT_MASK , &dregs->intclrr);
|
||||
}
|
87
drivers/usb/davinci_usb.h
Normal file
87
drivers/usb/davinci_usb.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* TI's Davinci platform specific USB wrapper functions.
|
||||
*
|
||||
* Copyright (c) 2008 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
|
||||
*/
|
||||
|
||||
#ifndef __DAVINCI_USB_H__
|
||||
#define __DAVINCI_USB_H__
|
||||
|
||||
#include <asm/arch/hardware.h>
|
||||
#include "musb_core.h"
|
||||
|
||||
/* Base address of DAVINCI usb0 wrapper */
|
||||
#define DAVINCI_USB0_BASE 0x01C64000
|
||||
|
||||
/* Base address of DAVINCI musb core */
|
||||
#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400)
|
||||
|
||||
/*
|
||||
* Davinci platform USB wrapper register overlay. Note: Only the required
|
||||
* registers are included in this structure. It can be expanded as required.
|
||||
*/
|
||||
struct davinci_usb_regs {
|
||||
u32 version;
|
||||
u32 ctrlr;
|
||||
u32 reserved[0x20];
|
||||
u32 intclrr;
|
||||
u32 intmskr;
|
||||
u32 intmsksetr;
|
||||
};
|
||||
|
||||
#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */
|
||||
#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */
|
||||
#define DAVINCI_USB_USBINT_SHIFT 16
|
||||
#define DAVINCI_USB_TXINT_SHIFT 0
|
||||
#define DAVINCI_USB_RXINT_SHIFT 8
|
||||
#define DAVINCI_INTR_DRVVBUS 0x0100
|
||||
|
||||
#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */
|
||||
#define DAVINCI_USB_TXINT_MASK \
|
||||
(DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT)
|
||||
#define DAVINCI_USB_RXINT_MASK \
|
||||
(DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT)
|
||||
#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \
|
||||
(0x80 + (8*(_bEnd)) + (_bOffset))
|
||||
|
||||
/* Integrated highspeed/otg PHY */
|
||||
#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34)
|
||||
#define USBPHY_PHYCLKGD (1 << 8)
|
||||
#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */
|
||||
#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */
|
||||
#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */
|
||||
#define USBPHY_CLKO1SEL (1 << 3)
|
||||
#define USBPHY_OSCPDWN (1 << 2)
|
||||
#define USBPHY_PHYPDWN (1 << 0)
|
||||
|
||||
/* Timeout for Davinci USB module */
|
||||
#define DAVINCI_USB_TIMEOUT 0x3FFFFFF
|
||||
|
||||
/* IO Expander I2C address and VBUS enable mask */
|
||||
#define IOEXP_I2C_ADDR 0x3A
|
||||
#define IOEXP_VBUSEN_MASK 1
|
||||
|
||||
/* extern functions */
|
||||
extern void lpsc_on(unsigned int id);
|
||||
extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len);
|
||||
extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len);
|
||||
extern void enable_vbus(void);
|
||||
#endif /* __DAVINCI_USB_H__ */
|
||||
|
141
drivers/usb/musb_core.c
Normal file
141
drivers/usb/musb_core.c
Normal file
@ -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]);
|
||||
}
|
317
drivers/usb/musb_core.h
Normal file
317
drivers/usb/musb_core.h
Normal file
@ -0,0 +1,317 @@
|
||||
/******************************************************************
|
||||
* Copyright 2008 Mentor Graphics Corporation
|
||||
* Copyright (C) 2008 by Texas Instruments
|
||||
*
|
||||
* This file is part of the Inventra Controller Driver for Linux.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is distributed in
|
||||
* the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with The Inventra Controller Driver for Linux ; if not,
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
* Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION
|
||||
* OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE
|
||||
* OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS
|
||||
* MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER.
|
||||
* MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT
|
||||
* SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR
|
||||
* GRAPHICS SUPPORT CUSTOMER.
|
||||
******************************************************************/
|
||||
|
||||
#ifndef __MUSB_HDRC_DEFS_H__
|
||||
#define __MUSB_HDRC_DEFS_H__
|
||||
|
||||
#include <usb.h>
|
||||
#include <usb_defs.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */
|
||||
|
||||
/* Mentor USB core register overlay structure */
|
||||
struct musb_regs {
|
||||
/* common registers */
|
||||
u8 faddr;
|
||||
u8 power;
|
||||
u16 intrtx;
|
||||
u16 intrrx;
|
||||
u16 intrtxe;
|
||||
u16 intrrxe;
|
||||
u8 intrusb;
|
||||
u8 intrusbe;
|
||||
u16 frame;
|
||||
u8 index;
|
||||
u8 testmode;
|
||||
/* indexed registers */
|
||||
u16 txmaxp;
|
||||
u16 txcsr;
|
||||
u16 rxmaxp;
|
||||
u16 rxcsr;
|
||||
u16 rxcount;
|
||||
u8 txtype;
|
||||
u8 txinterval;
|
||||
u8 rxtype;
|
||||
u8 rxinterval;
|
||||
u8 reserved0;
|
||||
u8 fifosize;
|
||||
/* fifo */
|
||||
u32 fifox[16];
|
||||
/* OTG, dynamic FIFO, version & vendor registers */
|
||||
u8 devctl;
|
||||
u8 reserved1;
|
||||
u8 txfifosz;
|
||||
u8 rxfifosz;
|
||||
u16 txfifoadd;
|
||||
u16 rxfifoadd;
|
||||
u32 vcontrol;
|
||||
u16 hwvers;
|
||||
u16 reserved2[5];
|
||||
u8 epinfo;
|
||||
u8 raminfo;
|
||||
u8 linkinfo;
|
||||
u8 vplen;
|
||||
u8 hseof1;
|
||||
u8 fseof1;
|
||||
u8 lseof1;
|
||||
u8 reserved3;
|
||||
/* target address registers */
|
||||
struct musb_tar_regs {
|
||||
u8 txfuncaddr;
|
||||
u8 reserved0;
|
||||
u8 txhubaddr;
|
||||
u8 txhubport;
|
||||
u8 rxfuncaddr;
|
||||
u8 reserved1;
|
||||
u8 rxhubaddr;
|
||||
u8 rxhubport;
|
||||
} tar[16];
|
||||
} __attribute((aligned(32)));
|
||||
|
||||
/*
|
||||
* MUSB Register bits
|
||||
*/
|
||||
|
||||
/* POWER */
|
||||
#define MUSB_POWER_ISOUPDATE 0x80
|
||||
#define MUSB_POWER_SOFTCONN 0x40
|
||||
#define MUSB_POWER_HSENAB 0x20
|
||||
#define MUSB_POWER_HSMODE 0x10
|
||||
#define MUSB_POWER_RESET 0x08
|
||||
#define MUSB_POWER_RESUME 0x04
|
||||
#define MUSB_POWER_SUSPENDM 0x02
|
||||
#define MUSB_POWER_ENSUSPEND 0x01
|
||||
#define MUSB_POWER_HSMODE_SHIFT 4
|
||||
|
||||
/* INTRUSB */
|
||||
#define MUSB_INTR_SUSPEND 0x01
|
||||
#define MUSB_INTR_RESUME 0x02
|
||||
#define MUSB_INTR_RESET 0x04
|
||||
#define MUSB_INTR_BABBLE 0x04
|
||||
#define MUSB_INTR_SOF 0x08
|
||||
#define MUSB_INTR_CONNECT 0x10
|
||||
#define MUSB_INTR_DISCONNECT 0x20
|
||||
#define MUSB_INTR_SESSREQ 0x40
|
||||
#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */
|
||||
|
||||
/* DEVCTL */
|
||||
#define MUSB_DEVCTL_BDEVICE 0x80
|
||||
#define MUSB_DEVCTL_FSDEV 0x40
|
||||
#define MUSB_DEVCTL_LSDEV 0x20
|
||||
#define MUSB_DEVCTL_VBUS 0x18
|
||||
#define MUSB_DEVCTL_VBUS_SHIFT 3
|
||||
#define MUSB_DEVCTL_HM 0x04
|
||||
#define MUSB_DEVCTL_HR 0x02
|
||||
#define MUSB_DEVCTL_SESSION 0x01
|
||||
|
||||
/* TESTMODE */
|
||||
#define MUSB_TEST_FORCE_HOST 0x80
|
||||
#define MUSB_TEST_FIFO_ACCESS 0x40
|
||||
#define MUSB_TEST_FORCE_FS 0x20
|
||||
#define MUSB_TEST_FORCE_HS 0x10
|
||||
#define MUSB_TEST_PACKET 0x08
|
||||
#define MUSB_TEST_K 0x04
|
||||
#define MUSB_TEST_J 0x02
|
||||
#define MUSB_TEST_SE0_NAK 0x01
|
||||
|
||||
/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */
|
||||
#define MUSB_FIFOSZ_DPB 0x10
|
||||
/* Allocation size (8, 16, 32, ... 4096) */
|
||||
#define MUSB_FIFOSZ_SIZE 0x0f
|
||||
|
||||
/* CSR0 */
|
||||
#define MUSB_CSR0_FLUSHFIFO 0x0100
|
||||
#define MUSB_CSR0_TXPKTRDY 0x0002
|
||||
#define MUSB_CSR0_RXPKTRDY 0x0001
|
||||
|
||||
/* CSR0 in Peripheral mode */
|
||||
#define MUSB_CSR0_P_SVDSETUPEND 0x0080
|
||||
#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040
|
||||
#define MUSB_CSR0_P_SENDSTALL 0x0020
|
||||
#define MUSB_CSR0_P_SETUPEND 0x0010
|
||||
#define MUSB_CSR0_P_DATAEND 0x0008
|
||||
#define MUSB_CSR0_P_SENTSTALL 0x0004
|
||||
|
||||
/* CSR0 in Host mode */
|
||||
#define MUSB_CSR0_H_DIS_PING 0x0800
|
||||
#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */
|
||||
#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */
|
||||
#define MUSB_CSR0_H_NAKTIMEOUT 0x0080
|
||||
#define MUSB_CSR0_H_STATUSPKT 0x0040
|
||||
#define MUSB_CSR0_H_REQPKT 0x0020
|
||||
#define MUSB_CSR0_H_ERROR 0x0010
|
||||
#define MUSB_CSR0_H_SETUPPKT 0x0008
|
||||
#define MUSB_CSR0_H_RXSTALL 0x0004
|
||||
|
||||
/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */
|
||||
#define MUSB_CSR0_P_WZC_BITS \
|
||||
(MUSB_CSR0_P_SENTSTALL)
|
||||
#define MUSB_CSR0_H_WZC_BITS \
|
||||
(MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \
|
||||
| MUSB_CSR0_RXPKTRDY)
|
||||
|
||||
/* TxType/RxType */
|
||||
#define MUSB_TYPE_SPEED 0xc0
|
||||
#define MUSB_TYPE_SPEED_SHIFT 6
|
||||
#define MUSB_TYPE_SPEED_HIGH 1
|
||||
#define MUSB_TYPE_SPEED_FULL 2
|
||||
#define MUSB_TYPE_SPEED_LOW 3
|
||||
#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */
|
||||
#define MUSB_TYPE_PROTO_SHIFT 4
|
||||
#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */
|
||||
#define MUSB_TYPE_PROTO_BULK 2
|
||||
#define MUSB_TYPE_PROTO_INTR 3
|
||||
|
||||
/* CONFIGDATA */
|
||||
#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */
|
||||
#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */
|
||||
#define MUSB_CONFIGDATA_BIGENDIAN 0x20
|
||||
#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */
|
||||
#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */
|
||||
#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */
|
||||
#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */
|
||||
#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */
|
||||
|
||||
/* TXCSR in Peripheral and Host mode */
|
||||
#define MUSB_TXCSR_AUTOSET 0x8000
|
||||
#define MUSB_TXCSR_MODE 0x2000
|
||||
#define MUSB_TXCSR_DMAENAB 0x1000
|
||||
#define MUSB_TXCSR_FRCDATATOG 0x0800
|
||||
#define MUSB_TXCSR_DMAMODE 0x0400
|
||||
#define MUSB_TXCSR_CLRDATATOG 0x0040
|
||||
#define MUSB_TXCSR_FLUSHFIFO 0x0008
|
||||
#define MUSB_TXCSR_FIFONOTEMPTY 0x0002
|
||||
#define MUSB_TXCSR_TXPKTRDY 0x0001
|
||||
|
||||
/* TXCSR in Peripheral mode */
|
||||
#define MUSB_TXCSR_P_ISO 0x4000
|
||||
#define MUSB_TXCSR_P_INCOMPTX 0x0080
|
||||
#define MUSB_TXCSR_P_SENTSTALL 0x0020
|
||||
#define MUSB_TXCSR_P_SENDSTALL 0x0010
|
||||
#define MUSB_TXCSR_P_UNDERRUN 0x0004
|
||||
|
||||
/* TXCSR in Host mode */
|
||||
#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200
|
||||
#define MUSB_TXCSR_H_DATATOGGLE 0x0100
|
||||
#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080
|
||||
#define MUSB_TXCSR_H_RXSTALL 0x0020
|
||||
#define MUSB_TXCSR_H_ERROR 0x0004
|
||||
#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8
|
||||
|
||||
/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
|
||||
#define MUSB_TXCSR_P_WZC_BITS \
|
||||
(MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \
|
||||
| MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY)
|
||||
#define MUSB_TXCSR_H_WZC_BITS \
|
||||
(MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \
|
||||
| MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY)
|
||||
|
||||
/* RXCSR in Peripheral and Host mode */
|
||||
#define MUSB_RXCSR_AUTOCLEAR 0x8000
|
||||
#define MUSB_RXCSR_DMAENAB 0x2000
|
||||
#define MUSB_RXCSR_DISNYET 0x1000
|
||||
#define MUSB_RXCSR_PID_ERR 0x1000
|
||||
#define MUSB_RXCSR_DMAMODE 0x0800
|
||||
#define MUSB_RXCSR_INCOMPRX 0x0100
|
||||
#define MUSB_RXCSR_CLRDATATOG 0x0080
|
||||
#define MUSB_RXCSR_FLUSHFIFO 0x0010
|
||||
#define MUSB_RXCSR_DATAERROR 0x0008
|
||||
#define MUSB_RXCSR_FIFOFULL 0x0002
|
||||
#define MUSB_RXCSR_RXPKTRDY 0x0001
|
||||
|
||||
/* RXCSR in Peripheral mode */
|
||||
#define MUSB_RXCSR_P_ISO 0x4000
|
||||
#define MUSB_RXCSR_P_SENTSTALL 0x0040
|
||||
#define MUSB_RXCSR_P_SENDSTALL 0x0020
|
||||
#define MUSB_RXCSR_P_OVERRUN 0x0004
|
||||
|
||||
/* RXCSR in Host mode */
|
||||
#define MUSB_RXCSR_H_AUTOREQ 0x4000
|
||||
#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400
|
||||
#define MUSB_RXCSR_H_DATATOGGLE 0x0200
|
||||
#define MUSB_RXCSR_H_RXSTALL 0x0040
|
||||
#define MUSB_RXCSR_H_REQPKT 0x0020
|
||||
#define MUSB_RXCSR_H_ERROR 0x0004
|
||||
#define MUSB_S_RXCSR_H_DATATOGGLE 9
|
||||
|
||||
/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
|
||||
#define MUSB_RXCSR_P_WZC_BITS \
|
||||
(MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \
|
||||
| MUSB_RXCSR_RXPKTRDY)
|
||||
#define MUSB_RXCSR_H_WZC_BITS \
|
||||
(MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \
|
||||
| MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY)
|
||||
|
||||
/* HUBADDR */
|
||||
#define MUSB_HUBADDR_MULTI_TT 0x80
|
||||
|
||||
/* Endpoint configuration information. Note: The value of endpoint fifo size
|
||||
* element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other
|
||||
* values are not supported
|
||||
*/
|
||||
struct musb_epinfo {
|
||||
u8 epnum; /* endpoint number */
|
||||
u8 epdir; /* endpoint direction */
|
||||
u16 epsize; /* endpoint FIFO size */
|
||||
};
|
||||
|
||||
/*
|
||||
* Platform specific MUSB configuration. Any platform using the musb
|
||||
* functionality should create one instance of this structure in the
|
||||
* platform specific file.
|
||||
*/
|
||||
struct musb_config {
|
||||
struct musb_regs *regs;
|
||||
u32 timeout;
|
||||
u8 musb_speed;
|
||||
};
|
||||
|
||||
/* externally defined data */
|
||||
extern struct musb_config musb_cfg;
|
||||
extern struct musb_regs *musbr;
|
||||
|
||||
/* exported functions */
|
||||
extern void musb_start(void);
|
||||
extern void musb_configure_ep(struct musb_epinfo *epinfo, u8 cnt);
|
||||
extern void write_fifo(u8 ep, u32 length, void *fifo_data);
|
||||
extern void read_fifo(u8 ep, u32 length, void *fifo_data);
|
||||
|
||||
/* extern functions */
|
||||
extern inline void musb_writew(u32 offset, u16 value);
|
||||
extern inline void musb_writeb(u32 offset, u8 value);
|
||||
extern inline u16 musb_readw(u32 offset);
|
||||
extern inline u8 musb_readb(u32 offset);
|
||||
|
||||
#endif /* __MUSB_HDRC_DEFS_H__ */
|
||||
|
792
drivers/usb/musb_hcd.c
Normal file
792
drivers/usb/musb_hcd.c
Normal file
@ -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 */
|
51
drivers/usb/musb_hcd.h
Normal file
51
drivers/usb/musb_hcd.h
Normal file
@ -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__ */
|
194
drivers/usb/usb_ehci.h
Normal file
194
drivers/usb/usb_ehci.h
Normal file
@ -0,0 +1,194 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2008, Juniper Networks, Inc.
|
||||
* Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2 of
|
||||
* the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef USB_EHCI_H
|
||||
#define USB_EHCI_H
|
||||
|
||||
#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS)
|
||||
#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2
|
||||
#endif
|
||||
|
||||
/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
|
||||
#define DeviceRequest \
|
||||
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
|
||||
|
||||
#define DeviceOutRequest \
|
||||
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
|
||||
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
|
||||
|
||||
#define EndpointRequest \
|
||||
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
|
||||
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
|
||||
|
||||
/*
|
||||
* Register Space.
|
||||
*/
|
||||
struct ehci_hccr {
|
||||
uint32_t cr_capbase;
|
||||
#define HC_LENGTH(p) (((p) >> 0) & 0x00ff)
|
||||
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
|
||||
uint32_t cr_hcsparams;
|
||||
#define HCS_PPC(p) ((p) & (1 << 4))
|
||||
#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */
|
||||
#define HCS_N_PORTS(p) (((p) >> 0) & 0xf)
|
||||
uint32_t cr_hccparams;
|
||||
uint8_t cr_hcsp_portrt[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ehci_hcor {
|
||||
uint32_t or_usbcmd;
|
||||
#define CMD_PARK (1 << 11) /* enable "park" */
|
||||
#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */
|
||||
#define CMD_ASE (1 << 5) /* async schedule enable */
|
||||
#define CMD_LRESET (1 << 7) /* partial reset */
|
||||
#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */
|
||||
#define CMD_PSE (1 << 4) /* periodic schedule enable */
|
||||
#define CMD_RESET (1 << 1) /* reset HC not bus */
|
||||
#define CMD_RUN (1 << 0) /* start/stop HC */
|
||||
uint32_t or_usbsts;
|
||||
#define STD_ASS (1 << 15)
|
||||
#define STS_HALT (1 << 12)
|
||||
uint32_t or_usbintr;
|
||||
uint32_t or_frindex;
|
||||
uint32_t or_ctrldssegment;
|
||||
uint32_t or_periodiclistbase;
|
||||
uint32_t or_asynclistaddr;
|
||||
uint32_t _reserved_[9];
|
||||
uint32_t or_configflag;
|
||||
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
|
||||
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
|
||||
uint32_t or_systune;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define USBMODE 0x68 /* USB Device mode */
|
||||
#define USBMODE_SDIS (1 << 3) /* Stream disable */
|
||||
#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */
|
||||
#define USBMODE_CM_HC (3 << 0) /* host controller mode */
|
||||
#define USBMODE_CM_IDLE (0 << 0) /* idle state */
|
||||
|
||||
/* Interface descriptor */
|
||||
struct usb_linux_interface_descriptor {
|
||||
unsigned char bLength;
|
||||
unsigned char bDescriptorType;
|
||||
unsigned char bInterfaceNumber;
|
||||
unsigned char bAlternateSetting;
|
||||
unsigned char bNumEndpoints;
|
||||
unsigned char bInterfaceClass;
|
||||
unsigned char bInterfaceSubClass;
|
||||
unsigned char bInterfaceProtocol;
|
||||
unsigned char iInterface;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Configuration descriptor information.. */
|
||||
struct usb_linux_config_descriptor {
|
||||
unsigned char bLength;
|
||||
unsigned char bDescriptorType;
|
||||
unsigned short wTotalLength;
|
||||
unsigned char bNumInterfaces;
|
||||
unsigned char bConfigurationValue;
|
||||
unsigned char iConfiguration;
|
||||
unsigned char bmAttributes;
|
||||
unsigned char MaxPower;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#if defined CONFIG_EHCI_DESC_BIG_ENDIAN
|
||||
#define ehci_readl(x) (*((volatile u32 *)(x)))
|
||||
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b))
|
||||
#else
|
||||
#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x))))
|
||||
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \
|
||||
cpu_to_le32(((volatile u32)b)))
|
||||
#endif
|
||||
|
||||
#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN
|
||||
#define hc32_to_cpu(x) be32_to_cpu((x))
|
||||
#define cpu_to_hc32(x) cpu_to_be32((x))
|
||||
#else
|
||||
#define hc32_to_cpu(x) le32_to_cpu((x))
|
||||
#define cpu_to_hc32(x) cpu_to_le32((x))
|
||||
#endif
|
||||
|
||||
#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */
|
||||
#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */
|
||||
#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */
|
||||
#define EHCI_PS_PO (1 << 13) /* RW port owner */
|
||||
#define EHCI_PS_PP (1 << 12) /* RW,RO port power */
|
||||
#define EHCI_PS_LS (3 << 10) /* RO line status */
|
||||
#define EHCI_PS_PR (1 << 8) /* RW port reset */
|
||||
#define EHCI_PS_SUSP (1 << 7) /* RW suspend */
|
||||
#define EHCI_PS_FPR (1 << 6) /* RW force port resume */
|
||||
#define EHCI_PS_OCC (1 << 5) /* RWC over current change */
|
||||
#define EHCI_PS_OCA (1 << 4) /* RO over current active */
|
||||
#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */
|
||||
#define EHCI_PS_PE (1 << 2) /* RW port enable */
|
||||
#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */
|
||||
#define EHCI_PS_CS (1 << 0) /* RO connect status */
|
||||
#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC)
|
||||
|
||||
#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10))
|
||||
|
||||
/*
|
||||
* Schedule Interface Space.
|
||||
*
|
||||
* IMPORTANT: Software must ensure that no interface data structure
|
||||
* reachable by the EHCI host controller spans a 4K page boundary!
|
||||
*
|
||||
* Periodic transfers (i.e. isochronous and interrupt transfers) are
|
||||
* not supported.
|
||||
*/
|
||||
|
||||
/* Queue Element Transfer Descriptor (qTD). */
|
||||
struct qTD {
|
||||
uint32_t qt_next;
|
||||
#define QT_NEXT_TERMINATE 1
|
||||
uint32_t qt_altnext;
|
||||
uint32_t qt_token;
|
||||
uint32_t qt_buffer[5];
|
||||
};
|
||||
|
||||
/* Queue Head (QH). */
|
||||
struct QH {
|
||||
uint32_t qh_link;
|
||||
#define QH_LINK_TERMINATE 1
|
||||
#define QH_LINK_TYPE_ITD 0
|
||||
#define QH_LINK_TYPE_QH 2
|
||||
#define QH_LINK_TYPE_SITD 4
|
||||
#define QH_LINK_TYPE_FSTN 6
|
||||
uint32_t qh_endpt1;
|
||||
uint32_t qh_endpt2;
|
||||
uint32_t qh_curtd;
|
||||
struct qTD qh_overlay;
|
||||
/*
|
||||
* Add dummy fill value to make the size of this struct
|
||||
* aligned to 32 bytes
|
||||
*/
|
||||
uint8_t fill[16];
|
||||
};
|
||||
|
||||
/* Low level init functions */
|
||||
int ehci_hcd_init(void);
|
||||
int ehci_hcd_stop(void);
|
||||
|
||||
#endif /* USB_EHCI_H */
|
880
drivers/usb/usb_ehci_core.c
Normal file
880
drivers/usb/usb_ehci_core.c
Normal file
@ -0,0 +1,880 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2008, Juniper Networks, Inc.
|
||||
* Copyright (c) 2008, Excito Elektronik i Skåne AB
|
||||
* Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2 of
|
||||
* the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
#include <malloc.h>
|
||||
#include "usb_ehci.h"
|
||||
|
||||
int rootdev;
|
||||
struct ehci_hccr *hccr; /* R/O registers, not need for volatile */
|
||||
volatile struct ehci_hcor *hcor;
|
||||
|
||||
static uint16_t portreset;
|
||||
static struct QH qh_list __attribute__((aligned(32)));
|
||||
|
||||
static struct descriptor {
|
||||
struct usb_hub_descriptor hub;
|
||||
struct usb_device_descriptor device;
|
||||
struct usb_linux_config_descriptor config;
|
||||
struct usb_linux_interface_descriptor interface;
|
||||
struct usb_endpoint_descriptor endpoint;
|
||||
} __attribute__ ((packed)) descriptor = {
|
||||
{
|
||||
0x8, /* bDescLength */
|
||||
0x29, /* bDescriptorType: hub descriptor */
|
||||
2, /* bNrPorts -- runtime modified */
|
||||
0, /* wHubCharacteristics */
|
||||
0xff, /* bPwrOn2PwrGood */
|
||||
0, /* bHubCntrCurrent */
|
||||
{}, /* Device removable */
|
||||
{} /* at most 7 ports! XXX */
|
||||
},
|
||||
{
|
||||
0x12, /* bLength */
|
||||
1, /* bDescriptorType: UDESC_DEVICE */
|
||||
0x0002, /* bcdUSB: v2.0 */
|
||||
9, /* bDeviceClass: UDCLASS_HUB */
|
||||
0, /* bDeviceSubClass: UDSUBCLASS_HUB */
|
||||
1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */
|
||||
64, /* bMaxPacketSize: 64 bytes */
|
||||
0x0000, /* idVendor */
|
||||
0x0000, /* idProduct */
|
||||
0x0001, /* bcdDevice */
|
||||
1, /* iManufacturer */
|
||||
2, /* iProduct */
|
||||
0, /* iSerialNumber */
|
||||
1 /* bNumConfigurations: 1 */
|
||||
},
|
||||
{
|
||||
0x9,
|
||||
2, /* bDescriptorType: UDESC_CONFIG */
|
||||
cpu_to_le16(0x19),
|
||||
1, /* bNumInterface */
|
||||
1, /* bConfigurationValue */
|
||||
0, /* iConfiguration */
|
||||
0x40, /* bmAttributes: UC_SELF_POWER */
|
||||
0 /* bMaxPower */
|
||||
},
|
||||
{
|
||||
0x9, /* bLength */
|
||||
4, /* bDescriptorType: UDESC_INTERFACE */
|
||||
0, /* bInterfaceNumber */
|
||||
0, /* bAlternateSetting */
|
||||
1, /* bNumEndpoints */
|
||||
9, /* bInterfaceClass: UICLASS_HUB */
|
||||
0, /* bInterfaceSubClass: UISUBCLASS_HUB */
|
||||
0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */
|
||||
0 /* iInterface */
|
||||
},
|
||||
{
|
||||
0x7, /* bLength */
|
||||
5, /* bDescriptorType: UDESC_ENDPOINT */
|
||||
0x81, /* bEndpointAddress:
|
||||
* UE_DIR_IN | EHCI_INTR_ENDPT
|
||||
*/
|
||||
3, /* bmAttributes: UE_INTERRUPT */
|
||||
8, 0, /* wMaxPacketSize */
|
||||
255 /* bInterval */
|
||||
},
|
||||
};
|
||||
|
||||
#if defined(CONFIG_EHCI_IS_TDI)
|
||||
#define ehci_is_TDI() (1)
|
||||
#else
|
||||
#define ehci_is_TDI() (0)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_EHCI_DCACHE)
|
||||
/*
|
||||
* Routines to handle (flush/invalidate) the dcache for the QH and qTD
|
||||
* structures and data buffers. This is needed on platforms using this
|
||||
* EHCI support with dcache enabled.
|
||||
*/
|
||||
static void flush_invalidate(u32 addr, int size, int flush)
|
||||
{
|
||||
if (flush)
|
||||
flush_dcache_range(addr, addr + size);
|
||||
else
|
||||
invalidate_dcache_range(addr, addr + size);
|
||||
}
|
||||
|
||||
static void cache_qtd(struct qTD *qtd, int flush)
|
||||
{
|
||||
u32 *ptr = (u32 *)qtd->qt_buffer[0];
|
||||
int len = (qtd->qt_token & 0x7fff0000) >> 16;
|
||||
|
||||
flush_invalidate((u32)qtd, sizeof(struct qTD), flush);
|
||||
if (ptr && len)
|
||||
flush_invalidate((u32)ptr, len, flush);
|
||||
}
|
||||
|
||||
|
||||
static inline struct QH *qh_addr(struct QH *qh)
|
||||
{
|
||||
return (struct QH *)((u32)qh & 0xffffffe0);
|
||||
}
|
||||
|
||||
static void cache_qh(struct QH *qh, int flush)
|
||||
{
|
||||
struct qTD *qtd;
|
||||
struct qTD *next;
|
||||
static struct qTD *first_qtd;
|
||||
|
||||
/*
|
||||
* Walk the QH list and flush/invalidate all entries
|
||||
*/
|
||||
while (1) {
|
||||
flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush);
|
||||
if ((u32)qh & QH_LINK_TYPE_QH)
|
||||
break;
|
||||
qh = qh_addr(qh);
|
||||
qh = (struct QH *)qh->qh_link;
|
||||
}
|
||||
qh = qh_addr(qh);
|
||||
|
||||
/*
|
||||
* Save first qTD pointer, needed for invalidating pass on this QH
|
||||
*/
|
||||
if (flush)
|
||||
first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay &
|
||||
0xffffffe0);
|
||||
else
|
||||
qtd = first_qtd;
|
||||
|
||||
/*
|
||||
* Walk the qTD list and flush/invalidate all entries
|
||||
*/
|
||||
while (1) {
|
||||
if (qtd == NULL)
|
||||
break;
|
||||
cache_qtd(qtd, flush);
|
||||
next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0);
|
||||
if (next == qtd)
|
||||
break;
|
||||
qtd = next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ehci_flush_dcache(struct QH *qh)
|
||||
{
|
||||
cache_qh(qh, 1);
|
||||
}
|
||||
|
||||
static inline void ehci_invalidate_dcache(struct QH *qh)
|
||||
{
|
||||
cache_qh(qh, 0);
|
||||
}
|
||||
#else /* CONFIG_EHCI_DCACHE */
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static inline void ehci_flush_dcache(struct QH *qh)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ehci_invalidate_dcache(struct QH *qh)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_EHCI_DCACHE */
|
||||
|
||||
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
|
||||
{
|
||||
uint32_t result;
|
||||
do {
|
||||
result = ehci_readl(ptr);
|
||||
if (result == ~(uint32_t)0)
|
||||
return -1;
|
||||
result &= mask;
|
||||
if (result == done)
|
||||
return 0;
|
||||
udelay(1);
|
||||
usec--;
|
||||
} while (usec > 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ehci_free(void *p, size_t sz)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int ehci_reset(void)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint32_t tmp;
|
||||
uint32_t *reg_ptr;
|
||||
int ret = 0;
|
||||
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
cmd |= CMD_RESET;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000);
|
||||
if (ret < 0) {
|
||||
printf("EHCI fail to reset\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ehci_is_TDI()) {
|
||||
reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE);
|
||||
tmp = ehci_readl(reg_ptr);
|
||||
tmp |= USBMODE_CM_HC;
|
||||
#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
|
||||
tmp |= USBMODE_BE;
|
||||
#endif
|
||||
ehci_writel(reg_ptr, tmp);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *ehci_alloc(size_t sz, size_t align)
|
||||
{
|
||||
static struct QH qh __attribute__((aligned(32)));
|
||||
static struct qTD td[3] __attribute__((aligned (32)));
|
||||
static int ntds;
|
||||
void *p;
|
||||
|
||||
switch (sz) {
|
||||
case sizeof(struct QH):
|
||||
p = &qh;
|
||||
ntds = 0;
|
||||
break;
|
||||
case sizeof(struct qTD):
|
||||
if (ntds == 3) {
|
||||
debug("out of TDs\n");
|
||||
return NULL;
|
||||
}
|
||||
p = &td[ntds];
|
||||
ntds++;
|
||||
break;
|
||||
default:
|
||||
debug("unknown allocation size\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(p, sz, 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
|
||||
{
|
||||
uint32_t addr, delta, next;
|
||||
int idx;
|
||||
|
||||
addr = (uint32_t) buf;
|
||||
idx = 0;
|
||||
while (idx < 5) {
|
||||
td->qt_buffer[idx] = cpu_to_hc32(addr);
|
||||
next = (addr + 4096) & ~4095;
|
||||
delta = next - addr;
|
||||
if (delta >= sz)
|
||||
break;
|
||||
sz -= delta;
|
||||
addr = next;
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (idx == 5) {
|
||||
debug("out of buffer pointers (%u bytes left)\n", sz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int length, struct devrequest *req)
|
||||
{
|
||||
struct QH *qh;
|
||||
struct qTD *td;
|
||||
volatile struct qTD *vtd;
|
||||
unsigned long ts;
|
||||
uint32_t *tdp;
|
||||
uint32_t endpt, token, usbsts;
|
||||
uint32_t c, toggle;
|
||||
uint32_t cmd;
|
||||
int ret = 0;
|
||||
|
||||
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
|
||||
buffer, length, req);
|
||||
if (req != NULL)
|
||||
debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
|
||||
req->request, req->request,
|
||||
req->requesttype, req->requesttype,
|
||||
le16_to_cpu(req->value), le16_to_cpu(req->value),
|
||||
le16_to_cpu(req->index));
|
||||
|
||||
qh = ehci_alloc(sizeof(struct QH), 32);
|
||||
if (qh == NULL) {
|
||||
debug("unable to allocate QH\n");
|
||||
return -1;
|
||||
}
|
||||
qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
|
||||
c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
|
||||
usb_pipeendpoint(pipe) == 0) ? 1 : 0;
|
||||
endpt = (8 << 28) |
|
||||
(c << 27) |
|
||||
(usb_maxpacket(dev, pipe) << 16) |
|
||||
(0 << 15) |
|
||||
(1 << 14) |
|
||||
(usb_pipespeed(pipe) << 12) |
|
||||
(usb_pipeendpoint(pipe) << 8) |
|
||||
(0 << 7) | (usb_pipedevice(pipe) << 0);
|
||||
qh->qh_endpt1 = cpu_to_hc32(endpt);
|
||||
endpt = (1 << 30) |
|
||||
(dev->portnr << 23) |
|
||||
(dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
|
||||
qh->qh_endpt2 = cpu_to_hc32(endpt);
|
||||
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
|
||||
td = NULL;
|
||||
tdp = &qh->qh_overlay.qt_next;
|
||||
|
||||
toggle =
|
||||
usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
|
||||
|
||||
if (req != NULL) {
|
||||
td = ehci_alloc(sizeof(struct qTD), 32);
|
||||
if (td == NULL) {
|
||||
debug("unable to allocate SETUP td\n");
|
||||
goto fail;
|
||||
}
|
||||
td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
token = (0 << 31) |
|
||||
(sizeof(*req) << 16) |
|
||||
(0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
|
||||
td->qt_token = cpu_to_hc32(token);
|
||||
if (ehci_td_buffer(td, req, sizeof(*req)) != 0) {
|
||||
debug("unable construct SETUP td\n");
|
||||
ehci_free(td, sizeof(*td));
|
||||
goto fail;
|
||||
}
|
||||
*tdp = cpu_to_hc32((uint32_t) td);
|
||||
tdp = &td->qt_next;
|
||||
toggle = 1;
|
||||
}
|
||||
|
||||
if (length > 0 || req == NULL) {
|
||||
td = ehci_alloc(sizeof(struct qTD), 32);
|
||||
if (td == NULL) {
|
||||
debug("unable to allocate DATA td\n");
|
||||
goto fail;
|
||||
}
|
||||
td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
token = (toggle << 31) |
|
||||
(length << 16) |
|
||||
((req == NULL ? 1 : 0) << 15) |
|
||||
(0 << 12) |
|
||||
(3 << 10) |
|
||||
((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
|
||||
td->qt_token = cpu_to_hc32(token);
|
||||
if (ehci_td_buffer(td, buffer, length) != 0) {
|
||||
debug("unable construct DATA td\n");
|
||||
ehci_free(td, sizeof(*td));
|
||||
goto fail;
|
||||
}
|
||||
*tdp = cpu_to_hc32((uint32_t) td);
|
||||
tdp = &td->qt_next;
|
||||
}
|
||||
|
||||
if (req != NULL) {
|
||||
td = ehci_alloc(sizeof(struct qTD), 32);
|
||||
if (td == NULL) {
|
||||
debug("unable to allocate ACK td\n");
|
||||
goto fail;
|
||||
}
|
||||
td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
token = (toggle << 31) |
|
||||
(0 << 16) |
|
||||
(1 << 15) |
|
||||
(0 << 12) |
|
||||
(3 << 10) |
|
||||
((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
|
||||
td->qt_token = cpu_to_hc32(token);
|
||||
*tdp = cpu_to_hc32((uint32_t) td);
|
||||
tdp = &td->qt_next;
|
||||
}
|
||||
|
||||
qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);
|
||||
|
||||
/* Flush dcache */
|
||||
ehci_flush_dcache(&qh_list);
|
||||
|
||||
usbsts = ehci_readl(&hcor->or_usbsts);
|
||||
ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));
|
||||
|
||||
/* Enable async. schedule. */
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
cmd |= CMD_ASE;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
|
||||
ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
|
||||
100 * 1000);
|
||||
if (ret < 0) {
|
||||
printf("EHCI fail timeout STD_ASS set\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Wait for TDs to be processed. */
|
||||
ts = get_timer(0);
|
||||
vtd = td;
|
||||
do {
|
||||
/* Invalidate dcache */
|
||||
ehci_invalidate_dcache(&qh_list);
|
||||
token = hc32_to_cpu(vtd->qt_token);
|
||||
if (!(token & 0x80))
|
||||
break;
|
||||
} while (get_timer(ts) < CONFIG_SYS_HZ);
|
||||
|
||||
/* Disable async schedule. */
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
cmd &= ~CMD_ASE;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
|
||||
ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
|
||||
100 * 1000);
|
||||
if (ret < 0) {
|
||||
printf("EHCI fail timeout STD_ASS reset\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
|
||||
|
||||
token = hc32_to_cpu(qh->qh_overlay.qt_token);
|
||||
if (!(token & 0x80)) {
|
||||
debug("TOKEN=%#x\n", token);
|
||||
switch (token & 0xfc) {
|
||||
case 0:
|
||||
toggle = token >> 31;
|
||||
usb_settoggle(dev, usb_pipeendpoint(pipe),
|
||||
usb_pipeout(pipe), toggle);
|
||||
dev->status = 0;
|
||||
break;
|
||||
case 0x40:
|
||||
dev->status = USB_ST_STALLED;
|
||||
break;
|
||||
case 0xa0:
|
||||
case 0x20:
|
||||
dev->status = USB_ST_BUF_ERR;
|
||||
break;
|
||||
case 0x50:
|
||||
case 0x10:
|
||||
dev->status = USB_ST_BABBLE_DET;
|
||||
break;
|
||||
default:
|
||||
dev->status = USB_ST_CRC_ERR;
|
||||
break;
|
||||
}
|
||||
dev->act_len = length - ((token >> 16) & 0x7fff);
|
||||
} else {
|
||||
dev->act_len = 0;
|
||||
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
|
||||
dev->devnum, ehci_readl(&hcor->or_usbsts),
|
||||
ehci_readl(&hcor->or_portsc[0]),
|
||||
ehci_readl(&hcor->or_portsc[1]));
|
||||
}
|
||||
|
||||
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
|
||||
|
||||
fail:
|
||||
td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
|
||||
while (td != (void *)QT_NEXT_TERMINATE) {
|
||||
qh->qh_overlay.qt_next = td->qt_next;
|
||||
ehci_free(td, sizeof(*td));
|
||||
td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
|
||||
}
|
||||
ehci_free(qh, sizeof(*qh));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int min3(int a, int b, int c)
|
||||
{
|
||||
|
||||
if (b < a)
|
||||
a = b;
|
||||
if (c < a)
|
||||
a = c;
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int length, struct devrequest *req)
|
||||
{
|
||||
uint8_t tmpbuf[4];
|
||||
u16 typeReq;
|
||||
void *srcptr = NULL;
|
||||
int len, srclen;
|
||||
uint32_t reg;
|
||||
uint32_t *status_reg;
|
||||
|
||||
if (le16_to_cpu(req->index) >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
|
||||
printf("The request port(%d) is not configured\n",
|
||||
le16_to_cpu(req->index) - 1);
|
||||
return -1;
|
||||
}
|
||||
status_reg = (uint32_t *)&hcor->or_portsc[
|
||||
le16_to_cpu(req->index) - 1];
|
||||
srclen = 0;
|
||||
|
||||
debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
|
||||
req->request, req->request,
|
||||
req->requesttype, req->requesttype,
|
||||
le16_to_cpu(req->value), le16_to_cpu(req->index));
|
||||
|
||||
typeReq = req->request << 8 | req->requesttype;
|
||||
|
||||
switch (le16_to_cpu(typeReq)) {
|
||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
||||
switch (le16_to_cpu(req->value) >> 8) {
|
||||
case USB_DT_DEVICE:
|
||||
debug("USB_DT_DEVICE request\n");
|
||||
srcptr = &descriptor.device;
|
||||
srclen = 0x12;
|
||||
break;
|
||||
case USB_DT_CONFIG:
|
||||
debug("USB_DT_CONFIG config\n");
|
||||
srcptr = &descriptor.config;
|
||||
srclen = 0x19;
|
||||
break;
|
||||
case USB_DT_STRING:
|
||||
debug("USB_DT_STRING config\n");
|
||||
switch (le16_to_cpu(req->value) & 0xff) {
|
||||
case 0: /* Language */
|
||||
srcptr = "\4\3\1\0";
|
||||
srclen = 4;
|
||||
break;
|
||||
case 1: /* Vendor */
|
||||
srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
|
||||
srclen = 14;
|
||||
break;
|
||||
case 2: /* Product */
|
||||
srcptr = "\52\3E\0H\0C\0I\0 "
|
||||
"\0H\0o\0s\0t\0 "
|
||||
"\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0";
|
||||
srclen = 42;
|
||||
break;
|
||||
default:
|
||||
debug("unknown value DT_STRING %x\n",
|
||||
le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("unknown value %x\n", le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8):
|
||||
switch (le16_to_cpu(req->value) >> 8) {
|
||||
case USB_DT_HUB:
|
||||
debug("USB_DT_HUB config\n");
|
||||
srcptr = &descriptor.hub;
|
||||
srclen = 0x8;
|
||||
break;
|
||||
default:
|
||||
debug("unknown value %x\n", le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8):
|
||||
debug("USB_REQ_SET_ADDRESS\n");
|
||||
rootdev = le16_to_cpu(req->value);
|
||||
break;
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||
debug("USB_REQ_SET_CONFIGURATION\n");
|
||||
/* Nothing to do */
|
||||
break;
|
||||
case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8):
|
||||
tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */
|
||||
tmpbuf[1] = 0;
|
||||
srcptr = tmpbuf;
|
||||
srclen = 2;
|
||||
break;
|
||||
case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
|
||||
memset(tmpbuf, 0, 4);
|
||||
reg = ehci_readl(status_reg);
|
||||
if (reg & EHCI_PS_CS)
|
||||
tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
|
||||
if (reg & EHCI_PS_PE)
|
||||
tmpbuf[0] |= USB_PORT_STAT_ENABLE;
|
||||
if (reg & EHCI_PS_SUSP)
|
||||
tmpbuf[0] |= USB_PORT_STAT_SUSPEND;
|
||||
if (reg & EHCI_PS_OCA)
|
||||
tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT;
|
||||
if (reg & EHCI_PS_PR &&
|
||||
(portreset & (1 << le16_to_cpu(req->index)))) {
|
||||
int ret;
|
||||
/* force reset to complete */
|
||||
reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR);
|
||||
ehci_writel(status_reg, reg);
|
||||
ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000);
|
||||
if (!ret)
|
||||
tmpbuf[0] |= USB_PORT_STAT_RESET;
|
||||
else
|
||||
printf("port(%d) reset error\n",
|
||||
le16_to_cpu(req->index) - 1);
|
||||
}
|
||||
if (reg & EHCI_PS_PP)
|
||||
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
|
||||
|
||||
if (ehci_is_TDI()) {
|
||||
switch ((reg >> 26) & 3) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
|
||||
}
|
||||
|
||||
if (reg & EHCI_PS_CSC)
|
||||
tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
|
||||
if (reg & EHCI_PS_PEC)
|
||||
tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
|
||||
if (reg & EHCI_PS_OCC)
|
||||
tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
|
||||
if (portreset & (1 << le16_to_cpu(req->index)))
|
||||
tmpbuf[2] |= USB_PORT_STAT_C_RESET;
|
||||
|
||||
srcptr = tmpbuf;
|
||||
srclen = 4;
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
|
||||
reg = ehci_readl(status_reg);
|
||||
reg &= ~EHCI_PS_CLEAR;
|
||||
switch (le16_to_cpu(req->value)) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
reg |= EHCI_PS_PE;
|
||||
ehci_writel(status_reg, reg);
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) {
|
||||
reg |= EHCI_PS_PP;
|
||||
ehci_writel(status_reg, reg);
|
||||
}
|
||||
break;
|
||||
case USB_PORT_FEAT_RESET:
|
||||
if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS &&
|
||||
!ehci_is_TDI() &&
|
||||
EHCI_PS_IS_LOWSPEED(reg)) {
|
||||
/* Low speed device, give up ownership. */
|
||||
debug("port %d low speed --> companion\n",
|
||||
req->index - 1);
|
||||
reg |= EHCI_PS_PO;
|
||||
ehci_writel(status_reg, reg);
|
||||
break;
|
||||
} else {
|
||||
reg |= EHCI_PS_PR;
|
||||
reg &= ~EHCI_PS_PE;
|
||||
ehci_writel(status_reg, reg);
|
||||
/*
|
||||
* caller must wait, then call GetPortStatus
|
||||
* usb 2.0 specification say 50 ms resets on
|
||||
* root
|
||||
*/
|
||||
wait_ms(50);
|
||||
portreset |= 1 << le16_to_cpu(req->index);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("unknown feature %x\n", le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
/* unblock posted writes */
|
||||
ehci_readl(&hcor->or_usbcmd);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
|
||||
reg = ehci_readl(status_reg);
|
||||
switch (le16_to_cpu(req->value)) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
reg &= ~EHCI_PS_PE;
|
||||
break;
|
||||
case USB_PORT_FEAT_C_ENABLE:
|
||||
reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE;
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams)))
|
||||
reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP);
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC;
|
||||
break;
|
||||
case USB_PORT_FEAT_OVER_CURRENT:
|
||||
reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
|
||||
break;
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
portreset &= ~(1 << le16_to_cpu(req->index));
|
||||
break;
|
||||
default:
|
||||
debug("unknown feature %x\n", le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
ehci_writel(status_reg, reg);
|
||||
/* unblock posted write */
|
||||
ehci_readl(&hcor->or_usbcmd);
|
||||
break;
|
||||
default:
|
||||
debug("Unknown request\n");
|
||||
goto unknown;
|
||||
}
|
||||
|
||||
wait_ms(1);
|
||||
len = min3(srclen, le16_to_cpu(req->length), length);
|
||||
if (srcptr != NULL && len > 0)
|
||||
memcpy(buffer, srcptr, len);
|
||||
else
|
||||
debug("Len is 0\n");
|
||||
|
||||
dev->act_len = len;
|
||||
dev->status = 0;
|
||||
return 0;
|
||||
|
||||
unknown:
|
||||
debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n",
|
||||
req->requesttype, req->request, le16_to_cpu(req->value),
|
||||
le16_to_cpu(req->index), le16_to_cpu(req->length));
|
||||
|
||||
dev->act_len = 0;
|
||||
dev->status = USB_ST_STALLED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_lowlevel_stop(void)
|
||||
{
|
||||
return ehci_hcd_stop();
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(void)
|
||||
{
|
||||
uint32_t reg;
|
||||
uint32_t cmd;
|
||||
|
||||
if (ehci_hcd_init() != 0)
|
||||
return -1;
|
||||
|
||||
/* EHCI spec section 4.1 */
|
||||
if (ehci_reset() != 0)
|
||||
return -1;
|
||||
|
||||
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
|
||||
if (ehci_hcd_init() != 0)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
/* Set head of reclaim list */
|
||||
memset(&qh_list, 0, sizeof(qh_list));
|
||||
qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
|
||||
qh_list.qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12));
|
||||
qh_list.qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
qh_list.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
qh_list.qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
|
||||
qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40);
|
||||
|
||||
/* Set async. queue head pointer. */
|
||||
ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list);
|
||||
|
||||
reg = ehci_readl(&hccr->cr_hcsparams);
|
||||
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
|
||||
printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
|
||||
/* Port Indicators */
|
||||
if (HCS_INDICATOR(reg))
|
||||
descriptor.hub.wHubCharacteristics |= 0x80;
|
||||
/* Port Power Control */
|
||||
if (HCS_PPC(reg))
|
||||
descriptor.hub.wHubCharacteristics |= 0x01;
|
||||
|
||||
/* Start the host controller. */
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
/* Philips, Intel, and maybe others need CMD_RUN before the
|
||||
* root hub will detect new devices (why?); NEC doesn't */
|
||||
cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
|
||||
cmd |= CMD_RUN;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
|
||||
/* take control over the ports */
|
||||
cmd = ehci_readl(&hcor->or_configflag);
|
||||
cmd |= FLAG_CF;
|
||||
ehci_writel(&hcor->or_configflag, cmd);
|
||||
/* unblock posted write */
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
wait_ms(5);
|
||||
reg = HC_VERSION(ehci_readl(&hccr->cr_capbase));
|
||||
printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
|
||||
|
||||
rootdev = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int length)
|
||||
{
|
||||
|
||||
if (usb_pipetype(pipe) != PIPE_BULK) {
|
||||
debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
|
||||
return -1;
|
||||
}
|
||||
return ehci_submit_async(dev, pipe, buffer, length, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int length, struct devrequest *setup)
|
||||
{
|
||||
|
||||
if (usb_pipetype(pipe) != PIPE_CONTROL) {
|
||||
debug("non-control pipe (type=%lu)", usb_pipetype(pipe));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (usb_pipedevice(pipe) == rootdev) {
|
||||
if (rootdev == 0)
|
||||
dev->speed = USB_SPEED_HIGH;
|
||||
return ehci_submit_root(dev, pipe, buffer, length, setup);
|
||||
}
|
||||
return ehci_submit_async(dev, pipe, buffer, length, setup);
|
||||
}
|
||||
|
||||
int
|
||||
submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int length, int interval)
|
||||
{
|
||||
|
||||
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
|
||||
dev, pipe, buffer, length, interval);
|
||||
return -1;
|
||||
}
|
29
drivers/usb/usb_ehci_core.h
Normal file
29
drivers/usb/usb_ehci_core.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2008, Juniper Networks, Inc.
|
||||
* Copyright (c) 2008, Excito Elektronik i Skåne AB
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2 of
|
||||
* the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef USB_EHCI_CORE_H
|
||||
#define USB_EHCI_CORE_H
|
||||
|
||||
extern int rootdev;
|
||||
extern struct ehci_hccr *hccr;
|
||||
extern volatile struct ehci_hcor *hcor;
|
||||
|
||||
#endif
|
100
drivers/usb/usb_ehci_fsl.c
Normal file
100
drivers/usb/usb_ehci_fsl.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
|
||||
*
|
||||
* Author: Tor Krill tor@excito.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
#include <mpc83xx.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/bitops.h>
|
||||
|
||||
#include "usb_ehci.h"
|
||||
#include "usb_ehci_fsl.h"
|
||||
#include "usb_ehci_core.h"
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*
|
||||
* Excerpts from linux ehci fsl driver.
|
||||
*/
|
||||
int ehci_hcd_init(void)
|
||||
{
|
||||
volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
|
||||
uint32_t addr, temp;
|
||||
|
||||
addr = (uint32_t)&(im->usb[0]);
|
||||
hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI);
|
||||
hcor = (struct ehci_hcor *)((uint32_t) hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
/* Configure clock */
|
||||
clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK,
|
||||
MPC83XX_SCCR_USB_DRCM_11);
|
||||
|
||||
/* Confgure interface. */
|
||||
temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
|
||||
out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp
|
||||
| REFSEL_16MHZ | UTMI_PHY_EN);
|
||||
|
||||
/* Wait for clock to stabilize */
|
||||
do {
|
||||
temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
|
||||
udelay(1000);
|
||||
} while (!(temp & PHY_CLK_VALID));
|
||||
|
||||
/* Set to Host mode */
|
||||
temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE));
|
||||
out_le32((void *)(addr + FSL_SOC_USB_USBMODE), temp | CM_HOST);
|
||||
|
||||
out_be32((void *)(addr + FSL_SOC_USB_SNOOP1), SNOOP_SIZE_2GB);
|
||||
out_be32((void *)(addr + FSL_SOC_USB_SNOOP2),
|
||||
0x80000000 | SNOOP_SIZE_2GB);
|
||||
|
||||
/* Init phy */
|
||||
/* TODO: handle different phys? */
|
||||
out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI);
|
||||
|
||||
/* Enable interface. */
|
||||
temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
|
||||
out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN);
|
||||
|
||||
out_be32((void *)(addr + FSL_SOC_USB_PRICTRL), 0x0000000c);
|
||||
out_be32((void *)(addr + FSL_SOC_USB_AGECNTTHRSH), 0x00000040);
|
||||
out_be32((void *)(addr + FSL_SOC_USB_SICTRL), 0x00000001);
|
||||
|
||||
/* Enable interface. */
|
||||
temp = in_be32((void *)(addr + FSL_SOC_USB_CTRL));
|
||||
out_be32((void *)(addr + FSL_SOC_USB_CTRL), temp | USB_EN);
|
||||
|
||||
temp = in_le32((void *)(addr + FSL_SOC_USB_USBMODE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
86
drivers/usb/usb_ehci_fsl.h
Normal file
86
drivers/usb/usb_ehci_fsl.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2005 freescale semiconductor
|
||||
* Copyright (c) 2005 MontaVista Software
|
||||
* Copyright (c) 2008 Excito Elektronik i Sk=E5ne AB
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _EHCI_FSL_H
|
||||
#define _EHCI_FSL_H
|
||||
|
||||
/* Global offsets */
|
||||
#define FSL_SKIP_PCI 0x100
|
||||
|
||||
/* offsets for the non-ehci registers in the FSL SOC USB controller */
|
||||
#define FSL_SOC_USB_ULPIVP 0x170
|
||||
#define FSL_SOC_USB_PORTSC1 0x184
|
||||
#define PORT_PTS_MSK (3 << 30)
|
||||
#define PORT_PTS_UTMI (0 << 30)
|
||||
#define PORT_PTS_ULPI (2 << 30)
|
||||
#define PORT_PTS_SERIAL (3 << 30)
|
||||
#define PORT_PTS_PTW (1 << 28)
|
||||
|
||||
/* USBMODE Register bits */
|
||||
#define CM_IDLE (0 << 0)
|
||||
#define CM_RESERVED (1 << 0)
|
||||
#define CM_DEVICE (2 << 0)
|
||||
#define CM_HOST (3 << 0)
|
||||
#define USBMODE_RESERVED_2 (0 << 2)
|
||||
#define SLOM (1 << 3)
|
||||
#define SDIS (1 << 4)
|
||||
|
||||
/* CONTROL Register bits */
|
||||
#define ULPI_INT_EN (1 << 0)
|
||||
#define WU_INT_EN (1 << 1)
|
||||
#define USB_EN (1 << 2)
|
||||
#define LSF_EN (1 << 3)
|
||||
#define KEEP_OTG_ON (1 << 4)
|
||||
#define OTG_PORT (1 << 5)
|
||||
#define REFSEL_12MHZ (0 << 6)
|
||||
#define REFSEL_16MHZ (1 << 6)
|
||||
#define REFSEL_48MHZ (2 << 6)
|
||||
#define PLL_RESET (1 << 8)
|
||||
#define UTMI_PHY_EN (1 << 9)
|
||||
#define PHY_CLK_SEL_UTMI (0 << 10)
|
||||
#define PHY_CLK_SEL_ULPI (1 << 10)
|
||||
#define CLKIN_SEL_USB_CLK (0 << 11)
|
||||
#define CLKIN_SEL_USB_CLK2 (1 << 11)
|
||||
#define CLKIN_SEL_SYS_CLK (2 << 11)
|
||||
#define CLKIN_SEL_SYS_CLK2 (3 << 11)
|
||||
#define RESERVED_18 (0 << 13)
|
||||
#define RESERVED_17 (0 << 14)
|
||||
#define RESERVED_16 (0 << 15)
|
||||
#define WU_INT (1 << 16)
|
||||
#define PHY_CLK_VALID (1 << 17)
|
||||
|
||||
#define FSL_SOC_USB_PORTSC2 0x188
|
||||
#define FSL_SOC_USB_USBMODE 0x1a8
|
||||
#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
|
||||
#define SNOOP_SIZE_2GB 0x1e
|
||||
|
||||
/* System Clock Control Register */
|
||||
#define MPC83XX_SCCR_USB_MASK 0x00f00000
|
||||
#define MPC83XX_SCCR_USB_DRCM_11 0x00300000
|
||||
#define MPC83XX_SCCR_USB_DRCM_01 0x00100000
|
||||
#define MPC83XX_SCCR_USB_DRCM_10 0x00200000
|
||||
|
||||
#endif /* _EHCI_FSL_H */
|
49
drivers/usb/usb_ehci_ixp.c
Normal file
49
drivers/usb/usb_ehci_ixp.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* (C) Copyright 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
|
||||
*
|
||||
* Author: Michael Trimarchi <trimarchimichael@yahoo.it>
|
||||
* This code is based on ehci freescale driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include "usb_ehci.h"
|
||||
#include "usb_ehci_core.h"
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(void)
|
||||
{
|
||||
hccr = (struct ehci_hccr *)(0xcd000100);
|
||||
hcor = (struct ehci_hcor *)((uint32_t) hccr
|
||||
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
printf("IXP4XX init hccr %x and hcor %x hc_length %d\n",
|
||||
(uint32_t)hccr, (uint32_t)hcor,
|
||||
(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
64
drivers/usb/usb_ehci_pci.c
Normal file
64
drivers/usb/usb_ehci_pci.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2008, Juniper Networks, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2 of
|
||||
* the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
#include "usb_ehci.h"
|
||||
#include "usb_ehci_core.h"
|
||||
|
||||
#ifdef CONFIG_PCI_EHCI_DEVICE
|
||||
static struct pci_device_id ehci_pci_ids[] = {
|
||||
/* Please add supported PCI EHCI controller ids here */
|
||||
{0, 0}
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(void)
|
||||
{
|
||||
pci_dev_t pdev;
|
||||
uint32_t addr;
|
||||
|
||||
pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
|
||||
if (pdev == -1) {
|
||||
printf("EHCI host controller not found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr);
|
||||
hccr = (struct ehci_hccr *)addr;
|
||||
hcor = (struct ehci_hcor *)((uint32_t) hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -36,7 +36,6 @@
|
||||
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
|
||||
#define CONFIG_S3C2410 1 /* in a SAMSUNG S3C2410 SoC */
|
||||
#define CONFIG_VCMA9 1 /* on a MPL VCMA9 Board */
|
||||
#define LITTLEENDIAN 1 /* used by usb_ohci.c */
|
||||
|
||||
/* input clock of PLL */
|
||||
#define CONFIG_SYS_CLK_FREQ 12000000/* VCMA9 has 12MHz input clock */
|
||||
|
@ -114,7 +114,6 @@
|
||||
|
||||
/* USB */
|
||||
#define CONFIG_USB_OHCI_NEW 1
|
||||
#define LITTLEENDIAN 1
|
||||
#define CONFIG_DOS_PARTITION 1
|
||||
#define CONFIG_SYS_USB_OHCI_CPU_INIT 1
|
||||
#define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00500000 /* AT91SAM9260_UHP_BASE */
|
||||
|
@ -131,7 +131,6 @@
|
||||
|
||||
/* USB */
|
||||
#define CONFIG_USB_OHCI_NEW 1
|
||||
#define LITTLEENDIAN 1
|
||||
#define CONFIG_DOS_PARTITION 1
|
||||
#define CONFIG_SYS_USB_OHCI_CPU_INIT 1
|
||||
#define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00700000 /* AT91_BASE_UHP */
|
||||
|
@ -116,7 +116,6 @@
|
||||
|
||||
/* USB */
|
||||
#define CONFIG_USB_OHCI_NEW 1
|
||||
#define LITTLEENDIAN 1
|
||||
#define CONFIG_DOS_PARTITION 1
|
||||
#define CONFIG_SYS_USB_OHCI_CPU_INIT 1
|
||||
#define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00500000 /* AT91SAM9260_UHP_BASE */
|
||||
|
@ -129,7 +129,6 @@
|
||||
|
||||
/* USB */
|
||||
#define CONFIG_USB_OHCI_NEW 1
|
||||
#define LITTLEENDIAN 1
|
||||
#define CONFIG_DOS_PARTITION 1
|
||||
#define CONFIG_SYS_USB_OHCI_CPU_INIT 1
|
||||
#define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00500000 /* AT91SAM9261_UHP_BASE */
|
||||
|
@ -136,7 +136,6 @@
|
||||
|
||||
/* USB */
|
||||
#define CONFIG_USB_OHCI_NEW 1
|
||||
#define LITTLEENDIAN 1
|
||||
#define CONFIG_DOS_PARTITION 1
|
||||
#define CONFIG_SYS_USB_OHCI_CPU_INIT 1
|
||||
#define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00a00000 /* AT91SAM9263_UHP_BASE */
|
||||
|
@ -171,6 +171,8 @@
|
||||
#define CONFIG_SYS_LONGHELP
|
||||
#define CONFIG_CRC32_VERIFY
|
||||
#define CONFIG_MX_CYCLIC
|
||||
#define CONFIG_MUSB_HCD
|
||||
#define CONFIG_USB_DAVINCI
|
||||
/*===================*/
|
||||
/* Linux Information */
|
||||
/*===================*/
|
||||
@ -203,6 +205,22 @@
|
||||
#else
|
||||
#error "Either CONFIG_SYS_USE_NAND or CONFIG_SYS_USE_NOR _MUST_ be defined !!!"
|
||||
#endif
|
||||
/*==========================*/
|
||||
/* USB MSC support (if any) */
|
||||
/*==========================*/
|
||||
#ifdef CONFIG_USB_DAVINCI
|
||||
#define CONFIG_CMD_USB
|
||||
#ifdef CONFIG_MUSB_HCD
|
||||
#define CONFIG_USB_STORAGE
|
||||
#define CONFIG_CMD_STORAGE
|
||||
#define CONFIG_CMD_FAT
|
||||
#define CONFIG_DOS_PARTITION
|
||||
#endif
|
||||
#ifdef CONFIG_USB_KEYBOARD
|
||||
#define CONFIG_SYS_USB_EVENT_POLL
|
||||
#define CONFIG_PREBOOT "usb start"
|
||||
#endif
|
||||
#endif
|
||||
/*=======================*/
|
||||
/* KGDB support (if any) */
|
||||
/*=======================*/
|
||||
|
@ -131,8 +131,6 @@
|
||||
#define CONFIG_SYS_USB_OHCI_SLOT_NAME "delta"
|
||||
#define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 3
|
||||
|
||||
#define LITTLEENDIAN 1 /* used by usb_ohci.c */
|
||||
|
||||
#define CONFIG_BOOTDELAY -1
|
||||
#define CONFIG_ETHADDR 08:00:3e:26:0a:5b
|
||||
#define CONFIG_NETMASK 255.255.0.0
|
||||
|
@ -216,7 +216,6 @@
|
||||
#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print Buffer Size */
|
||||
|
||||
#define CONFIG_SYS_DEVICE_DEREGISTER /* needs device_deregister */
|
||||
#define LITTLEENDIAN 1 /* used by usb_ohci.c */
|
||||
|
||||
#define CONFIG_SYS_HZ 1000
|
||||
#define CONFIG_SYS_HZ_CLOCK (AT91C_MASTER_CLOCK/2) /* AT91C_TC0_CMR is implicitly set to */
|
||||
|
@ -123,7 +123,6 @@
|
||||
#undef CONFIG_SYS_DIRECT_FLASH_TFTP
|
||||
|
||||
/* R8A66597 */
|
||||
#define LITTLEENDIAN /* for include/usb.h */
|
||||
#define CONFIG_USB_R8A66597_HCD
|
||||
#define CONFIG_R8A66597_BASE_ADDR SH7785LCR_USB_BASE
|
||||
#define CONFIG_R8A66597_XTAL 0x0000 /* 12MHz */
|
||||
|
@ -293,7 +293,6 @@
|
||||
#define CONFIG_SYS_USB_OHCI_SLOT_NAME "s3c6400"
|
||||
#define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 3
|
||||
#define CONFIG_SYS_USB_OHCI_CPU_INIT 1
|
||||
#define LITTLEENDIAN 1 /* used by usb_ohci.c */
|
||||
|
||||
#define CONFIG_USB_STORAGE 1
|
||||
#endif
|
||||
|
@ -44,7 +44,6 @@
|
||||
#define CONFIG_S3C2400 1 /* in a SAMSUNG S3C2400 SoC */
|
||||
#define CONFIG_TRAB 1 /* on a TRAB Board */
|
||||
#undef CONFIG_TRAB_50MHZ /* run the CPU at 50 MHz */
|
||||
#define LITTLEENDIAN 1 /* used by usb_ohci.c */
|
||||
|
||||
/* automatic software updates (see board/trab/auto_update.c) */
|
||||
#define CONFIG_AUTO_UPDATE 1
|
||||
|
@ -42,8 +42,6 @@
|
||||
*/
|
||||
#define CONFIG_PXA27X 1 /* This is an PXA27x CPU */
|
||||
|
||||
#define LITTLEENDIAN 1 /* used by usb_ohci.c */
|
||||
|
||||
#define CONFIG_MMC 1
|
||||
#define BOARD_LATE_INIT 1
|
||||
|
||||
|
@ -138,7 +138,7 @@ enum {
|
||||
|
||||
struct usb_device {
|
||||
int devnum; /* Device number on USB bus */
|
||||
int slow; /* Slow device? */
|
||||
int speed; /* full/low/high */
|
||||
char mf[32]; /* manufacturer */
|
||||
char prod[32]; /* product */
|
||||
char serial[32]; /* serial number */
|
||||
@ -171,6 +171,7 @@ struct usb_device {
|
||||
unsigned long status;
|
||||
int act_len; /* transfered bytes */
|
||||
int maxchild; /* Number of ports if hub */
|
||||
int portnr;
|
||||
struct usb_device *parent;
|
||||
struct usb_device *children[USB_MAXCHILDREN];
|
||||
};
|
||||
@ -180,8 +181,9 @@ struct usb_device {
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \
|
||||
defined(CONFIG_USB_OHCI_NEW) || defined(CONFIG_USB_SL811HS) || \
|
||||
defined(CONFIG_USB_ISP116X_HCD) || defined(CONFIG_USB_R8A66597_HCD)
|
||||
defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \
|
||||
defined(CONFIG_USB_SL811HS) || defined(CONFIG_USB_ISP116X_HCD) || \
|
||||
defined(CONFIG_USB_R8A66597_HCD) || defined(CONFIG_USB_DAVINCI)
|
||||
|
||||
int usb_lowlevel_init(void);
|
||||
int usb_lowlevel_stop(void);
|
||||
@ -263,13 +265,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
|
||||
((x_ & 0xFF000000UL) >> 24)); \
|
||||
})
|
||||
|
||||
#ifdef LITTLEENDIAN
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
# define swap_16(x) (x)
|
||||
# define swap_32(x) (x)
|
||||
#else
|
||||
# define swap_16(x) __swap_16(x)
|
||||
# define swap_32(x) __swap_32(x)
|
||||
#endif /* LITTLEENDIAN */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Calling this entity a "pipe" is glorifying it. A USB pipe
|
||||
@ -279,7 +281,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
|
||||
* - endpoint number (4 bits)
|
||||
* - current Data0/1 state (1 bit)
|
||||
* - direction (1 bit)
|
||||
* - speed (1 bit)
|
||||
* - speed (2 bits)
|
||||
* - max packet size (2 bits: 8, 16, 32 or 64)
|
||||
* - pipe type (2 bits: control, interrupt, bulk, isochronous)
|
||||
*
|
||||
@ -296,7 +298,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
|
||||
* - device: bits 8-14
|
||||
* - endpoint: bits 15-18
|
||||
* - Data0/1: bit 19
|
||||
* - speed: bit 26 (0 = Full, 1 = Low Speed)
|
||||
* - speed: bit 26 (0 = Full, 1 = Low Speed, 2 = High)
|
||||
* - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt,
|
||||
* 10 = control, 11 = bulk)
|
||||
*
|
||||
@ -308,8 +310,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
|
||||
/* Create various pipes... */
|
||||
#define create_pipe(dev,endpoint) \
|
||||
(((dev)->devnum << 8) | (endpoint << 15) | \
|
||||
((dev)->slow << 26) | (dev)->maxpacketsize)
|
||||
#define default_pipe(dev) ((dev)->slow << 26)
|
||||
((dev)->speed << 26) | (dev)->maxpacketsize)
|
||||
#define default_pipe(dev) ((dev)->speed << 26)
|
||||
|
||||
#define usb_sndctrlpipe(dev, endpoint) ((PIPE_CONTROL << 30) | \
|
||||
create_pipe(dev, endpoint))
|
||||
@ -359,7 +361,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate);
|
||||
#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff)
|
||||
#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)
|
||||
#define usb_pipedata(pipe) (((pipe) >> 19) & 1)
|
||||
#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
|
||||
#define usb_pipespeed(pipe) (((pipe) >> 26) & 3)
|
||||
#define usb_pipeslow(pipe) (usb_pipespeed(pipe) == USB_SPEED_LOW)
|
||||
#define usb_pipetype(pipe) (((pipe) >> 30) & 3)
|
||||
#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS)
|
||||
#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT)
|
||||
|
@ -80,6 +80,12 @@
|
||||
#define USB_DIR_OUT 0
|
||||
#define USB_DIR_IN 0x80
|
||||
|
||||
/* USB device speeds */
|
||||
#define USB_SPEED_FULL 0x0 /* 12Mbps */
|
||||
#define USB_SPEED_LOW 0x1 /* 1.5Mbps */
|
||||
#define USB_SPEED_HIGH 0x2 /* 480Mbps */
|
||||
#define USB_SPEED_RESERVED 0x3
|
||||
|
||||
/* Descriptor types */
|
||||
#define USB_DT_DEVICE 0x01
|
||||
#define USB_DT_CONFIG 0x02
|
||||
@ -202,6 +208,7 @@
|
||||
#define USB_PORT_FEAT_RESET 4
|
||||
#define USB_PORT_FEAT_POWER 8
|
||||
#define USB_PORT_FEAT_LOWSPEED 9
|
||||
#define USB_PORT_FEAT_HIGHSPEED 10
|
||||
#define USB_PORT_FEAT_C_CONNECTION 16
|
||||
#define USB_PORT_FEAT_C_ENABLE 17
|
||||
#define USB_PORT_FEAT_C_SUSPEND 18
|
||||
@ -216,6 +223,9 @@
|
||||
#define USB_PORT_STAT_RESET 0x0010
|
||||
#define USB_PORT_STAT_POWER 0x0100
|
||||
#define USB_PORT_STAT_LOW_SPEED 0x0200
|
||||
#define USB_PORT_STAT_HIGH_SPEED 0x0400 /* support for EHCI */
|
||||
#define USB_PORT_STAT_SPEED \
|
||||
(USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED)
|
||||
|
||||
/* wPortChange bits */
|
||||
#define USB_PORT_STAT_C_CONNECTION 0x0001
|
||||
|
Loading…
Reference in New Issue
Block a user