summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile6
-rw-r--r--drivers/isp116x-hcd.c1413
-rw-r--r--drivers/isp116x.h489
-rw-r--r--drivers/usb_ohci.c1929
-rw-r--r--drivers/usb_ohci.h445
-rw-r--r--drivers/usbdcore_ep0.c163
-rw-r--r--drivers/usbdcore_mpc8xx.c1400
-rw-r--r--drivers/usbdcore_omap1510.c27
-rw-r--r--drivers/usbtty.c699
-rw-r--r--drivers/usbtty.h60
10 files changed, 6305 insertions, 326 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 881153ac9d..fc98040762 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -31,7 +31,7 @@ COBJS = 3c589.o 5701rls.o ali512x.o ata_piix.o atmel_usart.o \
bcm570x.o bcm570x_autoneg.o cfb_console.o cfi_flash.o \
cs8900.o ct69000.o dataflash.o dc2114x.o dm9000x.o \
e1000.o eepro100.o enc28j60.o \
- i8042.o inca-ip_sw.o keyboard.o \
+ i8042.o inca-ip_sw.o isp116x-hcd.o keyboard.o \
lan91c96.o macb.o \
natsemi.o ne2000.o netarm_eth.o netconsole.o \
ns16550.o ns8382x.o ns87308.o ns7520_eth.o omap1510_i2c.o \
@@ -47,7 +47,9 @@ COBJS = 3c589.o 5701rls.o ali512x.o ata_piix.o atmel_usart.o \
status_led.o sym53c8xx.o systemace.o ahci.o \
ti_pci1410a.o tigon3.o tsec.o \
tsi108_eth.o tsi108_i2c.o tsi108_pci.o \
- usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbtty.o \
+ usb_ohci.o \
+ usbdcore.o usbdcore_ep0.o usbdcore_mpc8xx.o usbdcore_omap1510.o \
+ usbtty.o \
videomodes.o w83c553f.o \
ks8695eth.o \
pxa_pcmcia.o mpc8xx_pcmcia.o tqm8xx_pcmcia.o \
diff --git a/drivers/isp116x-hcd.c b/drivers/isp116x-hcd.c
new file mode 100644
index 0000000000..8e2bc7adcc
--- /dev/null
+++ b/drivers/isp116x-hcd.c
@@ -0,0 +1,1413 @@
+/*
+ * ISP116x HCD (Host Controller Driver) for u-boot.
+ *
+ * Copyright (C) 2006-2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2006-2007 Eurotech S.p.A. <info@eurotech.it>
+ *
+ * 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
+ *
+ *
+ * Derived in part from the SL811 HCD driver "u-boot/drivers/sl811_usb.c"
+ * (original copyright message follows):
+ *
+ * (C) Copyright 2004
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * This code is based on linux driver for sl811hs chip, source at
+ * drivers/usb/host/sl811.c:
+ *
+ * SL811 Host Controller Interface driver for USB.
+ *
+ * Copyright (c) 2003/06, Courage Co., Ltd.
+ *
+ * Based on:
+ * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
+ * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
+ * Adam Richter, Gregory P. Smith;
+ * 2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
+ * 3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
+ *
+ * [[GNU/GPL disclaimer]]
+ *
+ * and in part from AU1x00 OHCI HCD driver "u-boot/cpu/mips/au1x00_usb_ohci.c"
+ * (original copyright message follows):
+ *
+ * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00.
+ *
+ * (C) Copyright 2003
+ * Gary Jennejohn, DENX Software Engineering <gj@denx.de>
+ *
+ * [[GNU/GPL disclaimer]]
+ *
+ * Note: Part of this code has been derived from linux
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_USB_ISP116X_HCD
+#include <asm/io.h>
+#include <usb.h>
+#include <malloc.h>
+#include <linux/list.h>
+
+/*
+ * ISP116x chips require certain delays between accesses to its
+ * registers. The following timing options exist.
+ *
+ * 1. Configure your memory controller (the best)
+ * 2. Use ndelay (easiest, poorest). For that, enable the following macro.
+ *
+ * Value is in microseconds.
+ */
+#ifdef ISP116X_HCD_USE_UDELAY
+#define UDELAY 1
+#endif
+
+/*
+ * On some (slowly?) machines an extra delay after data packing into
+ * controller's FIFOs is required, * otherwise you may get the following
+ * error:
+ *
+ * uboot> usb start
+ * (Re)start USB...
+ * USB: scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT
+ * isp116x: isp116x_submit_job: ****** FIFO not ready! ******
+ *
+ * USB device not responding, giving up (status=4)
+ * isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ * isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ * isp116x: isp116x_submit_job: ****** FIFO not empty! ******
+ * 3 USB Device(s) found
+ * scanning bus for storage devices... 0 Storage Device(s) found
+ *
+ * Value is in milliseconds.
+ */
+#ifdef ISP116X_HCD_USE_EXTRA_DELAY
+#define EXTRA_DELAY 2
+#endif
+
+/*
+ * Enable the following defines if you wish enable debugging messages.
+ */
+#undef DEBUG /* enable debugging messages */
+#undef TRACE /* enable tracing code */
+#undef VERBOSE /* verbose debugging messages */
+
+#include "isp116x.h"
+
+#define DRIVER_VERSION "08 Jan 2007"
+static const char hcd_name[] = "isp116x-hcd";
+
+struct isp116x isp116x_dev;
+struct isp116x_platform_data isp116x_board;
+int got_rhsc = 0; /* root hub status change */
+struct usb_device *devgone; /* device which was disconnected */
+int rh_devnum = 0; /* address of Root Hub endpoint */
+
+/* ------------------------------------------------------------------------- */
+
+#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL))
+#define min_t(type,x,y) \
+ ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
+
+/* ------------------------------------------------------------------------- */
+
+static int isp116x_reset(struct isp116x *isp116x);
+
+/* --- Debugging functions ------------------------------------------------- */
+
+#define isp116x_show_reg(d, r) { \
+ if ((r) < 0x20) { \
+ DBG("%-12s[%02x]: %08x", #r, \
+ r, isp116x_read_reg32(d, r)); \
+ } else { \
+ DBG("%-12s[%02x]: %04x", #r, \
+ r, isp116x_read_reg16(d, r)); \
+ } \
+}
+
+#define isp116x_show_regs(d) { \
+ isp116x_show_reg(d, HCREVISION); \
+ isp116x_show_reg(d, HCCONTROL); \
+ isp116x_show_reg(d, HCCMDSTAT); \
+ isp116x_show_reg(d, HCINTSTAT); \
+ isp116x_show_reg(d, HCINTENB); \
+ isp116x_show_reg(d, HCFMINTVL); \
+ isp116x_show_reg(d, HCFMREM); \
+ isp116x_show_reg(d, HCFMNUM); \
+ isp116x_show_reg(d, HCLSTHRESH); \
+ isp116x_show_reg(d, HCRHDESCA); \
+ isp116x_show_reg(d, HCRHDESCB); \
+ isp116x_show_reg(d, HCRHSTATUS); \
+ isp116x_show_reg(d, HCRHPORT1); \
+ isp116x_show_reg(d, HCRHPORT2); \
+ isp116x_show_reg(d, HCHWCFG); \
+ isp116x_show_reg(d, HCDMACFG); \
+ isp116x_show_reg(d, HCXFERCTR); \
+ isp116x_show_reg(d, HCuPINT); \
+ isp116x_show_reg(d, HCuPINTENB); \
+ isp116x_show_reg(d, HCCHIPID); \
+ isp116x_show_reg(d, HCSCRATCH); \
+ isp116x_show_reg(d, HCITLBUFLEN); \
+ isp116x_show_reg(d, HCATLBUFLEN); \
+ isp116x_show_reg(d, HCBUFSTAT); \
+ isp116x_show_reg(d, HCRDITL0LEN); \
+ isp116x_show_reg(d, HCRDITL1LEN); \
+}
+
+#if defined(TRACE)
+
+static int isp116x_get_current_frame_number(struct usb_device *usb_dev)
+{
+ struct isp116x *isp116x = &isp116x_dev;
+
+ return isp116x_read_reg32(isp116x, HCFMNUM);
+}
+
+static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int len, char *str)
+{
+#if defined(VERBOSE)
+ int i;
+#endif
+
+ DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx",
+ str,
+ isp116x_get_current_frame_number(dev),
+ usb_pipedevice(pipe),
+ usb_pipeendpoint(pipe),
+ usb_pipeout(pipe) ? 'O' : 'I',
+ usb_pipetype(pipe) < 2 ?
+ (usb_pipeint(pipe) ?
+ "INTR" : "ISOC") :
+ (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status);
+#if defined(VERBOSE)
+ if (len > 0 && buffer) {
+ printf(__FILE__ ": data(%d):", len);
+ for (i = 0; i < 16 && i < len; i++)
+ printf(" %02x", ((__u8 *) buffer)[i]);
+ printf("%s\n", i < len ? "..." : "");
+ }
+#endif
+}
+
+#define PTD_DIR_STR(ptd) ({char __c; \
+ switch(PTD_GET_DIR(ptd)){ \
+ case 0: __c = 's'; break; \
+ case 1: __c = 'o'; break; \
+ default: __c = 'i'; break; \
+ }; __c;})
+
+/*
+ Dump PTD info. The code documents the format
+ perfectly, right :)
+*/
+static inline void dump_ptd(struct ptd *ptd)
+{
+#if defined(VERBOSE)
+ int k;
+#endif
+
+ DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x",
+ PTD_GET_CC(ptd),
+ PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd),
+ PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+ PTD_GET_TOGGLE(ptd),
+ PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd));
+#if defined(VERBOSE)
+ printf("isp116x: %s: PTD(byte): ", __FUNCTION__);
+ for (k = 0; k < sizeof(struct ptd); ++k)
+ printf("%02x ", ((u8 *) ptd)[k]);
+ printf("\n");
+#endif
+}
+
+static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type)
+{
+#if defined(VERBOSE)
+ int k;
+
+ if (type == 0 /* 0ut data */ ) {
+ printf("isp116x: %s: out data: ", __FUNCTION__);
+ for (k = 0; k < PTD_GET_LEN(ptd); ++k)
+ printf("%02x ", ((u8 *) buf)[k]);
+ printf("\n");
+ }
+ if (type == 1 /* 1n data */ ) {
+ printf("isp116x: %s: in data: ", __FUNCTION__);
+ for (k = 0; k < PTD_GET_COUNT(ptd); ++k)
+ printf("%02x ", ((u8 *) buf)[k]);
+ printf("\n");
+ }
+
+ if (PTD_GET_LAST(ptd))
+ DBG("--- last PTD ---");
+#endif
+}
+
+#else
+
+#define dump_msg(dev, pipe, buffer, len, str) do { } while (0)
+#define dump_pkt(dev, pipe, buffer, len, setup, str, small) do {} while (0)
+
+#define dump_ptd(ptd) do {} while (0)
+#define dump_ptd_data(ptd, buf, type) do {} while (0)
+
+#endif
+
+/* --- Virtual Root Hub ---------------------------------------------------- */
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] = {
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x10, /* __u16 bcdUSB; v1.1 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x01, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] = {
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x00, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+ 0x02,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] = {
+ 0x04, /* __u8 bLength; */
+ 0x03, /* __u8 bDescriptorType; String-descriptor */
+ 0x09, /* __u8 lang ID */
+ 0x04, /* __u8 lang ID */
+};
+
+static unsigned char root_hub_str_index1[] = {
+ 0x22, /* __u8 bLength; */
+ 0x03, /* __u8 bDescriptorType; String-descriptor */
+ 'I', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'S', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'P', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ '1', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ '1', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ '6', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'x', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ ' ', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'R', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'o', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'o', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 't', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ ' ', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'H', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'u', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'b', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+};
+
+/*
+ * Hub class-specific descriptor is constructed dynamically
+ */
+
+/* --- Virtual root hub management functions ------------------------------- */
+
+static int rh_check_port_status(struct isp116x *isp116x)
+{
+ u32 temp, ndp, i;
+ int res;
+
+ res = -1;
+ temp = isp116x_read_reg32(isp116x, HCRHSTATUS);
+ ndp = (temp & RH_A_NDP);
+ for (i = 0; i < ndp; i++) {
+ temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i);
+ /* check for a device disconnect */
+ if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
+ (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) {
+ res = i;
+ break;
+ }
+ }
+ return res;
+}
+
+/* --- HC management functions --------------------------------------------- */
+
+/* Write len bytes to fifo, pad till 32-bit boundary
+ */
+static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+ u8 *dp = (u8 *) buf;
+ u16 *dp2 = (u16 *) buf;
+ u16 w;
+ int quot = len % 4;
+
+ if ((unsigned long)dp2 & 1) {
+ /* not aligned */
+ for (; len > 1; len -= 2) {
+ w = *dp++;
+ w |= *dp++ << 8;
+ isp116x_raw_write_data16(isp116x, w);
+ }
+ if (len)
+ isp116x_write_data16(isp116x, (u16) * dp);
+ } else {
+ /* aligned */
+ for (; len > 1; len -= 2)
+ isp116x_raw_write_data16(isp116x, *dp2++);
+ if (len)
+ isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
+ }
+ if (quot == 1 || quot == 2)
+ isp116x_raw_write_data16(isp116x, 0);
+}
+
+/* Read len bytes from fifo and then read till 32-bit boundary
+ */
+static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
+{
+ u8 *dp = (u8 *) buf;
+ u16 *dp2 = (u16 *) buf;
+ u16 w;
+ int quot = len % 4;
+
+ if ((unsigned long)dp2 & 1) {
+ /* not aligned */
+ for (; len > 1; len -= 2) {
+ w = isp116x_raw_read_data16(isp116x);
+ *dp++ = w & 0xff;
+ *dp++ = (w >> 8) & 0xff;
+ }
+ if (len)
+ *dp = 0xff & isp116x_read_data16(isp116x);
+ } else {
+ /* aligned */
+ for (; len > 1; len -= 2)
+ *dp2++ = isp116x_raw_read_data16(isp116x);
+ if (len)
+ *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
+ }
+ if (quot == 1 || quot == 2)
+ isp116x_raw_read_data16(isp116x);
+}
+
+/* Write PTD's and data for scheduled transfers into the fifo ram.
+ * Fifo must be empty and ready */
+static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev,
+ unsigned long pipe, struct ptd *ptd, int n, void *data,
+ int len)
+{
+ int buflen = n * sizeof(struct ptd) + len;
+ int i, done;
+
+ DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen);
+
+ isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+ isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+ isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
+
+ done = 0;
+ for (i = 0; i < n; i++) {
+ DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i]));
+
+ dump_ptd(&ptd[i]);
+ isp116x_write_data16(isp116x, ptd[i].count);
+ isp116x_write_data16(isp116x, ptd[i].mps);
+ isp116x_write_data16(isp116x, ptd[i].len);
+ isp116x_write_data16(isp116x, ptd[i].faddr);
+
+ dump_ptd_data(&ptd[i], (__u8 *) data + done, 0);
+ write_ptddata_to_fifo(isp116x,
+ (__u8 *) data + done,
+ PTD_GET_LEN(&ptd[i]));
+
+ done += PTD_GET_LEN(&ptd[i]);
+ }
+}
+
+/* Read the processed PTD's and data from fifo ram back to URBs' buffers.
+ * Fifo must be full and done */
+static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev,
+ unsigned long pipe, struct ptd *ptd, int n, void *data,
+ int len)
+{
+ int buflen = n * sizeof(struct ptd) + len;
+ int i, done, cc, ret;
+
+ isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
+ isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
+ isp116x_write_addr(isp116x, HCATLPORT);
+
+ ret = TD_CC_NOERROR;
+ done = 0;
+ for (i = 0; i < n; i++) {
+ DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i]));
+
+ ptd[i].count = isp116x_read_data16(isp116x);
+ ptd[i].mps = isp116x_read_data16(isp116x);
+ ptd[i].len = isp116x_read_data16(isp116x);
+ ptd[i].faddr = isp116x_read_data16(isp116x);
+ dump_ptd(&ptd[i]);
+
+ read_ptddata_from_fifo(isp116x,
+ (__u8 *) data + done,
+ PTD_GET_LEN(&ptd[i]));
+ dump_ptd_data(&ptd[i], (__u8 *) data + done, 1);
+
+ done += PTD_GET_LEN(&ptd[i]);
+
+ cc = PTD_GET_CC(&ptd[i]);
+ if (cc == TD_DATAUNDERRUN) { /* underrun is no error... */
+ DBG("allowed data underrun");
+ cc = TD_CC_NOERROR;
+ }
+ if (cc != TD_CC_NOERROR && ret == TD_CC_NOERROR)
+ ret = cc;
+ }
+
+ DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen);
+
+ return ret;
+}
+
+/* Interrupt handling
+ */
+static int isp116x_interrupt(struct isp116x *isp116x)
+{
+ u16 irqstat;
+ u32 intstat;
+ int ret = 0;
+
+ isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+ irqstat = isp116x_read_reg16(isp116x, HCuPINT);
+ isp116x_write_reg16(isp116x, HCuPINT, irqstat);
+ DBG(">>>>>> irqstat %x <<<<<<", irqstat);
+
+ if (irqstat & HCuPINT_ATL) {
+ DBG(">>>>>> HCuPINT_ATL <<<<<<");
+ udelay(500);
+ ret = 1;
+ }
+
+ if (irqstat & HCuPINT_OPR) {
+ intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
+ isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
+ DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat);
+
+ if (intstat & HCINT_UE) {
+ ERR("unrecoverable error, controller disabled");
+
+ /* FIXME: be optimistic, hope that bug won't repeat
+ * often. Make some non-interrupt context restart the
+ * controller. Count and limit the retries though;
+ * either hardware or software errors can go forever...
+ */
+ isp116x_reset(isp116x);
+ ret = -1;
+ return -1;
+ }
+
+ if (intstat & HCINT_RHSC) {
+ got_rhsc = 1;
+ ret = 1;
+ /* When root hub or any of its ports is going
+ to come out of suspend, it may take more
+ than 10ms for status bits to stabilize. */
+ wait_ms(20);
+ }
+
+ if (intstat & HCINT_SO) {
+ ERR("schedule overrun");
+ ret = -1;
+ }
+
+ irqstat &= ~HCuPINT_OPR;
+ }
+
+ return ret;
+}
+
+#define PTD_NUM 64 /* it should be enougth... */
+struct ptd ptd[PTD_NUM];
+static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe)
+{
+ return min(PTD_NUM * usb_maxpacket(dev, pipe), PTD_NUM * 16);
+}
+
+/* Do an USB transfer
+ */
+static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe,
+ int dir, void *buffer, int len)
+{
+ struct isp116x *isp116x = &isp116x_dev;
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ int max = usb_maxpacket(dev, pipe);
+ int dir_out = usb_pipeout(pipe);
+ int speed_low = usb_pipeslow(pipe);
+ int i, done, stat, timeout, cc;
+ int retries = 10;
+
+ DBG("------------------------------------------------");
+ dump_msg(dev, pipe, buffer, len, "SUBMIT");
+ DBG("------------------------------------------------");
+
+ if (isp116x->disabled) {
+ ERR("EPIPE");
+ dev->status = USB_ST_CRC_ERR;
+ return -1;
+ }
+
+ /* device pulled? Shortcut the action. */
+ if (devgone == dev) {
+ ERR("ENODEV");
+ dev->status = USB_ST_CRC_ERR;
+ return USB_ST_CRC_ERR;
+ }
+
+ if (!max) {
+ ERR("pipesize for pipe %lx is zero", pipe);
+ dev->status = USB_ST_CRC_ERR;
+ return -1;
+ }
+
+ if (type == PIPE_ISOCHRONOUS) {
+ ERR("isochronous transfers not supported");
+ dev->status = USB_ST_CRC_ERR;
+ return -1;
+ }
+
+ /* FIFO not empty? */
+ if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) {
+ ERR("****** FIFO not empty! ******");
+ dev->status = USB_ST_BUF_ERR;
+ return -1;
+ }
+
+ retry:
+ isp116x_write_reg32(isp116x, HCINTSTAT, 0xff);
+
+ /* Prepare the PTD data */
+ done = 0;
+ i = 0;
+ do {
+ ptd[i].count = PTD_CC_MSK | PTD_ACTIVE_MSK |
+ PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out));
+ ptd[i].mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum);
+ ptd[i].len = PTD_LEN(max > len - done ? len - done : max) |
+ PTD_DIR(dir);
+ ptd[i].faddr = PTD_FA(usb_pipedevice(pipe));
+
+ usb_dotoggle(dev, epnum, dir_out);
+ done += PTD_GET_LEN(&ptd[i]);
+ i++;
+ if (i >= PTD_NUM) {
+ ERR("****** Cannot pack buffer! ******");
+ dev->status = USB_ST_BUF_ERR;
+ return -1;
+ }
+ } while (done < len);
+ ptd[i - 1].mps |= PTD_LAST_MSK;
+
+ /* Pack data into FIFO ram */
+ pack_fifo(isp116x, dev, pipe, ptd, i, buffer, len);
+#ifdef EXTRA_DELAY
+ wait_ms(EXTRA_DELAY);
+#endif
+
+ /* Start the data transfer */
+
+ /* Allow more time for a BULK device to react - some are slow */
+ if (usb_pipetype(pipe) == PIPE_BULK)
+ timeout = 5000;
+ else
+ timeout = 100;
+
+ /* Wait for it to complete */
+ for (;;) {
+ /* Check whether the controller is done */
+ stat = isp116x_interrupt(isp116x);
+
+ if (stat < 0) {
+ dev->status = USB_ST_CRC_ERR;
+ break;
+ }
+ if (stat > 0)
+ break;
+
+ /* Check the timeout */
+ if (--timeout)
+ udelay(1);
+ else {
+ ERR("CTL:TIMEOUT ");
+ stat = USB_ST_CRC_ERR;
+ break;
+ }
+ }
+
+ /* We got an Root Hub Status Change interrupt */
+ if (got_rhsc) {
+ isp116x_show_regs(isp116x);
+
+ got_rhsc = 0;
+
+ /* Abuse timeout */
+ timeout = rh_check_port_status(isp116x);
+ if (timeout >= 0) {
+ /*
+ * FIXME! NOTE! AAAARGH!
+ * This is potentially dangerous because it assumes
+ * that only one device is ever plugged in!
+ */
+ devgone = dev;
+ }
+ }
+
+ /* Ok, now we can read transfer status */
+
+ /* FIFO not ready? */
+ if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) {
+ ERR("****** FIFO not ready! ******");
+ dev->status = USB_ST_BUF_ERR;
+ return -1;
+ }
+
+ /* Unpack data from FIFO ram */
+ cc = unpack_fifo(isp116x, dev, pipe, ptd, i, buffer, len);
+
+ /* Mmm... sometime we get 0x0f as cc which is a non sense!
+ * Just retry the transfer...
+ */
+ if (cc == 0x0f && retries-- > 0) {
+ usb_dotoggle(dev, epnum, dir_out);
+ goto retry;
+ }
+
+ if (cc != TD_CC_NOERROR) {
+ DBG("****** completition code error %x ******", cc);
+ switch (cc) {
+ case TD_CC_BITSTUFFING:
+ dev->status = USB_ST_BIT_ERR;
+ break;
+ case TD_CC_STALL:
+ dev->status = USB_ST_STALLED;
+ break;
+ case TD_BUFFEROVERRUN:
+ case TD_BUFFERUNDERRUN:
+ dev->status = USB_ST_BUF_ERR;
+ break;
+ default:
+ dev->status = USB_ST_CRC_ERR;
+ }
+ return -cc;
+ }
+
+ dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)");
+
+ dev->status = 0;
+ return done;
+}
+
+/* Adapted from au1x00_usb_ohci.c
+ */
+static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int transfer_len,
+ struct devrequest *cmd)
+{
+ struct isp116x *isp116x = &isp116x_dev;
+ u32 tmp = 0;
+
+ int leni = transfer_len;
+ int len = 0;
+ int stat = 0;
+ u32 datab[4];
+ u8 *data_buf = (u8 *) datab;
+ u16 bmRType_bReq;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+
+ if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+ INFO("Root-Hub submit IRQ: NOT implemented");
+ return 0;
+ }
+
+ bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+ wValue = swap_16(cmd->value);
+ wIndex = swap_16(cmd->index);
+ wLength = swap_16(cmd->length);
+
+ DBG("--- HUB ----------------------------------------");
+ DBG("submit rh urb, req=%x val=%#x index=%#x len=%d",
+ bmRType_bReq, wValue, wIndex, wLength);
+ dump_msg(dev, pipe, buffer, transfer_len, "RH");
+ DBG("------------------------------------------------");
+
+ switch (bmRType_bReq) {
+ case RH_GET_STATUS:
+ DBG("RH_GET_STATUS");
+
+ *(__u16 *) data_buf = swap_16(1);
+ len = 2;
+ break;
+
+ case RH_GET_STATUS | RH_INTERFACE:
+ DBG("RH_GET_STATUS | RH_INTERFACE");
+
+ *(__u16 *) data_buf = swap_16(0);
+ len = 2;
+ break;
+
+ case RH_GET_STATUS | RH_ENDPOINT:
+ DBG("RH_GET_STATUS | RH_ENDPOINT");
+
+ *(__u16 *) data_buf = swap_16(0);
+ len = 2;
+ break;
+
+ case RH_GET_STATUS | RH_CLASS:
+ DBG("RH_GET_STATUS | RH_CLASS");
+
+ tmp = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+ *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE));
+ len = 4;
+ break;
+
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS");
+
+ tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1);
+ *(__u32 *) data_buf = swap_32(tmp);
+ isp116x_show_regs(isp116x);
+ len = 4;
+ break;
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ DBG("RH_CLEAR_FEATURE | RH_ENDPOINT");
+
+ switch (wValue) {
+ case RH_ENDPOINT_STALL:
+ DBG("C_HUB_ENDPOINT_STALL");
+ len = 0;
+ break;
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ DBG("RH_CLEAR_FEATURE | RH_CLASS");
+
+ switch (wValue) {
+ case RH_C_HUB_LOCAL_POWER:
+ DBG("C_HUB_LOCAL_POWER");
+ len = 0;
+ break;
+
+ case RH_C_HUB_OVER_CURRENT:
+ DBG("C_HUB_OVER_CURRENT");
+ isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);
+ len = 0;
+ break;
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS");
+
+ switch (wValue) {
+ case RH_PORT_ENABLE:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_CCS);
+ len = 0;
+ break;
+
+ case RH_PORT_SUSPEND:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_POCI);
+ len = 0;
+ break;
+
+ case RH_PORT_POWER:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_LSDA);
+ len = 0;
+ break;
+
+ case RH_C_PORT_CONNECTION:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_CSC);
+ len = 0;
+ break;
+
+ case RH_C_PORT_ENABLE:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PESC);
+ len = 0;
+ break;
+
+ case RH_C_PORT_SUSPEND:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PSSC);
+ len = 0;
+ break;
+
+ case RH_C_PORT_OVER_CURRENT:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_POCI);
+ len = 0;
+ break;
+
+ case RH_C_PORT_RESET:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PRSC);
+ len = 0;
+ break;
+
+ default:
+ ERR("invalid wValue");
+ stat = USB_ST_STALLED;
+ }
+
+ isp116x_show_regs(isp116x);
+
+ break;
+
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS");
+
+ switch (wValue) {
+ case RH_PORT_SUSPEND:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PSS);
+ len = 0;
+ break;
+
+ case RH_PORT_RESET:
+ /* Spin until any current reset finishes */
+ while (1) {
+ tmp =
+ isp116x_read_reg32(isp116x,
+ HCRHPORT1 + wIndex - 1);
+ if (!(tmp & RH_PS_PRS))
+ break;
+ wait_ms(1);
+ }
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PRS);
+ wait_ms(10);
+
+ len = 0;
+ break;
+
+ case RH_PORT_POWER:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PPS);
+ len = 0;
+ break;
+
+ case RH_PORT_ENABLE:
+ isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1,
+ RH_PS_PES);
+ len = 0;
+ break;
+
+ default:
+ ERR("invalid wValue");
+ stat = USB_ST_STALLED;
+ }
+
+ isp116x_show_regs(isp116x);
+
+ break;
+
+ case RH_SET_ADDRESS:
+ DBG("RH_SET_ADDRESS");
+
+ rh_devnum = wValue;
+ len = 0;
+ break;
+
+ case RH_GET_DESCRIPTOR:
+ DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength);
+
+ switch (wValue) {
+ case (USB_DT_DEVICE << 8): /* device descriptor */
+ len = min_t(unsigned int,
+ leni, min_t(unsigned int,
+ sizeof(root_hub_dev_des),
+ wLength));
+ data_buf = root_hub_dev_des;
+ break;
+
+ case (USB_DT_CONFIG << 8): /* configuration descriptor */
+ len = min_t(unsigned int,
+ leni, min_t(unsigned int,
+ sizeof(root_hub_config_des),
+ wLength));
+ data_buf = root_hub_config_des;
+ break;
+
+ case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */
+ len = min_t(unsigned int,
+ leni, min_t(unsigned int,
+ sizeof(root_hub_str_index0),
+ wLength));
+ data_buf = root_hub_str_index0;
+ break;
+
+ case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */
+ len = min_t(unsigned int,
+ leni, min_t(unsigned int,
+ sizeof(root_hub_str_index1),
+ wLength));
+ data_buf = root_hub_str_index1;
+ break;
+
+ default:
+ ERR("invalid wValue");
+ stat = USB_ST_STALLED;
+ }
+
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ DBG("RH_GET_DESCRIPTOR | RH_CLASS");
+
+ tmp = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+ data_buf[0] = 0x09; /* min length; */
+ data_buf[1] = 0x29;
+ data_buf[2] = tmp & RH_A_NDP;
+ data_buf[3] = 0;
+ if (tmp & RH_A_PSM) /* per-port power switching? */
+ data_buf[3] |= 0x01;
+ if (tmp & RH_A_NOCP) /* no overcurrent reporting? */
+ data_buf[3] |= 0x10;
+ else if (tmp & RH_A_OCPM) /* per-port overcurrent rep? */
+ data_buf[3] |= 0x08;
+
+ /* Corresponds to data_buf[4-7] */
+ datab[1] = 0;
+ data_buf[5] = (tmp & RH_A_POTPGT) >> 24;
+
+ tmp = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+ data_buf[7] = tmp & RH_B_DR;
+ if (data_buf[2] < 7)
+ data_buf[8] = 0xff;
+ else {
+ data_buf[0] += 2;
+ data_buf[8] = (tmp & RH_B_DR) >> 8;
+ data_buf[10] = data_buf[9] = 0xff;
+ }
+
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int, data_buf[0], wLength));
+ break;
+
+ case RH_GET_CONFIGURATION:
+ DBG("RH_GET_CONFIGURATION");
+
+ *(__u8 *) data_buf = 0x01;
+ len = 1;
+ break;
+
+ case RH_SET_CONFIGURATION:
+ DBG("RH_SET_CONFIGURATION");
+
+ isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC);
+ len = 0;
+ break;
+
+ default:
+ ERR("*** *** *** unsupported root hub command *** *** ***");
+ stat = USB_ST_STALLED;
+ }
+
+ len = min_t(int, len, leni);
+ if (buffer != data_buf)
+ memcpy(buffer, data_buf, len);
+
+ dev->act_len = len;
+ dev->status = stat;
+ DBG("dev act_len %d, status %d", dev->act_len, dev->status);
+
+ dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)");
+
+ return stat;
+}
+
+/* --- Transfer functions -------------------------------------------------- */
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int len, int interval)
+{
+ DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d",
+ dev, pipe, buffer, len, interval);
+
+ return -1;
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int len, struct devrequest *setup)
+{
+ int devnum = usb_pipedevice(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ int max = max_transfer_len(dev, pipe);
+ int dir_in = usb_pipein(pipe);
+ int done, ret;
+
+ /* Control message is for the HUB? */
+ if (devnum == rh_devnum)
+ return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup);
+
+ /* Ok, no HUB message so send the message to the device */
+
+ /* Setup phase */
+ DBG("--- SETUP PHASE --------------------------------");
+ usb_settoggle(dev, epnum, 1, 0);
+ ret = isp116x_submit_job(dev, pipe,
+ PTD_DIR_SETUP,
+ setup, sizeof(struct devrequest));
+ if (ret < 0) {
+ DBG("control setup phase error (ret = %d", ret);
+ return -1;
+ }
+
+ /* Data phase */
+ DBG("--- DATA PHASE ---------------------------------");
+ done = 0;
+ usb_settoggle(dev, epnum, !dir_in, 1);
+ while (done < len) {
+ ret = isp116x_submit_job(dev, pipe,
+ dir_in ? PTD_DIR_IN : PTD_DIR_OUT,
+ (__u8 *) buffer + done,
+ max > len - done ? len - done : max);
+ if (ret < 0) {
+ DBG("control data phase error (ret = %d)", ret);
+ return -1;
+ }
+ done += ret;
+
+ if (dir_in && ret < max) /* short packet */
+ break;
+ }
+
+ /* Status phase */
+ DBG("--- STATUS PHASE -------------------------------");
+ usb_settoggle(dev, epnum, !dir_in, 1);
+ ret = isp116x_submit_job(dev, pipe,
+ !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0);
+ if (ret < 0) {
+ DBG("control status phase error (ret = %d", ret);
+ return -1;
+ }
+
+ dev->act_len = done;
+
+ dump_msg(dev, pipe, buffer, len, "DEV(ret)");
+
+ return done;
+}
+
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int len)
+{
+ int dir_out = usb_pipeout(pipe);
+ int max = max_transfer_len(dev, pipe);
+ int done, ret;
+
+ DBG("--- BULK ---------------------------------------");
+ DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d",
+ usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
+
+ done = 0;
+ while (done < len) {
+ ret = isp116x_submit_job(dev, pipe,
+ !dir_out ? PTD_DIR_IN : PTD_DIR_OUT,
+ (__u8 *) buffer + done,
+ max > len - done ? len - done : max);
+ if (ret < 0) {
+ DBG("error on bulk message (ret = %d)", ret);
+ return -1;
+ }
+
+ done += ret;
+
+ if (!dir_out && ret < max) /* short packet */
+ break;
+ }
+
+ dev->act_len = done;
+
+ return 0;
+}
+
+/* --- Basic functions ----------------------------------------------------- */
+
+static int isp116x_sw_reset(struct isp116x *isp116x)
+{
+ int retries = 15;
+ int ret = 0;
+
+ DBG("");
+
+ isp116x->disabled = 1;
+
+ isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);
+ isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR);
+ while (--retries) {
+ /* It usually resets within 1 ms */
+ wait_ms(1);
+ if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR))
+ break;
+ }
+ if (!retries) {
+ ERR("software reset timeout");
+ ret = -1;
+ }
+ return ret;
+}
+
+static int isp116x_reset(struct isp116x *isp116x)
+{
+ unsigned long t;
+ u16 clkrdy = 0;
+ int ret, timeout = 15 /* ms */ ;
+
+ DBG("");
+
+ ret = isp116x_sw_reset(isp116x);
+ if (ret)
+ return ret;
+
+ for (t = 0; t < timeout; t++) {
+ clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;
+ if (clkrdy)
+ break;
+ wait_ms(1);
+ }
+ if (!clkrdy) {
+ ERR("clock not ready after %dms", timeout);
+ /* After sw_reset the clock won't report to be ready, if
+ H_WAKEUP pin is high. */
+ ERR("please make sure that the H_WAKEUP pin is pulled low!");
+ ret = -1;
+ }
+ return ret;
+}
+
+static void isp116x_stop(struct isp116x *isp116x)
+{
+ u32 val;
+
+ DBG("");
+
+ isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+ /* Switch off ports' power, some devices don't come up
+ after next 'start' without this */
+ val = isp116x_read_reg32(isp116x, HCRHDESCA);
+ val &= ~(RH_A_NPS | RH_A_PSM);
+ isp116x_write_reg32(isp116x, HCRHDESCA, val);
+ isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
+
+ isp116x_sw_reset(isp116x);
+}
+
+/*
+ * Configure the chip. The chip must be successfully reset by now.
+ */
+static int isp116x_start(struct isp116x *isp116x)
+{
+ struct isp116x_platform_data *board = isp116x->board;
+ u32 val;
+
+ DBG("");
+
+ /* Clear interrupt status and disable all interrupt sources */
+ isp116x_write_reg16(isp116x, HCuPINT, 0xff);
+ isp116x_write_reg16(isp116x, HCuPINTENB, 0);
+
+ isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
+ isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
+
+ /* Hardware configuration */
+ val = HCHWCFG_DBWIDTH(1);
+ if (board->sel15Kres)
+ val |= HCHWCFG_15KRSEL;
+ /* Remote wakeup won't work without working clock */
+ if (board->remote_wakeup_enable)
+ val |= HCHWCFG_CLKNOTSTOP;
+ if (board->oc_enable)
+ val |= HCHWCFG_ANALOG_OC;
+ isp116x_write_reg16(isp116x, HCHWCFG, val);
+
+ /* --- Root hub configuration */
+ val = (25 << 24) & RH_A_POTPGT;
+ /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to
+ be always set. Yet, instead, we request individual port
+ power switching. */
+ val |= RH_A_PSM;
+ /* Report overcurrent per port */
+ val |= RH_A_OCPM;
+ isp116x_write_reg32(isp116x, HCRHDESCA, val);
+ isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
+
+ val = RH_B_PPCM;
+ isp116x_write_reg32(isp116x, HCRHDESCB, val);
+ isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);
+
+ val = 0;
+ if (board->remote_wakeup_enable)
+ val |= RH_HS_DRWE;
+ isp116x_write_reg32(isp116x, HCRHSTATUS, val);
+ isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
+
+ isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf);
+
+ /* Go operational */
+ val = HCCONTROL_USB_OPER;
+ if (board->remote_wakeup_enable)
+ val |= HCCONTROL_RWE;
+ isp116x_write_reg32(isp116x, HCCONTROL, val);
+
+ /* Disable ports to avoid race in device enumeration */
+ isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
+ isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
+
+ isp116x_show_regs(isp116x);
+
+ isp116x->disabled = 0;
+
+ return 0;
+}
+
+/* --- Init functions ------------------------------------------------------ */
+
+int isp116x_check_id(struct isp116x *isp116x)
+{
+ int val;
+
+ val = isp116x_read_reg16(isp116x, HCCHIPID);
+ if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+ ERR("invalid chip ID %04x", val);
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_lowlevel_init(void)
+{
+ struct isp116x *isp116x = &isp116x_dev;
+
+ DBG("");
+
+ /* Init device registers addr */
+ isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR;
+ isp116x->data_reg = (u16 *) ISP116X_HCD_DATA;
+
+ /* Setup specific board settings */
+#ifdef ISP116X_HCD_SEL15kRES
+ isp116x_board.sel15Kres = 1;
+#endif
+#ifdef ISP116X_HCD_OC_ENABLE
+ isp116x_board.oc_enable = 1;
+#endif
+#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE
+ isp116x_board.remote_wakeup_enable = 1;
+#endif
+ isp116x->board = &isp116x_board;
+
+ /* Try to get ISP116x silicon chip ID */
+ if (isp116x_check_id(isp116x) < 0)
+ return -1;
+
+ isp116x->disabled = 1;
+ isp116x->sleeping = 0;
+
+ isp116x_reset(isp116x);
+ isp116x_start(isp116x);
+
+ return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+ struct isp116x *isp116x = &isp116x_dev;
+
+ DBG("");
+
+ if (!isp116x->disabled)
+ isp116x_stop(isp116x);
+
+ return 0;
+}
+
+#endif /* CONFIG_USB_ISP116X_HCD */
diff --git a/drivers/isp116x.h b/drivers/isp116x.h
new file mode 100644
index 0000000000..a3ce3b582c
--- /dev/null
+++ b/drivers/isp116x.h
@@ -0,0 +1,489 @@
+/*
+ * ISP116x register declarations and HCD data structures
+ *
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ * Portions:
+ * Copyright (C) 2004 Lothar Wassmann
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ *
+ * 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
+ */
+
+#ifdef DEBUG
+#define DBG(fmt, args...) \
+ printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#else
+#define DBG(fmt, args...) do {} while (0)
+#endif
+
+#ifdef VERBOSE
+# define VDBG DBG
+#else
+# define VDBG(fmt, args...) do {} while (0)
+#endif
+
+#define ERR(fmt, args...) \
+ printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#define WARN(fmt, args...) \
+ printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
+#define INFO(fmt, args...) \
+ printf("isp116x: " fmt "\n" , ## args)
+
+/* ------------------------------------------------------------------------- */
+
+/* us of 1ms frame */
+#define MAX_LOAD_LIMIT 850
+
+/* Full speed: max # of bytes to transfer for a single urb
+ at a time must be < 1024 && must be multiple of 64.
+ 832 allows transfering 4kiB within 5 frames. */
+#define MAX_TRANSFER_SIZE_FULLSPEED 832
+
+/* Low speed: there is no reason to schedule in very big
+ chunks; often the requested long transfers are for
+ string descriptors containing short strings. */
+#define MAX_TRANSFER_SIZE_LOWSPEED 64
+
+/* Bytetime (us), a rough indication of how much time it
+ would take to transfer a byte of useful data over USB */
+#define BYTE_TIME_FULLSPEED 1
+#define BYTE_TIME_LOWSPEED 20
+
+/* Buffer sizes */
+#define ISP116x_BUF_SIZE 4096
+#define ISP116x_ITL_BUFSIZE 0
+#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
+
+#define ISP116x_WRITE_OFFSET 0x80
+
+/* --- ISP116x registers/bits ---------------------------------------------- */
+
+#define HCREVISION 0x00
+#define HCCONTROL 0x01
+#define HCCONTROL_HCFS (3 << 6) /* host controller
+ functional state */
+#define HCCONTROL_USB_RESET (0 << 6)
+#define HCCONTROL_USB_RESUME (1 << 6)
+#define HCCONTROL_USB_OPER (2 << 6)
+#define HCCONTROL_USB_SUSPEND (3 << 6)
+#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */
+#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */
+#define HCCMDSTAT 0x02
+#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */
+#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */
+#define HCINTSTAT 0x03
+#define HCINT_SO (1 << 0) /* scheduling overrun */
+#define HCINT_WDH (1 << 1) /* writeback of done_head */
+#define HCINT_SF (1 << 2) /* start frame */
+#define HCINT_RD (1 << 3) /* resume detect */
+#define HCINT_UE (1 << 4) /* unrecoverable error */
+#define HCINT_FNO (1 << 5) /* frame number overflow */
+#define HCINT_RHSC (1 << 6) /* root hub status change */
+#define HCINT_OC (1 << 30) /* ownership change */
+#define HCINT_MIE (1 << 31) /* master interrupt enable */
+#define HCINTENB 0x04
+#define HCINTDIS 0x05
+#define HCFMINTVL 0x0d
+#define HCFMREM 0x0e
+#define HCFMNUM 0x0f
+#define HCLSTHRESH 0x11
+#define HCRHDESCA 0x12
+#define RH_A_NDP (0x3 << 0) /* # downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* overcurrent protection
+ mode */
+#define RH_A_NOCP (1 << 12) /* no overcurrent protection */
+#define RH_A_POTPGT (0xff << 24) /* power on -> power good
+ time */
+#define HCRHDESCB 0x13
+#define RH_B_DR (0xffff << 0) /* device removable flags */
+#define RH_B_PPCM (0xffff << 16) /* port power control mask */
+#define HCRHSTATUS 0x14
+#define RH_HS_LPS (1 << 0) /* local power status */
+#define RH_HS_OCI (1 << 1) /* over current indicator */
+#define RH_HS_DRWE (1 << 15) /* device remote wakeup
+ enable */
+#define RH_HS_LPSC (1 << 16) /* local power status change */
+#define RH_HS_OCIC (1 << 17) /* over current indicator
+ change */
+#define RH_HS_CRWE (1 << 31) /* clear remote wakeup
+ enable */
+#define HCRHPORT1 0x15
+#define RH_PS_CCS (1 << 0) /* current connect status */
+#define RH_PS_PES (1 << 1) /* port enable status */
+#define RH_PS_PSS (1 << 2) /* port suspend status */
+#define RH_PS_POCI (1 << 3) /* port over current
+ indicator */
+#define RH_PS_PRS (1 << 4) /* port reset status */
+#define RH_PS_PPS (1 << 8) /* port power status */
+#define RH_PS_LSDA (1 << 9) /* low speed device attached */
+#define RH_PS_CSC (1 << 16) /* connect status change */
+#define RH_PS_PESC (1 << 17) /* port enable status change */
+#define RH_PS_PSSC (1 << 18) /* port suspend status
+ change */
+#define RH_PS_OCIC (1 << 19) /* over current indicator
+ change */
+#define RH_PS_PRSC (1 << 20) /* port reset status change */
+#define HCRHPORT_CLRMASK (0x1f << 16)
+#define HCRHPORT2 0x16
+#define HCHWCFG 0x20
+#define HCHWCFG_15KRSEL (1 << 12)
+#define HCHWCFG_CLKNOTSTOP (1 << 11)
+#define HCHWCFG_ANALOG_OC (1 << 10)
+#define HCHWCFG_DACK_MODE (1 << 8)
+#define HCHWCFG_EOT_POL (1 << 7)
+#define HCHWCFG_DACK_POL (1 << 6)
+#define HCHWCFG_DREQ_POL (1 << 5)
+#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
+#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define HCHWCFG_INT_POL (1 << 2)
+#define HCHWCFG_INT_TRIGGER (1 << 1)
+#define HCHWCFG_INT_ENABLE (1 << 0)
+#define HCDMACFG 0x21
+#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
+#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
+#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
+#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
+#define HCDMACFG_DMA_ENABLE (1 << 4)
+#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
+#define HCDMACFG_CTR_SEL (1 << 2)
+#define HCDMACFG_ITLATL_SEL (1 << 1)
+#define HCDMACFG_DMA_RW_SELECT (1 << 0)
+#define HCXFERCTR 0x22
+#define HCuPINT 0x24
+#define HCuPINT_SOF (1 << 0)
+#define HCuPINT_ATL (1 << 1)
+#define HCuPINT_AIIEOT (1 << 2)
+#define HCuPINT_OPR (1 << 4)
+#define HCuPINT_SUSP (1 << 5)
+#define HCuPINT_CLKRDY (1 << 6)
+#define HCuPINTENB 0x25
+#define HCCHIPID 0x27
+#define HCCHIPID_MASK 0xff00
+#define HCCHIPID_MAGIC 0x6100
+#define HCSCRATCH 0x28
+#define HCSWRES 0x29
+#define HCSWRES_MAGIC 0x00f6
+#define HCITLBUFLEN 0x2a
+#define HCATLBUFLEN 0x2b
+#define HCBUFSTAT 0x2c
+#define HCBUFSTAT_ITL0_FULL (1 << 0)
+#define HCBUFSTAT_ITL1_FULL (1 << 1)
+#define HCBUFSTAT_ATL_FULL (1 << 2)
+#define HCBUFSTAT_ITL0_DONE (1 << 3)
+#define HCBUFSTAT_ITL1_DONE (1 << 4)
+#define HCBUFSTAT_ATL_DONE (1 << 5)
+#define HCRDITL0LEN 0x2d
+#define HCRDITL1LEN 0x2e
+#define HCITLPORT 0x40
+#define HCATLPORT 0x41
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13)
+#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK)
+#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7)
+#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK)
+
+/* Hardware transfer status codes -- CC from ptd->count */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED 0x0F
+
+/* ------------------------------------------------------------------------- */
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+/* Philips transfer descriptor */
+struct ptd {
+ u16 count;
+#define PTD_COUNT_MSK (0x3ff << 0)
+#define PTD_TOGGLE_MSK (1 << 10)
+#define PTD_ACTIVE_MSK (1 << 11)
+#define PTD_CC_MSK (0xf << 12)
+ u16 mps;
+#define PTD_MPS_MSK (0x3ff << 0)
+#define PTD_SPD_MSK (1 << 10)
+#define PTD_LAST_MSK (1 << 11)
+#define PTD_EP_MSK (0xf << 12)
+ u16 len;
+#define PTD_LEN_MSK (0x3ff << 0)
+#define PTD_DIR_MSK (3 << 10)
+#define PTD_DIR_SETUP (0)
+#define PTD_DIR_OUT (1)
+#define PTD_DIR_IN (2)
+#define PTD_B5_5_MSK (1 << 13)
+ u16 faddr;
+#define PTD_FA_MSK (0x7f << 0)
+#define PTD_FMT_MSK (1 << 7)
+} __attribute__ ((packed, aligned(2)));
+
+struct isp116x_ep {
+ struct usb_device *udev;
+ struct ptd ptd;
+
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+
+ u16 length; /* of current packet */
+ unsigned char *data; /* to databuf */
+
+ u16 error_count;
+};
+
+/* URB struct */
+#define N_URB_TD 48
+#define URB_DEL 1
+typedef struct {
+ struct isp116x_ep *ed;
+ void *transfer_buffer; /* (in) associated data buffer */
+ int actual_length; /* (return) actual transfer length */
+ unsigned long pipe; /* (in) pipe information */
+#if 0
+ int state;
+#endif
+} urb_priv_t;
+
+struct isp116x_platform_data {
+ /* Enable internal resistors on downstream ports */
+ unsigned sel15Kres:1;
+ /* On-chip overcurrent detection */
+ unsigned oc_enable:1;
+ /* Enable wakeup by devices on usb bus (e.g. wakeup
+ by attachment/detachment or by device activity
+ such as moving a mouse). When chosen, this option
+ prevents stopping internal clock, increasing
+ thereby power consumption in suspended state. */
+ unsigned remote_wakeup_enable:1;
+};
+
+struct isp116x {
+ u16 *addr_reg;
+ u16 *data_reg;
+
+ struct isp116x_platform_data *board;
+
+ struct dentry *dentry;
+ unsigned long stat1, stat2, stat4, stat8, stat16;
+
+ /* Status flags */
+ unsigned disabled:1;
+ unsigned sleeping:1;
+
+ /* Root hub registers */
+ u32 rhdesca;
+ u32 rhdescb;
+ u32 rhstatus;
+ u32 rhport[2];
+
+ /* Schedule for the current frame */
+ struct isp116x_ep *atl_active;
+ int atl_buflen;
+ int atl_bufshrt;
+ int atl_last_dir;
+ int atl_finishing;
+};
+
+/* ------------------------------------------------- */
+
+/* Inter-io delay (ns). The chip is picky about access timings; it
+ * expects at least:
+ * 150ns delay between consecutive accesses to DATA_REG,
+ * 300ns delay between access to ADDR_REG and DATA_REG
+ * OE, WE MUST NOT be changed during these intervals
+ */
+#if defined(UDELAY)
+#define isp116x_delay(h,d) udelay(d)
+#else
+#define isp116x_delay(h,d) do {} while (0)
+#endif
+
+static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
+{
+ writew(reg & 0xff, isp116x->addr_reg);
+ isp116x_delay(isp116x, UDELAY);
+}
+
+static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
+{
+ writew(val, isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+}
+
+static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
+{
+ __raw_writew(val, isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+}
+
+static inline u16 isp116x_read_data16(struct isp116x *isp116x)
+{
+ u16 val;
+
+ val = readw(isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+ return val;
+}
+
+static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
+{
+ u16 val;
+
+ val = __raw_readw(isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+ return val;
+}
+
+static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
+{
+ writew(val & 0xffff, isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+ writew(val >> 16, isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+}
+
+static inline u32 isp116x_read_data32(struct isp116x *isp116x)
+{
+ u32 val;
+
+ val = (u32) readw(isp116x->data_reg);
+ isp116x_delay(isp116x, UDELAY);
+ val |= ((u32) readw(isp116x->data_reg)) << 16;
+ isp116x_delay(isp116x, UDELAY);
+ return val;
+}
+
+/* Let's keep register access functions out of line. Hint:
+ we wait at least 150 ns at every access.
+*/
+static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
+{
+ isp116x_write_addr(isp116x, reg);
+ return isp116x_read_data16(isp116x);
+}
+
+static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
+{
+ isp116x_write_addr(isp116x, reg);
+ return isp116x_read_data32(isp116x);
+}
+
+static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
+ unsigned val)
+{
+ isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+ isp116x_write_data16(isp116x, (u16) (val & 0xffff));
+}
+
+static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
+ unsigned val)
+{
+ isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
+ isp116x_write_data32(isp116x, (u32) val);
+}
+
+/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */
+
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c
new file mode 100644
index 0000000000..d6b745fadd
--- /dev/null
+++ b/drivers/usb_ohci.c
@@ -0,0 +1,1929 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus.
+ *
+ * Interrupt support is added. Now, it has been tested
+ * on ULI1575 chip and works well with USB keyboard.
+ *
+ * (C) Copyright 2007
+ * Zhang Wei, Freescale Semiconductor, Inc. <wei.zhang@freescale.com>
+ *
+ * (C) Copyright 2003
+ * Gary Jennejohn, DENX Software Engineering <gj@denx.de>
+ *
+ * Note: Much of this code has been derived from Linux 2.4
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell
+ *
+ * Modified for the MP2USB by (C) Copyright 2005 Eric Benard
+ * ebenard@eukrea.com - based on s3c24x0's driver
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+/*
+ * IMPORTANT NOTES
+ * 1 - Read doc/README.generic_usb_ohci
+ * 2 - this driver is intended for use with USB Mass Storage Devices
+ * (BBB) and USB keyboard. There is NO support for Isochronous pipes!
+ * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG
+ * to activate workaround for bug #41 or this driver will NOT work!
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_USB_OHCI_NEW
+
+#include <asm/byteorder.h>
+
+#if defined(CONFIG_PCI_OHCI)
+# include <pci.h>
+#endif
+
+#include <malloc.h>
+#include <usb.h>
+#include "usb_ohci.h"
+
+#if defined(CONFIG_ARM920T) || \
+ defined(CONFIG_S3C2400) || \
+ defined(CONFIG_S3C2410) || \
+ defined(CONFIG_440EP) || \
+ defined(CONFIG_PCI_OHCI) || \
+ defined(CONFIG_MPC5200)
+# define OHCI_USE_NPS /* force NoPowerSwitching mode */
+#endif
+
+#undef OHCI_VERBOSE_DEBUG /* not always helpful */
+#undef DEBUG
+#undef SHOW_INFO
+#undef OHCI_FILL_TRACE
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT \
+ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+/*
+ * e.g. PCI controllers need this
+ */
+#ifdef CFG_OHCI_SWAP_REG_ACCESS
+# define readl(a) __swap_32(*((vu_long *)(a)))
+# define writel(a, b) (*((vu_long *)(b)) = __swap_32((vu_long)a))
+#else
+# define readl(a) (*((vu_long *)(a)))
+# define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a))
+#endif /* CFG_OHCI_SWAP_REG_ACCESS */
+
+#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+
+#ifdef CONFIG_PCI_OHCI
+static struct pci_device_id ohci_pci_ids[] = {
+ {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */
+ /* Please add supported PCI OHCI controller ids here */
+ {0, 0}
+};
+#endif
+
+#ifdef DEBUG
+#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while(0)
+#endif /* DEBUG */
+#define err(format, arg...) printf("ERROR: " format "\n", ## arg)
+#undef SHOW_INFO
+#ifdef SHOW_INFO
+#define info(format, arg...) printf("INFO: " format "\n", ## arg)
+#else
+#define info(format, arg...) do {} while(0)
+#endif
+
+#ifdef CFG_OHCI_BE_CONTROLLER
+# define m16_swap(x) cpu_to_be16(x)
+# define m32_swap(x) cpu_to_be32(x)
+#else
+# define m16_swap(x) cpu_to_le16(x)
+# define m32_swap(x) cpu_to_le32(x)
+#endif /* CFG_OHCI_BE_CONTROLLER */
+
+/* global ohci_t */
+static ohci_t gohci;
+/* this must be aligned to a 256 byte boundary */
+struct ohci_hcca ghcca[1];
+/* a pointer to the aligned storage */
+struct ohci_hcca *phcca;
+/* this allocates EDs for all possible endpoints */
+struct ohci_device ohci_dev;
+/* RHSC flag */
+int got_rhsc;
+/* device which was disconnected */
+struct usb_device *devgone;
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define OHCI_QUIRK_AMD756 0xabcd
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = readl (&hc->regs->roothub.register); \
+ if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = readl (&hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci *hc)
+ { return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci *hc)
+ { return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+
+/* forward declaration */
+static int hc_interrupt (void);
+static void
+td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer,
+ int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval);
+
+/*-------------------------------------------------------------------------*
+ * URB support functions
+ *-------------------------------------------------------------------------*/
+
+/* free HCD-private data associated with this URB */
+
+static void urb_free_priv (urb_priv_t * urb)
+{
+ int i;
+ int last;
+ struct td * td;
+
+ last = urb->length - 1;
+ if (last >= 0) {
+ for (i = 0; i <= last; i++) {
+ td = urb->td[i];
+ if (td) {
+ td->usb_dev = NULL;
+ urb->td[i] = NULL;
+ }
+ }
+ }
+ free(urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+static int sohci_get_current_frame_number (struct usb_device * dev);
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header */
+
+static void pkt_print (urb_priv_t *purb, struct usb_device * dev,
+ unsigned long pipe, void * buffer,
+ int transfer_len, struct devrequest * setup, char * str, int small)
+{
+ dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx",
+ str,
+ sohci_get_current_frame_number (dev),
+ usb_pipedevice (pipe),
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)? 'O': 'I',
+ usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
+ (usb_pipecontrol (pipe)? "CTRL": "BULK"),
+ (purb ? purb->actual_length : 0),
+ transfer_len, dev->status);
+#ifdef OHCI_VERBOSE_DEBUG
+ if (!small) {
+ int i, len;
+
+ if (usb_pipecontrol (pipe)) {
+ printf (__FILE__ ": cmd(8):");
+ for (i = 0; i < 8 ; i++)
+ printf (" %02x", ((__u8 *) setup) [i]);
+ printf ("\n");
+ }
+ if (transfer_len > 0 && buffer) {
+ printf (__FILE__ ": data(%d/%d):",
+ (purb ? purb->actual_length : 0),
+ transfer_len);
+ len = usb_pipeout (pipe)?
+ transfer_len:
+ (purb ? purb->actual_length : 0);
+ for (i = 0; i < 16 && i < len; i++)
+ printf (" %02x", ((__u8 *) buffer) [i]);
+ printf ("%s\n", i < len? "...": "");
+ }
+ }
+#endif
+}
+
+/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
+void ep_print_int_eds (ohci_t *ohci, char * str) {
+ int i, j;
+ __u32 * ed_p;
+ for (i= 0; i < 32; i++) {
+ j = 5;
+ ed_p = &(ohci->hcca->int_table [i]);
+ if (*ed_p == 0)
+ continue;
+ printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i);
+ while (*ed_p != 0 && j--) {
+ ed_t *ed = (ed_t *)m32_swap(ed_p);
+ printf (" ed: %4x;", ed->hwINFO);
+ ed_p = &ed->hwNextED;
+ }
+ printf ("\n");
+ }
+}
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+ dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+ ed_t *edp = (ed_t *)value;
+
+ if (value) {
+ dbg ("%s %08x", label, value);
+ dbg ("%08x", edp->hwINFO);
+ dbg ("%08x", edp->hwTailP);
+ dbg ("%08x", edp->hwHeadP);
+ dbg ("%08x", edp->hwNextED);
+ }
+}
+
+static char * hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+/* dump control and status registers */
+static void ohci_dump_status (ohci_t *controller)
+{
+ struct ohci_regs *regs = controller->regs;
+ __u32 temp;
+
+ temp = readl (&regs->revision) & 0xff;
+ if (temp != 0x10)
+ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+ temp = readl (&regs->control);
+ dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = readl (&regs->cmdstatus);
+ dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
+ ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+
+ maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+ maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+ maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+ maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+ maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+ maybe_print_eds ("donehead", readl (&regs->donehead));
+}
+
+static void ohci_dump_roothub (ohci_t *controller, int verbose)
+{
+ __u32 temp, ndp, i;
+
+ temp = roothub_a (controller);
+ ndp = (temp & RH_A_NDP);
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+ ndp = (ndp == 2) ? 1:0;
+#endif
+ if (verbose) {
+ dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = roothub_b (controller);
+ dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status (controller);
+ dbg ("roothub.status: %08x%s%s%s%s%s%s",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
+ i,
+ temp,
+ (temp & RH_PS_PRSC) ? " PRSC" : "",
+ (temp & RH_PS_OCIC) ? " OCIC" : "",
+ (temp & RH_PS_PSSC) ? " PSSC" : "",
+ (temp & RH_PS_PESC) ? " PESC" : "",
+ (temp & RH_PS_CSC) ? " CSC" : "",
+
+ (temp & RH_PS_LSDA) ? " LSDA" : "",
+ (temp & RH_PS_PPS) ? " PPS" : "",
+ (temp & RH_PS_PRS) ? " PRS" : "",
+ (temp & RH_PS_POCI) ? " POCI" : "",
+ (temp & RH_PS_PSS) ? " PSS" : "",
+
+ (temp & RH_PS_PES) ? " PES" : "",
+ (temp & RH_PS_CCS) ? " CCS" : ""
+ );
+ }
+}
+
+static void ohci_dump (ohci_t *controller, int verbose)
+{
+ dbg ("OHCI controller usb-%s state", controller->slot_name);
+
+ /* dumps some of the state we know about */
+ ohci_dump_status (controller);
+ if (verbose)
+ ep_print_int_eds (controller, "hcca");
+ dbg ("hcca frame #%04x", controller->hcca->frame_no);
+ ohci_dump_roothub (controller, 1);
+}
+
+
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*
+ * Interface functions (URB)
+ *-------------------------------------------------------------------------*/
+
+/* get a transfer request */
+
+int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
+{
+ ohci_t *ohci;
+ ed_t * ed;
+ urb_priv_t *purb_priv = urb;
+ int i, size = 0;
+ struct usb_device *dev = urb->dev;
+ unsigned long pipe = urb->pipe;
+ void *buffer = urb->transfer_buffer;
+ int transfer_len = urb->transfer_buffer_length;
+ int interval = urb->interval;
+
+ ohci = &gohci;
+
+ /* when controller's hung, permit only roothub cleanup attempts
+ * such as powering down ports */
+ if (ohci->disabled) {
+ err("sohci_submit_job: EPIPE");
+ return -1;
+ }
+
+ /* we're about to begin a new transaction here so mark the URB unfinished */
+ urb->finished = 0;
+
+ /* every endpoint has a ed, locate and fill it */
+ if (!(ed = ep_add_ed (dev, pipe, interval, 1))) {
+ err("sohci_submit_job: ENOMEM");
+ return -1;
+ }
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (usb_pipetype (pipe)) {
+ case PIPE_BULK: /* one TD for every 4096 Byte */
+ size = (transfer_len - 1) / 4096 + 1;
+ break;
+ case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
+ size = (transfer_len == 0)? 2:
+ (transfer_len - 1) / 4096 + 3;
+ break;
+ case PIPE_INTERRUPT: /* 1 TD */
+ size = 1;
+ break;
+ }
+
+ ed->purb = urb;
+
+ if (size >= (N_URB_TD - 1)) {
+ err("need %d TDs, only have %d", size, N_URB_TD);
+ return -1;
+ }
+ purb_priv->pipe = pipe;
+
+ /* fill the private part of the URB */
+ purb_priv->length = size;
+ purb_priv->ed = ed;
+ purb_priv->actual_length = 0;
+
+ /* allocate the TDs */
+ /* note that td[0] was allocated in ep_add_ed */
+ for (i = 0; i < size; i++) {
+ purb_priv->td[i] = td_alloc (dev);
+ if (!purb_priv->td[i]) {
+ purb_priv->length = i;
+ urb_free_priv (purb_priv);
+ err("sohci_submit_job: ENOMEM");
+ return -1;
+ }
+ }
+
+ if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+ urb_free_priv (purb_priv);
+ err("sohci_submit_job: EINVAL");
+ return -1;
+ }
+
+ /* link the ed into a chain if is not already */
+ if (ed->state != ED_OPER)
+ ep_link (ohci, ed);
+
+ /* fill the TDs and link it to the ed */
+ td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval);
+
+ return 0;
+}
+
+static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb)
+{
+ struct ohci_regs *regs = hc->regs;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_INTERRUPT:
+ /* implicitly requeued */
+ if (urb->dev->irq_handle &&
+ (urb->dev->irq_act_len = urb->actual_length)) {
+ writel (OHCI_INTR_WDH, &regs->intrenable);
+ readl (&regs->intrenable); /* PCI posting flush */
+ urb->dev->irq_handle(urb->dev);
+ writel (OHCI_INTR_WDH, &regs->intrdisable);
+ readl (&regs->intrdisable); /* PCI posting flush */
+ }
+ urb->actual_length = 0;
+ td_submit_job (
+ urb->dev,
+ urb->pipe,
+ urb->transfer_buffer,
+ urb->transfer_buffer_length,
+ NULL,
+ urb,
+ urb->interval);
+ break;
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+/* tell us the current USB frame number */
+
+static int sohci_get_current_frame_number (struct usb_device *usb_dev)
+{
+ ohci_t *ohci = &gohci;
+
+ return m16_swap (ohci->hcca->frame_no);
+}
+#endif
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right branch to insert an interrupt ed into the int tree
+ * do some load ballancing;
+ * returns the branch and
+ * sets the interval to interval = 2^integer (ld (interval)) */
+
+static int ep_int_ballance (ohci_t * ohci, int interval, int load)
+{
+ int i, branch = 0;
+
+ /* search for the least loaded interrupt endpoint
+ * branch of all 32 branches
+ */
+ for (i = 0; i < 32; i++)
+ if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])
+ branch = i;
+
+ branch = branch % interval;
+ for (i = branch; i < 32; i += interval)
+ ohci->ohci_int_load [i] += load;
+
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 2^int( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{
+ int i;
+ for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
+ return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree
+ * in order to process it sequentially the indexes of the branches have to be mapped
+ * the mapping reverses the bits of a word of num_bits length */
+
+static int ep_rev (int num_bits, int word)
+{
+ int i, wout = 0;
+
+ for (i = 0; i < num_bits; i++)
+ wout |= (((word >> i) & 1) << (num_bits - i - 1));
+ return wout;
+}
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (ohci_t *ohci, ed_t *edi)
+{
+ volatile ed_t *ed = edi;
+ int int_branch;
+ int i;
+ int inter;
+ int interval;
+ int load;
+ __u32 * ed_p;
+
+ ed->state = ED_OPER;
+ ed->int_interval = 0;
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ed->hwNextED = 0;
+ if (ohci->ed_controltail == NULL) {
+ writel (ed, &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
+ !ohci->ed_rm_list[1] && !ohci->sleeping) {
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_controltail = edi;
+ break;
+
+ case PIPE_BULK:
+ ed->hwNextED = 0;
+ if (ohci->ed_bulktail == NULL) {
+ writel (ed, &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
+ !ohci->ed_rm_list[1] && !ohci->sleeping) {
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_bulktail = edi;
+ break;
+
+ case PIPE_INTERRUPT:
+ load = ed->int_load;
+ interval = ep_2_n_interval (ed->int_period);
+ ed->int_interval = interval;
+ int_branch = ep_int_ballance (ohci, interval, load);
+ ed->int_branch = int_branch;
+
+ for (i = 0; i < ep_rev (6, interval); i += inter) {
+ inter = 1;
+ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);
+ (*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval);
+ ed_p = &(((ed_t *)ed_p)->hwNextED))
+ inter = ep_rev (6, ((ed_t *)ed_p)->int_interval);
+ ed->hwNextED = *ed_p;
+ *ed_p = m32_swap(ed);
+ }
+ break;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed,
+ unsigned index, unsigned period)
+{
+ for (; index < NUM_INTS; index += period) {
+ __u32 *ed_p = &ohci->hcca->int_table [index];
+
+ /* ED might have been unlinked through another path */
+ while (*ed_p != 0) {
+ if (((struct ed *)m32_swap (ed_p)) == ed) {
+ *ed_p = ed->hwNextED;
+ break;
+ }
+ ed_p = & (((struct ed *)m32_swap (ed_p))->hwNextED);
+ }
+ }
+}
+
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed */
+
+static int ep_unlink (ohci_t *ohci, ed_t *edi)
+{
+ volatile ed_t *ed = edi;
+ int i;
+
+ ed->hwINFO |= m32_swap (OHCI_ED_SKIP);
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ } else {
+ ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_BULK:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ } else {
+ ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_INTERRUPT:
+ periodic_unlink (ohci, ed, 0, 1);
+ for (i = ed->int_branch; i < 32; i += ed->int_interval)
+ ohci->ohci_int_load[i] -= ed->int_load;
+ break;
+ }
+ ed->state = ED_UNLINK;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* add/reinit an endpoint; this should be done once at the
+ * usb_set_configuration command, but the USB stack is a little bit
+ * stateless so we do it at every transaction if the state of the ed
+ * is ED_NEW then a dummy td is added and the state is changed to
+ * ED_UNLINK in all other cases the state is left unchanged the ed
+ * info fields are setted anyway even though most of them should not
+ * change
+ */
+static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe,
+ int interval, int load)
+{
+ td_t *td;
+ ed_t *ed_ret;
+ volatile ed_t *ed;
+
+ ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) |
+ (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))];
+
+ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
+ err("ep_add_ed: pending delete");
+ /* pending delete request */
+ return NULL;
+ }
+
+ if (ed->state == ED_NEW) {
+ ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */
+ /* dummy td; end of td list for ed */
+ td = td_alloc (usb_dev);
+ ed->hwTailP = m32_swap ((unsigned long)td);
+ ed->hwHeadP = ed->hwTailP;
+ ed->state = ED_UNLINK;
+ ed->type = usb_pipetype (pipe);
+ ohci_dev.ed_cnt++;
+ }
+
+ ed->hwINFO = m32_swap (usb_pipedevice (pipe)
+ | usb_pipeendpoint (pipe) << 7
+ | (usb_pipeisoc (pipe)? 0x8000: 0)
+ | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
+ | usb_pipeslow (pipe) << 13
+ | usb_maxpacket (usb_dev, pipe) << 16);
+
+ if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
+ ed->int_period = interval;
+ ed->int_load = load;
+ }
+
+ return ed_ret;
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void td_fill (ohci_t *ohci, unsigned int info,
+ void *data, int len,
+ struct usb_device *dev, int index, urb_priv_t *urb_priv)
+{
+ volatile td_t *td, *td_pt;
+#ifdef OHCI_FILL_TRACE
+ int i;
+#endif
+
+ if (index > urb_priv->length) {
+ err("index > length");
+ return;
+ }
+ /* use this td as the next dummy */
+ td_pt = urb_priv->td [index];
+ td_pt->hwNextTD = 0;
+
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf);
+
+ td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
+ td->index = index;
+ td->data = (__u32)data;
+#ifdef OHCI_FILL_TRACE
+ if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) {
+ for (i = 0; i < len; i++)
+ printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
+ printf("\n");
+ }
+#endif
+ if (!len)
+ data = 0;
+
+ td->hwINFO = m32_swap (info);
+ td->hwCBP = m32_swap ((unsigned long)data);
+ if (data)
+ td->hwBE = m32_swap ((unsigned long)(data + len - 1));
+ else
+ td->hwBE = 0;
+ td->hwNextTD = m32_swap ((unsigned long)td_pt);
+
+ /* append to queue */
+ td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare all TDs of a transfer */
+
+static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval)
+{
+ ohci_t *ohci = &gohci;
+ int data_len = transfer_len;
+ void *data;
+ int cnt = 0;
+ __u32 info = 0;
+ unsigned int toggle = 0;
+
+ /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
+ if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
+ toggle = TD_T_TOGGLE;
+ } else {
+ toggle = TD_T_DATA0;
+ usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);
+ }
+ urb->td_cnt = 0;
+ if (data_len)
+ data = buffer;
+ else
+ data = 0;
+
+ switch (usb_pipetype (pipe)) {
+ case PIPE_BULK:
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
+ while(data_len > 4096) {
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb);
+ data += 4096; data_len -= 4096; cnt++;
+ }
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb);
+ cnt++;
+
+ if (!ohci->sleeping)
+ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ break;
+
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (ohci, info, setup, 8, dev, cnt++, urb);
+ if (data_len > 0) {
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
+ /* NOTE: mishandles transfers >8K, some >4K */
+ td_fill (ohci, info, data, data_len, dev, cnt++, urb);
+ }
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (ohci, info, data, 0, dev, cnt++, urb);
+ if (!ohci->sleeping)
+ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+ break;
+
+ case PIPE_INTERRUPT:
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_DP_OUT | toggle:
+ TD_CC | TD_R | TD_DP_IN | toggle;
+ td_fill (ohci, info, data, data_len, dev, cnt++, urb);
+ break;
+ }
+ if (urb->length != cnt)
+ dbg("TD LENGTH %d != CNT %d", urb->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length(td_t * td)
+{
+ __u32 tdINFO, tdBE, tdCBP;
+ urb_priv_t *lurb_priv = td->ed->purb;
+
+ tdINFO = m32_swap (td->hwINFO);
+ tdBE = m32_swap (td->hwBE);
+ tdCBP = m32_swap (td->hwCBP);
+
+
+ if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL &&
+ ((td->index == 0) || (td->index == lurb_priv->length - 1)))) {
+ if (tdBE != 0) {
+ if (td->hwCBP == 0)
+ lurb_priv->actual_length += tdBE - td->data + 1;
+ else
+ lurb_priv->actual_length += tdCBP - td->data;
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+
+static td_t * dl_reverse_done_list (ohci_t *ohci)
+{
+ __u32 td_list_hc;
+ td_t *td_rev = NULL;
+ td_t *td_list = NULL;
+ urb_priv_t *lurb_priv = NULL;
+
+ td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0;
+ ohci->hcca->done_head = 0;
+
+ while (td_list_hc) {
+ td_list = (td_t *)td_list_hc;
+
+ if (TD_CC_GET (m32_swap (td_list->hwINFO))) {
+ lurb_priv = td_list->ed->purb;
+ dbg(" USB-error/status: %x : %p",
+ TD_CC_GET (m32_swap (td_list->hwINFO)), td_list);
+ if (td_list->ed->hwHeadP & m32_swap (0x1)) {
+ if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) {
+ td_list->ed->hwHeadP =
+ (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) |
+ (td_list->ed->hwHeadP & m32_swap (0x2));
+ lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1;
+ } else
+ td_list->ed->hwHeadP &= m32_swap (0xfffffff2);
+ }
+#ifdef CONFIG_MPC5200
+ td_list->hwNextTD = 0;
+#endif
+ }
+
+ td_list->next_dl_td = td_rev;
+ td_rev = td_list;
+ td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0;
+ }
+ return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+static int dl_done_list (ohci_t *ohci, td_t *td_list)
+{
+ td_t *td_list_next = NULL;
+ ed_t *ed;
+ int cc = 0;
+ int stat = 0;
+ /* urb_t *urb; */
+ urb_priv_t *lurb_priv;
+ __u32 tdINFO, edHeadP, edTailP;
+
+ while (td_list) {
+ td_list_next = td_list->next_dl_td;
+
+ tdINFO = m32_swap (td_list->hwINFO);
+
+ ed = td_list->ed;
+ lurb_priv = ed->purb;
+
+ dl_transfer_length(td_list);
+
+ /* error code of transfer */
+ cc = TD_CC_GET (tdINFO);
+ if (cc != 0) {
+ dbg("ConditionCode %#x", cc);
+ stat = cc_to_error[cc];
+ }
+
+ /* see if this done list makes for all TD's of current URB,
+ * and mark the URB finished if so */
+ if (++(lurb_priv->td_cnt) == lurb_priv->length) {
+#if 1
+ if ((ed->state & (ED_OPER | ED_UNLINK)) &&
+ (lurb_priv->state != URB_DEL))
+#else
+ if ((ed->state & (ED_OPER | ED_UNLINK)))
+#endif
+ lurb_priv->finished = sohci_return_job(ohci,
+ lurb_priv);
+ else
+ dbg("dl_done_list: strange.., ED state %x, ed->state\n");
+ } else
+ dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt,
+ lurb_priv->length);
+ if (ed->state != ED_NEW &&
+ (usb_pipetype (lurb_priv->pipe) != PIPE_INTERRUPT)) {
+ edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0;
+ edTailP = m32_swap (ed->hwTailP);
+
+ /* unlink eds if they are not busy */
+ if ((edHeadP == edTailP) && (ed->state == ED_OPER))
+ ep_unlink (ohci, ed);
+ }
+
+ td_list = td_list_next;
+ }
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub
+ *-------------------------------------------------------------------------*/
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x10, /* __u16 bcdUSB; v1.1 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x01, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] =
+{
+ 0x04, /* __u8 bLength; */
+ 0x03, /* __u8 bDescriptorType; String-descriptor */
+ 0x09, /* __u8 lang ID */
+ 0x04, /* __u8 lang ID */
+};
+
+static unsigned char root_hub_str_index1[] =
+{
+ 28, /* __u8 bLength; */
+ 0x03, /* __u8 bDescriptorType; String-descriptor */
+ 'O', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'H', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'C', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'I', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ ' ', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'R', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'o', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'o', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 't', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ ' ', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'H', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'u', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'b', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+};
+
+/* Hub class-specific descriptor is constructed dynamically */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define OK(x) len = (x); break
+#ifdef DEBUG
+#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);}
+#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);}
+#else
+#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status)
+#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1])
+#endif
+#define RD_RH_STAT roothub_status(&gohci)
+#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1)
+
+/* request to virtual root hub */
+
+int rh_check_port_status(ohci_t *controller)
+{
+ __u32 temp, ndp, i;
+ int res;
+
+ res = -1;
+ temp = roothub_a (controller);
+ ndp = (temp & RH_A_NDP);
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+ ndp = (ndp == 2) ? 1:0;
+#endif
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ /* check for a device disconnect */
+ if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
+ (RH_PS_PESC | RH_PS_CSC)) &&
+ ((temp & RH_PS_CCS) == 0)) {
+ res = i;
+ break;
+ }
+ }
+ return res;
+}
+
+static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int transfer_len, struct devrequest *cmd)
+{
+ void * data = buffer;
+ int leni = transfer_len;
+ int len = 0;
+ int stat = 0;
+ __u32 datab[4];
+ __u8 *data_buf = (__u8 *)datab;
+ __u16 bmRType_bReq;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+
+#ifdef DEBUG
+pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+ if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+ info("Root-Hub submit IRQ: NOT implemented");
+ return 0;
+ }
+
+ bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+ wValue = cpu_to_le16 (cmd->value);
+ wIndex = cpu_to_le16 (cmd->index);
+ wLength = cpu_to_le16 (cmd->length);
+
+ info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x",
+ dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
+
+ switch (bmRType_bReq) {
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
+
+ case RH_GET_STATUS:
+ *(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *) data_buf = cpu_to_le32 (
+ RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+ OK (4);
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL): OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ case RH_C_HUB_LOCAL_POWER:
+ OK(0);
+ case (RH_C_HUB_OVER_CURRENT):
+ WR_RH_STAT(RH_HS_OCIC); OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
+ case (RH_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
+ case (RH_PORT_POWER):
+ WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
+ case (RH_C_PORT_CONNECTION):
+ WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
+ case (RH_C_PORT_ENABLE):
+ WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
+ case (RH_C_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
+ case (RH_C_PORT_OVER_CURRENT):
+ WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
+ case (RH_C_PORT_RESET):
+ WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
+ case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
+ if (RD_RH_PORTSTAT & RH_PS_CCS)
+ WR_RH_PORTSTAT (RH_PS_PRS);
+ OK (0);
+ case (RH_PORT_POWER):
+ WR_RH_PORTSTAT (RH_PS_PPS );
+ wait_ms(100);
+ OK (0);
+ case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
+ if (RD_RH_PORTSTAT & RH_PS_CCS)
+ WR_RH_PORTSTAT (RH_PS_PES );
+ OK (0);
+ }
+ break;
+
+ case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0);
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_dev_des),
+ wLength));
+ data_buf = root_hub_dev_des; OK(len);
+ case (0x02): /* configuration descriptor */
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_config_des),
+ wLength));
+ data_buf = root_hub_config_des; OK(len);
+ case (0x03): /* string descriptors */
+ if(wValue==0x0300) {
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_str_index0),
+ wLength));
+ data_buf = root_hub_str_index0;
+ OK(len);
+ }
+ if(wValue==0x0301) {
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_str_index1),
+ wLength));
+ data_buf = root_hub_str_index1;
+ OK(len);
+ }
+ default:
+ stat = USB_ST_STALLED;
+ }
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ {
+ __u32 temp = roothub_a (&gohci);
+
+ data_buf [0] = 9; /* min length; */
+ data_buf [1] = 0x29;
+ data_buf [2] = temp & RH_A_NDP;
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+ data_buf [2] = (data_buf [2] == 2) ? 1:0;
+#endif
+ data_buf [3] = 0;
+ if (temp & RH_A_PSM) /* per-port power switching? */
+ data_buf [3] |= 0x1;
+ if (temp & RH_A_NOCP) /* no overcurrent reporting? */
+ data_buf [3] |= 0x10;
+ else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */
+ data_buf [3] |= 0x8;
+
+ /* corresponds to data_buf[4-7] */
+ datab [1] = 0;
+ data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+ temp = roothub_b (&gohci);
+ data_buf [7] = temp & RH_B_DR;
+ if (data_buf [2] < 7) {
+ data_buf [8] = 0xff;
+ } else {
+ data_buf [0] += 2;
+ data_buf [8] = (temp & RH_B_DR) >> 8;
+ data_buf [10] = data_buf [9] = 0xff;
+ }
+
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int, data_buf [0], wLength));
+ OK (len);
+ }
+
+ case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
+
+ case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0);
+
+ default:
+ dbg ("unsupported root hub command");
+ stat = USB_ST_STALLED;
+ }
+
+#ifdef DEBUG
+ ohci_dump_roothub (&gohci, 1);
+#else
+ wait_ms(1);
+#endif
+
+ len = min_t(int, len, leni);
+ if (data != data_buf)
+ memcpy (data, data_buf, len);
+ dev->act_len = len;
+ dev->status = stat;
+
+#ifdef DEBUG
+ pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/);
+#else
+ wait_ms(1);
+#endif
+
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* common code for handling submit messages - used for all but root hub */
+/* accesses. */
+int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup, int interval)
+{
+ int stat = 0;
+ int maxsize = usb_maxpacket(dev, pipe);
+ int timeout;
+ urb_priv_t *urb;
+
+ urb = malloc(sizeof(urb_priv_t));
+ memset(urb, 0, sizeof(urb_priv_t));
+
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->transfer_buffer = buffer;
+ urb->transfer_buffer_length = transfer_len;
+ urb->interval = interval;
+
+ /* device pulled? Shortcut the action. */
+ if (devgone == dev) {
+ dev->status = USB_ST_CRC_ERR;
+ return 0;
+ }
+
+#ifdef DEBUG
+ urb->actual_length = 0;
+ pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+ if (!maxsize) {
+ err("submit_common_message: pipesize for pipe %lx is zero",
+ pipe);
+ return -1;
+ }
+
+ if (sohci_submit_job(urb, setup) < 0) {
+ err("sohci_submit_job failed");
+ return -1;
+ }
+
+#if 0
+ wait_ms(10);
+ /* ohci_dump_status(&gohci); */
+#endif
+
+ /* allow more time for a BULK device to react - some are slow */
+#define BULK_TO 5000 /* timeout in milliseconds */
+ if (usb_pipetype (pipe) == PIPE_BULK)
+ timeout = BULK_TO;
+ else
+ timeout = 100;
+
+ /* wait for it to complete */
+ for (;;) {
+ /* check whether the controller is done */
+ stat = hc_interrupt();
+ if (stat < 0) {
+ stat = USB_ST_CRC_ERR;
+ break;
+ }
+
+ /* NOTE: since we are not interrupt driven in U-Boot and always
+ * handle only one URB at a time, we cannot assume the
+ * transaction finished on the first successful return from
+ * hc_interrupt().. unless the flag for current URB is set,
+ * meaning that all TD's to/from device got actually
+ * transferred and processed. If the current URB is not
+ * finished we need to re-iterate this loop so as
+ * hc_interrupt() gets called again as there needs to be some
+ * more TD's to process still */
+ if ((stat >= 0) && (stat != 0xff) && (urb->finished)) {
+ /* 0xff is returned for an SF-interrupt */
+ break;
+ }
+
+ if (--timeout) {
+ wait_ms(1);
+ if (!urb->finished)
+ dbg("\%");
+
+ } else {
+ err("CTL:TIMEOUT ");
+ dbg("submit_common_msg: TO status %x\n", stat);
+ urb->finished = 1;
+ stat = USB_ST_CRC_ERR;
+ break;
+ }
+ }
+
+ dev->status = stat;
+ dev->act_len = transfer_len;
+
+#ifdef DEBUG
+ pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+
+ /* free TDs in urb_priv */
+ if (usb_pipetype (pipe) != PIPE_INTERRUPT)
+ urb_free_priv (urb);
+ return 0;
+}
+
+/* submit routines called from usb.c */
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len)
+{
+ info("submit_bulk_msg");
+ return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup)
+{
+ int maxsize = usb_maxpacket(dev, pipe);
+
+ info("submit_control_msg");
+#ifdef DEBUG
+ pkt_print(NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+ if (!maxsize) {
+ err("submit_control_message: pipesize for pipe %lx is zero",
+ pipe);
+ return -1;
+ }
+ if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
+ gohci.rh.dev = dev;
+ /* root hub - redirect */
+ return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
+ setup);
+ }
+
+ return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, int interval)
+{
+ info("submit_int_msg");
+ return submit_common_msg(dev, pipe, buffer, transfer_len, NULL,
+ interval);
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (ohci_t *ohci)
+{
+ int timeout = 30;
+ int smm_timeout = 50; /* 0,5 sec */
+
+ dbg("%s\n", __FUNCTION__);
+
+ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+ writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
+ info("USB HC TakeOver from SMM");
+ while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+ wait_ms (10);
+ if (--smm_timeout == 0) {
+ err("USB HC TakeOver failed!");
+ return -1;
+ }
+ }
+ }
+
+ /* Disable HC interrupts */
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n",
+ ohci->slot_name,
+ readl(&ohci->regs->control));
+
+ /* Reset USB (needed by some controllers) */
+ ohci->hc_control = 0;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* HC Reset requires max 10 us delay */
+ writel (OHCI_HCR, &ohci->regs->cmdstatus);
+ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--timeout == 0) {
+ err("USB HC reset timed out!");
+ return -1;
+ }
+ udelay (1);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub */
+
+static int hc_start (ohci_t * ohci)
+{
+ __u32 mask;
+ unsigned int fminterval;
+
+ ohci->disabled = 1;
+
+ /* Tell the controller where the control and bulk lists are
+ * The lists are empty now. */
+
+ writel (0, &ohci->regs->ed_controlhead);
+ writel (0, &ohci->regs->ed_bulkhead);
+
+ writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */
+
+ fminterval = 0x2edf;
+ writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+ fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
+ writel (fminterval, &ohci->regs->fminterval);
+ writel (0x628, &ohci->regs->lsthresh);
+
+ /* start controller operations */
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci->disabled = 0;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* disable all interrupts */
+ mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD |
+ OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC |
+ OHCI_INTR_OC | OHCI_INTR_MIE);
+ writel (mask, &ohci->regs->intrdisable);
+ /* clear all interrupts */
+ mask &= ~OHCI_INTR_MIE;
+ writel (mask, &ohci->regs->intrstatus);
+ /* Choose the interrupts we care about now - but w/o MIE */
+ mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+ writel (mask, &ohci->regs->intrenable);
+
+#ifdef OHCI_USE_NPS
+ /* required for AMD-756 and some Mac platforms */
+ writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
+ &ohci->regs->roothub.a);
+ writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif /* OHCI_USE_NPS */
+
+#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
+ /* POTPGT delay is bits 24-31, in 2 ms units. */
+ mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+
+ /* connect the virtual root hub */
+ ohci->rh.devnum = 0;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Poll USB interrupt. */
+void usb_event_poll(void)
+{
+ hc_interrupt();
+}
+
+/* an interrupt happens */
+
+static int hc_interrupt (void)
+{
+ ohci_t *ohci = &gohci;
+ struct ohci_regs *regs = ohci->regs;
+ int ints;
+ int stat = -1;
+
+ if ((ohci->hcca->done_head != 0) &&
+ !(m32_swap (ohci->hcca->done_head) & 0x01)) {
+ ints = OHCI_INTR_WDH;
+ } else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
+ ohci->disabled++;
+ err ("%s device removed!", ohci->slot_name);
+ return -1;
+ } else if ((ints &= readl (&regs->intrenable)) == 0) {
+ dbg("hc_interrupt: returning..\n");
+ return 0xff;
+ }
+
+ /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */
+
+ if (ints & OHCI_INTR_RHSC) {
+ got_rhsc = 1;
+ stat = 0xff;
+ }
+
+ if (ints & OHCI_INTR_UE) {
+ ohci->disabled++;
+ err ("OHCI Unrecoverable Error, controller usb-%s disabled",
+ ohci->slot_name);
+ /* e.g. due to PCI Master/Target Abort */
+
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#else
+ wait_ms(1);
+#endif
+ /* FIXME: be optimistic, hope that bug won't repeat often. */
+ /* Make some non-interrupt context restart the controller. */
+ /* Count and limit the retries though; either hardware or */
+ /* software errors can go forever... */
+ hc_reset (ohci);
+ return -1;
+ }
+
+ if (ints & OHCI_INTR_WDH) {
+ wait_ms(1);
+ writel (OHCI_INTR_WDH, &regs->intrdisable);
+ (void)readl (&regs->intrdisable); /* flush */
+ stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci));
+ writel (OHCI_INTR_WDH, &regs->intrenable);
+ (void)readl (&regs->intrdisable); /* flush */
+ }
+
+ if (ints & OHCI_INTR_SO) {
+ dbg("USB Schedule overrun\n");
+ writel (OHCI_INTR_SO, &regs->intrenable);
+ stat = -1;
+ }
+
+ /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */
+ if (ints & OHCI_INTR_SF) {
+ unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1;
+ wait_ms(1);
+ writel (OHCI_INTR_SF, &regs->intrdisable);
+ if (ohci->ed_rm_list[frame] != NULL)
+ writel (OHCI_INTR_SF, &regs->intrenable);
+ stat = 0xff;
+ }
+
+ writel (ints, &regs->intrstatus);
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci (ohci_t *ohci)
+{
+ dbg ("USB HC release ohci usb-%s", ohci->slot_name);
+
+ if (!ohci->disabled)
+ hc_reset (ohci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * low level initalisation routine, called from usb.c
+ */
+static char ohci_inited = 0;
+
+int usb_lowlevel_init(void)
+{
+#ifdef CONFIG_PCI_OHCI
+ pci_dev_t pdev;
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+ /* cpu dependant init */
+ if(usb_cpu_init())
+ return -1;
+#endif
+
+#ifdef CFG_USB_OHCI_BOARD_INIT
+ /* board dependant init */
+ if(usb_board_init())
+ return -1;
+#endif
+ memset (&gohci, 0, sizeof (ohci_t));
+
+ /* align the storage */
+ if ((__u32)&ghcca[0] & 0xff) {
+ err("HCCA not aligned!!");
+ return -1;
+ }
+ phcca = &ghcca[0];
+ info("aligned ghcca %p", phcca);
+ memset(&ohci_dev, 0, sizeof(struct ohci_device));
+ if ((__u32)&ohci_dev.ed[0] & 0x7) {
+ err("EDs not aligned!!");
+ return -1;
+ }
+ memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
+ if ((__u32)gtd & 0x7) {
+ err("TDs not aligned!!");
+ return -1;
+ }
+ ptd = gtd;
+ gohci.hcca = phcca;
+ memset (phcca, 0, sizeof (struct ohci_hcca));
+
+ gohci.disabled = 1;
+ gohci.sleeping = 0;
+ gohci.irq = -1;
+#ifdef CONFIG_PCI_OHCI
+ pdev = pci_find_devices(ohci_pci_ids, 0);
+
+ if (pdev != -1) {
+ u16 vid, did;
+ u32 base;
+ pci_read_config_word(pdev, PCI_VENDOR_ID, &vid);
+ pci_read_config_word(pdev, PCI_DEVICE_ID, &did);
+ printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n",
+ vid, did, (pdev >> 16) & 0xff,
+ (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7);
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base);
+ printf("OHCI regs address 0x%08x\n", base);
+ gohci.regs = (struct ohci_regs *)base;
+ } else
+ return -1;
+#else
+ gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE;
+#endif
+
+ gohci.flags = 0;
+ gohci.slot_name = CFG_USB_OHCI_SLOT_NAME;
+
+ if (hc_reset (&gohci) < 0) {
+ hc_release_ohci (&gohci);
+ err ("can't reset usb-%s", gohci.slot_name);
+#ifdef CFG_USB_OHCI_BOARD_INIT
+ /* board dependant cleanup */
+ usb_board_init_fail();
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+ /* cpu dependant cleanup */
+ usb_cpu_init_fail();
+#endif
+ return -1;
+ }
+
+ /* FIXME this is a second HC reset; why?? */
+ /* writel(gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control);
+ wait_ms(10); */
+ if (hc_start (&gohci) < 0) {
+ err ("can't start usb-%s", gohci.slot_name);
+ hc_release_ohci (&gohci);
+ /* Initialization failed */
+#ifdef CFG_USB_OHCI_BOARD_INIT
+ /* board dependant cleanup */
+ usb_board_stop();
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+ /* cpu dependant cleanup */
+ usb_cpu_stop();
+#endif
+ return -1;
+ }
+
+#ifdef DEBUG
+ ohci_dump (&gohci, 1);
+#else
+ wait_ms(1);
+#endif
+ ohci_inited = 1;
+ return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+ /* this gets called really early - before the controller has */
+ /* even been initialized! */
+ if (!ohci_inited)
+ return 0;
+ /* TODO release any interrupts, etc. */
+ /* call hc_release_ohci() here ? */
+ hc_reset (&gohci);
+
+#ifdef CFG_USB_OHCI_BOARD_INIT
+ /* board dependant cleanup */
+ if(usb_board_stop())
+ return -1;
+#endif
+
+#ifdef CFG_USB_OHCI_CPU_INIT
+ /* cpu dependant cleanup */
+ if(usb_cpu_stop())
+ return -1;
+#endif
+
+ return 0;
+}
+#endif /* CONFIG_USB_OHCI_NEW */
diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h
new file mode 100644
index 0000000000..380cb4c927
--- /dev/null
+++ b/drivers/usb_ohci.h
@@ -0,0 +1,445 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * usb-ohci.h
+ */
+
+/* functions for doing board or CPU specific setup/cleanup */
+extern int usb_board_init(void);
+extern int usb_board_stop(void);
+extern int usb_board_init_fail(void);
+
+extern int usb_cpu_init(void);
+extern int usb_cpu_stop(void);
+extern int usb_cpu_init_fail(void);
+
+
+static int cc_to_error[16] = {
+
+/* mapping of the OHCI CC status to error codes */
+ /* No Error */ 0,
+ /* CRC Error */ USB_ST_CRC_ERR,
+ /* Bit Stuff */ USB_ST_BIT_ERR,
+ /* Data Togg */ USB_ST_CRC_ERR,
+ /* Stall */ USB_ST_STALLED,
+ /* DevNotResp */ -1,
+ /* PIDCheck */ USB_ST_BIT_ERR,
+ /* UnExpPID */ USB_ST_BIT_ERR,
+ /* DataOver */ USB_ST_BUF_ERR,
+ /* DataUnder */ USB_ST_BUF_ERR,
+ /* reservd */ -1,
+ /* reservd */ -1,
+ /* BufferOver */ USB_ST_BUF_ERR,
+ /* BuffUnder */ USB_ST_BUF_ERR,
+ /* Not Access */ -1,
+ /* Not Access */ -1
+};
+
+/* ED States */
+
+#define ED_NEW 0x00
+#define ED_UNLINK 0x01
+#define ED_OPER 0x02
+#define ED_DEL 0x04
+#define ED_URB_DEL 0x08
+
+/* usb_ohci_ed */
+struct ed {
+ __u32 hwINFO;
+ __u32 hwTailP;
+ __u32 hwHeadP;
+ __u32 hwNextED;
+
+ struct ed *ed_prev;
+ __u8 int_period;
+ __u8 int_branch;
+ __u8 int_load;
+ __u8 int_interval;
+ __u8 state;
+ __u8 type;
+ __u16 last_iso;
+ struct ed *ed_rm_list;
+
+ struct usb_device *usb_dev;
+ void *purb;
+ __u32 unused[2];
+} __attribute((aligned(16)));
+typedef struct ed ed_t;
+
+
+/* TD info field */
+#define TD_CC 0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC 0x0C000000
+#define TD_T 0x03000000
+#define TD_T_DATA0 0x02000000
+#define TD_T_DATA1 0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R 0x00040000
+#define TD_DI 0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP 0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN 0x00100000
+#define TD_DP_OUT 0x00080000
+
+#define TD_ISO 0x00010000
+#define TD_DEL 0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+#define TD_NOTACCESSED 0x0F
+
+
+#define MAXPSW 1
+
+struct td {
+ __u32 hwINFO;
+ __u32 hwCBP; /* Current Buffer Pointer */
+ __u32 hwNextTD; /* Next TD Pointer */
+ __u32 hwBE; /* Memory Buffer End Pointer */
+
+/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */
+ __u16 hwPSW[MAXPSW];
+/* #endif */
+ __u8 unused;
+ __u8 index;
+ struct ed *ed;
+ struct td *next_dl_td;
+ struct usb_device *usb_dev;
+ int transfer_len;
+ __u32 data;
+
+ __u32 unused2[2];
+} __attribute((aligned(32)));
+typedef struct td td_t;
+
+#define OHCI_ED_SKIP (1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of. It must be 256-byte aligned.
+ */
+
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table[NUM_INTS]; /* Interrupt ED table */
+#if defined(CONFIG_MPC5200)
+ __u16 pad1; /* set to 0 on each frame_no change */
+ __u16 frame_no; /* current frame number */
+#else
+ __u16 frame_no; /* current frame number */
+ __u16 pad1; /* set to 0 on each frame_no change */
+#endif
+ __u32 done_head; /* info returned for an interrupt */
+ u8 reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+
+/*
+ * Maximum number of root hub ports.
+ */
+#ifndef CFG_USB_OHCI_MAX_ROOT_PORTS
+# error "CFG_USB_OHCI_MAX_ROOT_PORTS undefined!"
+#endif
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 donehead;
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus[CFG_USB_OHCI_MAX_ROOT_PORTS];
+ } roothub;
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+ int devnum; /* Address of Root Hub endpoint */
+ void *dev; /* was urb */
+ void *int_addr;
+ int send;
+ int interval;
+};
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
+
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+/* urb */
+#define N_URB_TD 48
+typedef struct
+{
+ ed_t *ed;
+ __u16 length; /* number of tds associated with this request */
+ __u16 td_cnt; /* number of tds already serviced */
+ struct usb_device *dev;
+ int state;
+ unsigned long pipe;
+ void *transfer_buffer;
+ int transfer_buffer_length;
+ int interval;
+ int actual_length;
+ int finished;
+ td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */
+} urb_priv_t;
+#define URB_DEL 1
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+typedef struct ohci {
+ struct ohci_hcca *hcca; /* hcca */
+ /*dma_addr_t hcca_dma;*/
+
+ int irq;
+ int disabled; /* e.g. got a UE, we're hung */
+ int sleeping;
+ unsigned long flags; /* for HC bugs */
+
+ struct ohci_regs *regs; /* OHCI controller's memory */
+
+ int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/
+ ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */
+ ed_t *ed_bulktail; /* last endpoint of bulk list */
+ ed_t *ed_controltail; /* last endpoint of control list */
+ int intrstatus;
+ __u32 hc_control; /* copy of the hc control reg */
+ struct usb_device *dev[32];
+ struct virt_root_hub rh;
+
+ const char *slot_name;
+} ohci_t;
+
+#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
+
+struct ohci_device {
+ ed_t ed[NUM_EDS];
+ int ed_cnt;
+};
+
+/* hcd */
+/* endpoint */
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe,
+ int interval, int load);
+
+/*-------------------------------------------------------------------------*/
+
+/* we need more TDs than EDs */
+#define NUM_TD 64
+
+/* +1 so we can align the storage */
+td_t gtd[NUM_TD+1];
+/* pointers to aligned storage */
+td_t *ptd;
+
+/* TDs ... */
+static inline struct td *
+td_alloc (struct usb_device *usb_dev)
+{
+ int i;
+ struct td *td;
+
+ td = NULL;
+ for (i = 0; i < NUM_TD; i++)
+ {
+ if (ptd[i].usb_dev == NULL)
+ {
+ td = &ptd[i];
+ td->usb_dev = usb_dev;
+ break;
+ }
+ }
+
+ return td;
+}
+
+static inline void
+ed_free (struct ed *ed)
+{
+ ed->usb_dev = NULL;
+}
diff --git a/drivers/usbdcore_ep0.c b/drivers/usbdcore_ep0.c
index 260befe978..1e44f322a7 100644
--- a/drivers/usbdcore_ep0.c
+++ b/drivers/usbdcore_ep0.c
@@ -2,6 +2,9 @@
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, deckard@CodeHermit.ie
+ *
* Based on
* linux/drivers/usbd/ep0.c
*
@@ -39,11 +42,17 @@
* function driver. This may need to change.
*
* XXX
+ *
+ * As alluded to above, a simple callback cdc_recv_setup has been implemented
+ * in the usb_device data structure to facilicate passing
+ * Common Device Class packets to a function driver.
+ *
+ * XXX
*/
#include <common.h>
-#if defined(CONFIG_OMAP1510) && defined(CONFIG_USB_DEVICE)
+#if defined(CONFIG_USB_DEVICE)
#include "usbdcore.h"
#if 0
@@ -69,7 +78,7 @@ static int ep0_get_status (struct usb_device_instance *device,
char *cp;
urb->actual_length = 2;
- cp = urb->buffer;
+ cp = (char*)urb->buffer;
cp[0] = cp[1] = 0;
switch (requesttype) {
@@ -115,7 +124,7 @@ static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
*
* Copy configuration data to urb transfer buffer if there is room for it.
*/
-static void copy_config (struct urb *urb, void *data, int max_length,
+void copy_config (struct urb *urb, void *data, int max_length,
int max_buf)
{
int available;
@@ -128,10 +137,7 @@ static void copy_config (struct urb *urb, void *data, int max_length,
dbg_ep0 (1, "data is NULL");
return;
}
- if (!(length = *(unsigned char *) data)) {
- dbg_ep0 (1, "length is zero");
- return;
- }
+ length = max_length;
if (length > max_length) {
dbg_ep0 (1, "length: %d >= max_length: %d", length,
@@ -192,7 +198,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
/* setup tx urb */
urb->actual_length = 0;
- cp = urb->buffer;
+ cp = (char*)urb->buffer;
dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
@@ -200,7 +206,6 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
case USB_DESCRIPTOR_TYPE_DEVICE:
{
struct usb_device_descriptor *device_descriptor;
-
if (!
(device_descriptor =
usbd_device_device_descriptor (device, port))) {
@@ -214,20 +219,16 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
/* correct the correct control endpoint 0 max packet size into the descriptor */
device_descriptor =
(struct usb_device_descriptor *) urb->buffer;
- device_descriptor->bMaxPacketSize0 =
- urb->device->bus->maxpacketsize;
}
- /*dbg_ep0(3, "copied device configuration, actual_length: %x", urb->actual_length); */
+ dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
break;
case USB_DESCRIPTOR_TYPE_CONFIGURATION:
{
- int bNumInterface;
struct usb_configuration_descriptor
*configuration_descriptor;
struct usb_device_descriptor *device_descriptor;
-
if (!
(device_descriptor =
usbd_device_device_descriptor (device, port))) {
@@ -251,130 +252,35 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
index);
return -1;
}
+ dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
copy_config (urb, configuration_descriptor,
- sizeof (struct
- usb_configuration_descriptor),
- max);
-
-
- /* iterate across interfaces for specified configuration */
- dbg_ep0 (0, "bNumInterfaces: %d",
- configuration_descriptor->bNumInterfaces);
- for (bNumInterface = 0;
- bNumInterface <
- configuration_descriptor->bNumInterfaces;
- bNumInterface++) {
- int bAlternateSetting;
- struct usb_interface_instance
- *interface_instance;
-
- dbg_ep0 (3, "[%d] bNumInterfaces: %d",
- bNumInterface,
- configuration_descriptor->bNumInterfaces);
-
- if (! (interface_instance = usbd_device_interface_instance (device,
- port, index, bNumInterface)))
- {
- dbg_ep0 (3, "[%d] interface_instance NULL",
- bNumInterface);
- return -1;
- }
- /* iterate across interface alternates */
- for (bAlternateSetting = 0;
- bAlternateSetting < interface_instance->alternates;
- bAlternateSetting++) {
- /*int class; */
- int bNumEndpoint;
- struct usb_interface_descriptor *interface_descriptor;
-
- struct usb_alternate_instance *alternate_instance;
-
- dbg_ep0 (3, "[%d:%d] alternates: %d",
- bNumInterface,
- bAlternateSetting,
- interface_instance->alternates);
-
- if (! (alternate_instance = usbd_device_alternate_instance (device, port, index, bNumInterface, bAlternateSetting))) {
- dbg_ep0 (3, "[%d] alternate_instance NULL",
- bNumInterface);
- return -1;
- }
- /* copy descriptor for this interface */
- copy_config (urb, alternate_instance->interface_descriptor,
- sizeof (struct usb_interface_descriptor),
- max);
-
- /*dbg_ep0(3, "[%d:%d] classes: %d endpoints: %d", bNumInterface, bAlternateSetting, */
- /* alternate_instance->classes, alternate_instance->endpoints); */
-
- /* iterate across classes for this alternate interface */
-#if 0
- for (class = 0;
- class < alternate_instance->classes;
- class++) {
- struct usb_class_descriptor *class_descriptor;
- /*dbg_ep0(3, "[%d:%d:%d] classes: %d", bNumInterface, bAlternateSetting, */
- /* class, alternate_instance->classes); */
- if (!(class_descriptor = usbd_device_class_descriptor_index (device, port, index, bNumInterface, bAlternateSetting, class))) {
- dbg_ep0 (3, "[%d] class NULL",
- class);
- return -1;
- }
- /* copy descriptor for this class */
- copy_config (urb, class_descriptor,
- sizeof (struct usb_class_descriptor),
- max);
- }
-#endif
-
- /* iterate across endpoints for this alternate interface */
- interface_descriptor = alternate_instance->interface_descriptor;
- for (bNumEndpoint = 0;
- bNumEndpoint < alternate_instance->endpoints;
- bNumEndpoint++) {
- struct usb_endpoint_descriptor *endpoint_descriptor;
- dbg_ep0 (3, "[%d:%d:%d] endpoint: %d",
- bNumInterface,
- bAlternateSetting,
- bNumEndpoint,
- interface_descriptor->
- bNumEndpoints);
- if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, index, bNumInterface, bAlternateSetting, bNumEndpoint))) {
- dbg_ep0 (3, "[%d] endpoint NULL",
- bNumEndpoint);
- return -1;
- }
- /* copy descriptor for this endpoint */
- copy_config (urb, endpoint_descriptor,
- sizeof (struct usb_endpoint_descriptor),
- max);
- }
- }
- }
- dbg_ep0 (3, "lengths: %d %d",
- le16_to_cpu (configuration_descriptor->wTotalLength),
- urb->actual_length);
+ cpu_to_le16(configuration_descriptor->wTotalLength),
+ max);
}
+
break;
case USB_DESCRIPTOR_TYPE_STRING:
{
struct usb_string_descriptor *string_descriptor;
-
if (!(string_descriptor = usbd_get_string (index))) {
+ serial_printf("Invalid string index %d\n", index);
return -1;
}
- /*dbg_ep0(3, "string_descriptor: %p", string_descriptor); */
+ dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
copy_config (urb, string_descriptor, string_descriptor->bLength, max);
}
break;
case USB_DESCRIPTOR_TYPE_INTERFACE:
+ serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
return -1;
case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
return -1;
case USB_DESCRIPTOR_TYPE_HID:
{
+ serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
return -1; /* unsupported at this time */
#if 0
int bNumInterface =
@@ -403,6 +309,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
break;
case USB_DESCRIPTOR_TYPE_REPORT:
{
+ serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
return -1; /* unsupported at this time */
#if 0
int bNumInterface =
@@ -434,12 +341,19 @@ static int ep0_get_descriptor (struct usb_device_instance *device,
#endif
}
break;
+ case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
+ {
+ /* If a USB device supports both a full speed and low speed operation
+ * we must send a Device_Qualifier descriptor here
+ */
+ return -1;
+ }
default:
return -1;
}
- dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d packet size: %2d",
+ dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
urb->buffer, urb->buffer_length, urb->actual_length,
device->bus->endpoint_array[0].tx_packetSize);
/*
@@ -495,6 +409,12 @@ int ep0_recv_setup (struct urb *urb)
/* handle USB Standard Request (c.f. USB Spec table 9-2) */
if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
+ if(device->device_state <= STATE_CONFIGURED){
+ /* Attempt to handle a CDC specific request if we are
+ * in the configured state.
+ */
+ return device->cdc_recv_setup(request,urb);
+ }
dbg_ep0 (1, "non standard request: %x",
request->bmRequestType & USB_REQ_TYPE_MASK);
return -1; /* Stall here */
@@ -567,6 +487,7 @@ int ep0_recv_setup (struct urb *urb)
le16_to_cpu (request->wValue) & 0xff);
case USB_REQ_GET_CONFIGURATION:
+ serial_printf("get config %d\n", device->configuration);
return ep0_get_one (device, urb,
device->configuration);
@@ -642,7 +563,6 @@ int ep0_recv_setup (struct urb *urb)
/*dbg_ep0(2, "address: %d %d %d", */
/* request->wValue, le16_to_cpu(request->wValue), device->address); */
- serial_printf ("DEVICE_ADDRESS_ASSIGNED.. event?\n");
return 0;
case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */
@@ -653,9 +573,10 @@ int ep0_recv_setup (struct urb *urb)
/* c.f. 9.4.7 - the top half of wValue is reserved */
/* */
if ((device->configuration =
- le16_to_cpu (request->wValue) & 0x7f) != 0) {
+ le16_to_cpu (request->wValue) & 0xFF80) != 0) {
/* c.f. 9.4.7 - zero is the default or addressed state, in our case this */
/* is the same is configuration zero */
+ serial_printf("error setting dev->config to zero!\n");
device->configuration = 0; /* TBR - ?????? */
}
/* reset interface and alternate settings */
diff --git a/drivers/usbdcore_mpc8xx.c b/drivers/usbdcore_mpc8xx.c
new file mode 100644
index 0000000000..e87284b178
--- /dev/null
+++ b/drivers/usbdcore_mpc8xx.c
@@ -0,0 +1,1400 @@
+/*
+ * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit
+ * bodonoghue@CodeHermit.ie
+ *
+ * References
+ * DasUBoot/drivers/usbdcore_omap1510.c, for design and implementation ideas.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Notes :
+ * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX
+ * packet to force the USB re-transmit protocol.
+ *
+ * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console
+ * be careful that tracing doesn't create Hiesen-bugs with respect to
+ * response timeouts to control requests.
+ *
+ * 3. This driver should be able to support any higher level driver that
+ * that wants to do either of the two standard UDC implementations
+ * Control-Bulk-Interrupt or Bulk-IN/Bulk-Out standards. Hence
+ * gserial and cdc_acm should work with this code.
+ *
+ * 4. NAK events never actually get raised at all, the documentation
+ * is just wrong !
+ *
+ * 5. For some reason, cbd_datlen is *always* +2 the value it should be.
+ * this means that having an RX cbd of 16 bytes is not possible, since
+ * the same size is reported for 14 bytes received as 16 bytes received
+ * until we can find out why this happens, RX cbds must be limited to 8
+ * bytes. TODO: check errata for this behaviour.
+ *
+ * 6. Right now this code doesn't support properly powering up with the USB
+ * cable attached to the USB host my development board the Adder87x doesn't
+ * have a pull-up fitted to allow this, so it is necessary to power the
+ * board and *then* attached the USB cable to the host. However somebody
+ * with a different design in their board may be able to keep the cable
+ * constantly connected and simply enable/disable a pull-up re
+ * figure 31.1 in MPC885RM.pdf instead of having to power up the board and
+ * then attach the cable !
+ *
+ */
+#include <common.h>
+#include <config.h>
+
+#if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE)
+#include <commproc.h>
+#include "usbdcore.h"
+#include "usbdcore_mpc8xx.h"
+#include "usbdcore_ep0.h"
+
+#define ERR(fmt, args...)\
+ serial_printf("ERROR : [%s] %s:%d: "fmt,\
+ __FILE__,__FUNCTION__,__LINE__, ##args)
+#ifdef __DEBUG_UDC__
+#define DBG(fmt,args...)\
+ serial_printf("[%s] %s:%d: "fmt,\
+ __FILE__,__FUNCTION__,__LINE__, ##args)
+#else
+#define DBG(fmt,args...)
+#endif
+
+/* Static Data */
+#ifdef __SIMULATE_ERROR__
+static char err_poison_test = 0;
+#endif
+static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS];
+static u32 address_base = STATE_NOT_READY;
+static mpc8xx_udc_state_t udc_state = 0;
+static struct usb_device_instance *udc_device = 0;
+static volatile usb_epb_t *endpoints[MAX_ENDPOINTS];
+static volatile cbd_t *tx_cbd[TX_RING_SIZE];
+static volatile cbd_t *rx_cbd[RX_RING_SIZE];
+static volatile immap_t *immr = 0;
+static volatile cpm8xx_t *cp = 0;
+static volatile usb_pram_t *usb_paramp = 0;
+static volatile usb_t *usbp = 0;
+static int rx_ct = 0;
+static int tx_ct = 0;
+
+/* Static Function Declarations */
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+ usb_device_state_t final);
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+ usb_device_state_t final);
+static void mpc8xx_udc_stall (unsigned int ep);
+static void mpc8xx_udc_flush_tx_fifo (int epid);
+static void mpc8xx_udc_flush_rx_fifo (void);
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+ struct urb *tx_urb);
+static void mpc8xx_udc_dump_request (struct usb_device_request *request);
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+ volatile cpm8xx_t * cp);
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi);
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_cbd_init (void);
+static void mpc8xx_udc_endpoint_init (void);
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size);
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment);
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp);
+static void mpc8xx_udc_set_nak (unsigned int ep);
+static short mpc8xx_udc_handle_txerr (void);
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid);
+
+/******************************************************************************
+ Global Linkage
+ *****************************************************************************/
+
+/* udc_init
+ *
+ * Do initial bus gluing
+ */
+int udc_init (void)
+{
+ /* Init various pointers */
+ immr = (immap_t *) CFG_IMMR;
+ cp = (cpm8xx_t *) & (immr->im_cpm);
+ usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]);
+ usbp = (usb_t *) & (cp->cp_scc[0]);
+
+ memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS));
+
+ udc_device = 0;
+ udc_state = STATE_NOT_READY;
+
+ usbp->usmod = 0x00;
+ usbp->uscom = 0;
+
+ /* Set USB Frame #0, Respond at Address & Get a clock source */
+ usbp->usaddr = 0x00;
+ mpc8xx_udc_clock_init (immr, cp);
+
+ /* PA15, PA14 as perhiperal USBRXD and USBOE */
+ immr->im_ioport.iop_padir &= ~0x0003;
+ immr->im_ioport.iop_papar |= 0x0003;
+
+ /* PC11/PC10 as peripheral USBRXP USBRXN */
+ immr->im_ioport.iop_pcso |= 0x0030;
+
+ /* PC7/PC6 as perhiperal USBTXP and USBTXN */
+ immr->im_ioport.iop_pcdir |= 0x0300;
+ immr->im_ioport.iop_pcpar |= 0x0300;
+
+ /* Set the base address */
+ address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE);
+
+ /* Initialise endpoints and circular buffers */
+ mpc8xx_udc_endpoint_init ();
+ mpc8xx_udc_cbd_init ();
+
+ /* Assign allocated Dual Port Endpoint descriptors */
+ usb_paramp->ep0ptr = (u32) endpoints[0];
+ usb_paramp->ep1ptr = (u32) endpoints[1];
+ usb_paramp->ep2ptr = (u32) endpoints[2];
+ usb_paramp->ep3ptr = (u32) endpoints[3];
+ usb_paramp->frame_n = 0;
+
+ DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n",
+ usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr,
+ usb_paramp->ep3ptr);
+
+ return 0;
+}
+
+/* udc_irq
+ *
+ * Poll for whatever events may have occured
+ */
+void udc_irq (void)
+{
+ int epid = 0;
+ volatile cbd_t *rx_cbdp = 0;
+ volatile cbd_t *rx_cbdp_base = 0;
+
+ if (udc_state != STATE_READY) {
+ return;
+ }
+
+ if (usbp->usber & USB_E_BSY) {
+ /* This shouldn't happen. If it does then it's a bug ! */
+ usbp->usber |= USB_E_BSY;
+ mpc8xx_udc_flush_rx_fifo ();
+ }
+
+ /* Scan all RX/Bidirectional Endpoints for RX data. */
+ for (epid = 0; epid < MAX_ENDPOINTS; epid++) {
+ if (!ep_ref[epid].prx) {
+ continue;
+ }
+ rx_cbdp = rx_cbdp_base = ep_ref[epid].prx;
+
+ do {
+ if (!(rx_cbdp->cbd_sc & RX_BD_E)) {
+
+ if (rx_cbdp->cbd_sc & 0x1F) {
+ /* Corrupt data discard it.
+ * Controller has NAK'd this packet.
+ */
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+ } else {
+ if (!epid) {
+ mpc8xx_udc_ep0_rx (rx_cbdp);
+
+ } else {
+ /* Process data */
+ mpc8xx_udc_set_nak (epid);
+ mpc8xx_udc_epn_rx (epid, rx_cbdp);
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+ }
+ }
+
+ /* Advance RX CBD pointer */
+ mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+ ep_ref[epid].prx = rx_cbdp;
+ } else {
+ /* Advance RX CBD pointer */
+ mpc8xx_udc_advance_rx (&rx_cbdp, epid);
+ }
+
+ } while (rx_cbdp != rx_cbdp_base);
+ }
+
+ /* Handle TX events as appropiate, the correct place to do this is
+ * in a tx routine. Perhaps TX on epn was pre-empted by ep0
+ */
+
+ if (usbp->usber & USB_E_TXB) {
+ usbp->usber |= USB_E_TXB;
+ }
+
+ if (usbp->usber & (USB_TX_ERRMASK)) {
+ mpc8xx_udc_handle_txerr ();
+ }
+
+ /* Switch to the default state, respond at the default address */
+ if (usbp->usber & USB_E_RESET) {
+ usbp->usber |= USB_E_RESET;
+ usbp->usaddr = 0x00;
+ udc_device->device_state = STATE_DEFAULT;
+ }
+
+ /* if(usbp->usber&USB_E_IDLE){
+ We could suspend here !
+ usbp->usber|=USB_E_IDLE;
+ DBG("idle state change\n");
+ }
+ if(usbp->usbs){
+ We could resume here when IDLE is deasserted !
+ Not worth doing, so long as we are self powered though.
+ }
+ */
+
+ return;
+}
+
+/* udc_endpoint_write
+ *
+ * Write some data to an endpoint
+ */
+int udc_endpoint_write (struct usb_endpoint_instance *epi)
+{
+ int ep = 0;
+ short epid = 1, unnak = 0, ret = 0;
+
+ if (udc_state != STATE_READY) {
+ ERR ("invalid udc_state != STATE_READY!\n");
+ return -1;
+ }
+
+ if (!udc_device || !epi) {
+ return -1;
+ }
+
+ if (udc_device->device_state != STATE_CONFIGURED) {
+ return -1;
+ }
+
+ ep = epi->endpoint_address & 0x03;
+ if (ep >= MAX_ENDPOINTS) {
+ return -1;
+ }
+
+ /* Set NAK for all RX endpoints during TX */
+ for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+
+ /* Don't set NAK on DATA IN/CONTROL endpoints */
+ if (ep_ref[epid].sc & USB_DIR_IN) {
+ continue;
+ }
+
+ if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) {
+ unnak |= 1 << epid;
+ }
+
+ mpc8xx_udc_set_nak (epid);
+ }
+
+ mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep],
+ epi->tx_urb);
+ ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]);
+
+ /* Remove temporary NAK */
+ for (epid = 1; epid < MAX_ENDPOINTS; epid++) {
+ if (unnak & (1 << epid)) {
+ udc_unset_nak (epid);
+ }
+ }
+
+ return ret;
+}
+
+/* mpc8xx_udc_assign_urb
+ *
+ * Associate a given urb to an endpoint TX or RX transmit/receive buffers
+ */
+static int mpc8xx_udc_assign_urb (int ep, char direction)
+{
+ struct usb_endpoint_instance *epi = 0;
+
+ if (ep >= MAX_ENDPOINTS) {
+ goto err;
+ }
+ epi = &udc_device->bus->endpoint_array[ep];
+ if (!epi) {
+ goto err;
+ }
+
+ if (!ep_ref[ep].urb) {
+ ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array);
+ if (!ep_ref[ep].urb) {
+ goto err;
+ }
+ } else {
+ ep_ref[ep].urb->actual_length = 0;
+ }
+
+ switch (direction) {
+ case USB_DIR_IN:
+ epi->tx_urb = ep_ref[ep].urb;
+ break;
+ case USB_DIR_OUT:
+ epi->rcv_urb = ep_ref[ep].urb;
+ break;
+ default:
+ goto err;
+ }
+ return 0;
+
+ err:
+ udc_state = STATE_ERROR;
+ return -1;
+}
+
+/* udc_setup_ep
+ *
+ * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram
+ * Isochronous endpoints aren't yet supported!
+ */
+void udc_setup_ep (struct usb_device_instance *device, unsigned int ep,
+ struct usb_endpoint_instance *epi)
+{
+ uchar direction = 0;
+ int ep_attrib = 0;
+
+ if (epi && (ep < MAX_ENDPOINTS)) {
+
+ if (ep == 0) {
+ if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL
+ || epi->tx_attributes !=
+ USB_ENDPOINT_XFER_CONTROL) {
+
+ /* ep0 must be a control endpoint */
+ udc_state = STATE_ERROR;
+ return;
+
+ }
+ if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+ mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize,
+ epi->rcv_packetSize);
+ }
+ usbp->usep[ep] = 0x0000;
+ return;
+ }
+
+ if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) {
+
+ direction = 1;
+ ep_attrib = epi->tx_attributes;
+ epi->rcv_packetSize = 0;
+ ep_ref[ep].sc |= USB_DIR_IN;
+ } else {
+
+ direction = 0;
+ ep_attrib = epi->rcv_attributes;
+ epi->tx_packetSize = 0;
+ ep_ref[ep].sc &= ~USB_DIR_IN;
+ }
+
+ if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address
+ & USB_ENDPOINT_DIR_MASK)) {
+ return;
+ }
+
+ switch (ep_attrib) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+ mpc8xx_udc_cbd_attach (ep,
+ epi->tx_packetSize,
+ epi->rcv_packetSize);
+ }
+ usbp->usep[ep] = ep << 12;
+ epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb;
+
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ if (!(ep_ref[ep].sc & EP_ATTACHED)) {
+ if (direction) {
+ mpc8xx_udc_cbd_attach (ep,
+ epi->tx_packetSize,
+ 0);
+ } else {
+ mpc8xx_udc_cbd_attach (ep,
+ 0,
+ epi->rcv_packetSize);
+ }
+ }
+ usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8);
+
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ default:
+ serial_printf ("Error endpoint attrib %d>3\n", ep_attrib);
+ udc_state = STATE_ERROR;
+ break;
+ }
+ }
+
+}
+
+/* udc_connect
+ *
+ * Move state, switch on the USB
+ */
+void udc_connect (void)
+{
+ /* Enable pull-up resistor on D+
+ * TODO: fit a pull-up resistor to drive SE0 for > 2.5us
+ */
+
+ if (udc_state != STATE_ERROR) {
+ udc_state = STATE_READY;
+ usbp->usmod |= USMOD_EN;
+ }
+}
+
+/* udc_disconnect
+ *
+ * Disconnect is not used but, is included for completeness
+ */
+void udc_disconnect (void)
+{
+ /* Disable pull-up resistor on D-
+ * TODO: fix a pullup resistor to control this
+ */
+
+ if (udc_state != STATE_ERROR) {
+ udc_state = STATE_NOT_READY;
+ }
+ usbp->usmod &= ~USMOD_EN;
+}
+
+/* udc_enable
+ *
+ * Grab an EP0 URB, register interest in a subset of USB events
+ */
+void udc_enable (struct usb_device_instance *device)
+{
+ if (udc_state == STATE_ERROR) {
+ return;
+ }
+
+ udc_device = device;
+
+ if (!ep_ref[0].urb) {
+ ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array);
+ }
+
+ /* Register interest in all events except SOF, enable transceiver */
+ usbp->usber = 0x03FF;
+ usbp->usbmr = 0x02F7;
+
+ return;
+}
+
+/* udc_disable
+ *
+ * disable the currently hooked device
+ */
+void udc_disable (void)
+{
+ int i = 0;
+
+ if (udc_state == STATE_ERROR) {
+ DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n");
+ return;
+ }
+
+ udc_device = 0;
+
+ for (; i < MAX_ENDPOINTS; i++) {
+ if (ep_ref[i].urb) {
+ usbd_dealloc_urb (ep_ref[i].urb);
+ ep_ref[i].urb = 0;
+ }
+ }
+
+ usbp->usbmr = 0x00;
+ usbp->usmod = ~USMOD_EN;
+ udc_state = STATE_NOT_READY;
+}
+
+/* udc_startup_events
+ *
+ * Enable the specified device
+ */
+void udc_startup_events (struct usb_device_instance *device)
+{
+ udc_enable (device);
+ if (udc_state == STATE_READY) {
+ usbd_device_event_irq (device, DEVICE_CREATE, 0);
+ }
+}
+
+/* udc_set_nak
+ *
+ * Allow upper layers to signal lower layers should not accept more RX data
+ *
+ */
+void udc_set_nak (int epid)
+{
+ if (epid) {
+ mpc8xx_udc_set_nak (epid);
+ }
+}
+
+/* udc_unset_nak
+ *
+ * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint.
+ * Switch off NAKing on this endpoint to accept more data output from host.
+ *
+ */
+void udc_unset_nak (int epid)
+{
+ if (epid > MAX_ENDPOINTS) {
+ return;
+ }
+
+ if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) {
+ usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK);
+ __asm__ ("eieio");
+ }
+}
+
+/******************************************************************************
+ Static Linkage
+******************************************************************************/
+
+/* udc_state_transition_up
+ * udc_state_transition_down
+ *
+ * Helper functions to implement device state changes. The device states and
+ * the events that transition between them are:
+ *
+ * STATE_ATTACHED
+ * || /\
+ * \/ ||
+ * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET
+ * || /\
+ * \/ ||
+ * STATE_POWERED
+ * || /\
+ * \/ ||
+ * DEVICE_RESET DEVICE_POWER_INTERRUPTION
+ * || /\
+ * \/ ||
+ * STATE_DEFAULT
+ * || /\
+ * \/ ||
+ * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET
+ * || /\
+ * \/ ||
+ * STATE_ADDRESSED
+ * || /\
+ * \/ ||
+ * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED
+ * || /\
+ * \/ ||
+ * STATE_CONFIGURED
+ *
+ * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED
+ * to STATE_CONFIGURED) from the specified initial state to the specified final
+ * state, passing through each intermediate state on the way. If the initial
+ * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then
+ * no state transitions will take place.
+ *
+ * udc_state_transition_down transitions down (in the direction from
+ * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the
+ * specified final state, passing through each intermediate state on the way.
+ * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final
+ * state, then no state transitions will take place.
+ *
+ */
+
+static void mpc8xx_udc_state_transition_up (usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial < final) {
+ switch (initial) {
+ case STATE_ATTACHED:
+ usbd_device_event_irq (udc_device,
+ DEVICE_HUB_CONFIGURED, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq (udc_device,
+ DEVICE_ADDRESS_ASSIGNED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq (udc_device, DEVICE_CONFIGURED,
+ 0);
+ case STATE_CONFIGURED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void mpc8xx_udc_state_transition_down (usb_device_state_t initial,
+ usb_device_state_t final)
+{
+ if (initial > final) {
+ switch (initial) {
+ case STATE_CONFIGURED:
+ usbd_device_event_irq (udc_device,
+ DEVICE_DE_CONFIGURED, 0);
+ if (final == STATE_ADDRESSED)
+ break;
+ case STATE_ADDRESSED:
+ usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
+ if (final == STATE_DEFAULT)
+ break;
+ case STATE_DEFAULT:
+ usbd_device_event_irq (udc_device,
+ DEVICE_POWER_INTERRUPTION, 0);
+ if (final == STATE_POWERED)
+ break;
+ case STATE_POWERED:
+ usbd_device_event_irq (udc_device, DEVICE_HUB_RESET,
+ 0);
+ case STATE_ATTACHED:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* mpc8xx_udc_stall
+ *
+ * Force returning of STALL tokens on the given endpoint. Protocol or function
+ * STALL conditions are permissable here
+ */
+static void mpc8xx_udc_stall (unsigned int ep)
+{
+ usbp->usep[ep] |= STALL_BITMASK;
+}
+
+/* mpc8xx_udc_set_nak
+ *
+ * Force returning of NAK responses for the given endpoint as a kind of very
+ * simple flow control
+ */
+static void mpc8xx_udc_set_nak (unsigned int ep)
+{
+ usbp->usep[ep] |= NAK_BITMASK;
+ __asm__ ("eieio");
+}
+
+/* mpc8xx_udc_handle_txerr
+ *
+ * Handle errors relevant to TX. Return a status code to allow calling
+ * indicative of what if anything happened
+ */
+static short mpc8xx_udc_handle_txerr ()
+{
+ short ep = 0, ret = 0;
+
+ for (; ep < TX_RING_SIZE; ep++) {
+ if (usbp->usber & (0x10 << ep)) {
+
+ /* Timeout or underrun */
+ if (tx_cbd[ep]->cbd_sc & 0x06) {
+ ret = 1;
+ mpc8xx_udc_flush_tx_fifo (ep);
+
+ } else {
+ if (usbp->usep[ep] & STALL_BITMASK) {
+ if (!ep) {
+ usbp->usep[ep] &= ~STALL_BITMASK;
+ }
+ } /* else NAK */
+ }
+ usbp->usber |= (0x10 << ep);
+ }
+ }
+ return ret;
+}
+
+/* mpc8xx_udc_advance_rx
+ *
+ * Advance cbd rx
+ */
+static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid)
+{
+ if ((*rx_cbdp)->cbd_sc & RX_BD_W) {
+ *rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CFG_IMMR);
+
+ } else {
+ (*rx_cbdp)++;
+ }
+}
+
+
+/* mpc8xx_udc_flush_tx_fifo
+ *
+ * Flush a given TX fifo. Assumes one tx cbd per endpoint
+ */
+static void mpc8xx_udc_flush_tx_fifo (int epid)
+{
+ volatile cbd_t *tx_cbdp = 0;
+
+ if (epid > MAX_ENDPOINTS) {
+ return;
+ }
+
+ /* TX stop */
+ immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01);
+ __asm__ ("eieio");
+ while (immr->im_cpm.cp_cpcr & 0x01);
+
+ usbp->uscom = 0x40 | 0;
+
+ /* reset ring */
+ tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CFG_IMMR);
+ tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W);
+
+
+ endpoints[epid]->tptr = endpoints[epid]->tbase;
+ endpoints[epid]->tstate = 0x00;
+ endpoints[epid]->tbcnt = 0x00;
+
+ /* TX start */
+ immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01);
+ __asm__ ("eieio");
+ while (immr->im_cpm.cp_cpcr & 0x01);
+
+ return;
+}
+
+/* mpc8xx_udc_flush_rx_fifo
+ *
+ * For the sake of completeness of the namespace, it seems like
+ * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo();
+ * If RX_BD_E is true => a driver bug either here or in an upper layer
+ * not polling frequently enough. If RX_BD_E is true we have told the host
+ * we have accepted data but, the CPM found it had no-where to put that data
+ * which needless to say would be a bad thing.
+ */
+static void mpc8xx_udc_flush_rx_fifo ()
+{
+ int i = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+ ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n",
+ rx_cbd[i], rx_cbd[i]->cbd_datlen,
+ rx_cbd[i]->cbd_sc);
+
+ }
+ }
+ ERR ("BUG : Input over-run\n");
+}
+
+/* mpc8xx_udc_clear_rxbd
+ *
+ * Release control of RX CBD to CP.
+ */
+static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp)
+{
+ rx_cbdp->cbd_datlen = 0x0000;
+ rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I));
+ __asm__ ("eieio");
+}
+
+/* mpc8xx_udc_tx_irq
+ *
+ * Parse for tx timeout, control RX or USB reset/busy conditions
+ * Return -1 on timeout, -2 on fatal error, else return zero
+ */
+static int mpc8xx_udc_tx_irq (int ep)
+{
+ int i = 0;
+
+ if (usbp->usber & (USB_TX_ERRMASK)) {
+ if (mpc8xx_udc_handle_txerr ()) {
+ /* Timeout, controlling function must retry send */
+ return -1;
+ }
+ }
+
+ if (usbp->usber & (USB_E_RESET | USB_E_BSY)) {
+ /* Fatal, abandon TX transaction */
+ return -2;
+ }
+
+ if (usbp->usber & USB_E_RXB) {
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) {
+ if ((rx_cbd[i] == ep_ref[0].prx) || ep) {
+ return -2;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* mpc8xx_udc_ep_tx
+ *
+ * Transmit in a re-entrant fashion outbound USB packets.
+ * Implement retry/timeout mechanism described in USB specification
+ * Toggle DATA0/DATA1 pids as necessary
+ * Introduces non-standard tx_retry. The USB standard has no scope for slave
+ * devices to give up TX, however tx_retry stops us getting stuck in an endless
+ * TX loop.
+ */
+static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi)
+{
+ struct urb *urb = epi->tx_urb;
+ volatile cbd_t *tx_cbdp = 0;
+ unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0;
+ int ret = 0;
+
+ if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) {
+ return -1;
+ }
+
+ ep = epi->endpoint_address & 0x03;
+ tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR);
+
+ if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) {
+ mpc8xx_udc_flush_tx_fifo (ep);
+ usbp->usber |= USB_E_TXB;
+ };
+
+ while (tx_retry++ < 100) {
+ ret = mpc8xx_udc_tx_irq (ep);
+ if (ret == -1) {
+ /* ignore timeout here */
+ } else if (ret == -2) {
+ /* Abandon TX */
+ mpc8xx_udc_flush_tx_fifo (ep);
+ return -1;
+ }
+
+ tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR);
+ while (tx_cbdp->cbd_sc & TX_BD_R) {
+ };
+ tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W);
+
+ pkt_len = urb->actual_length - epi->sent;
+
+ if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) {
+ pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT);
+ }
+
+ for (x = 0; x < pkt_len; x++) {
+ *((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) =
+ urb->buffer[epi->sent + x];
+ }
+ tx_cbdp->cbd_datlen = pkt_len;
+ tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid);
+ __asm__ ("eieio");
+
+#ifdef __SIMULATE_ERROR__
+ if (++err_poison_test == 2) {
+ err_poison_test = 0;
+ tx_cbdp->cbd_sc &= ~TX_BD_TC;
+ }
+#endif
+
+ usbp->uscom = (USCOM_STR | ep);
+
+ while (!(usbp->usber & USB_E_TXB)) {
+ ret = mpc8xx_udc_tx_irq (ep);
+ if (ret == -1) {
+ /* TX timeout */
+ break;
+ } else if (ret == -2) {
+ if (usbp->usber & USB_E_TXB) {
+ usbp->usber |= USB_E_TXB;
+ }
+ mpc8xx_udc_flush_tx_fifo (ep);
+ return -1;
+ }
+ };
+
+ if (usbp->usber & USB_E_TXB) {
+ usbp->usber |= USB_E_TXB;
+ }
+
+ /* ACK must be present <= 18bit times from TX */
+ if (ret == -1) {
+ continue;
+ }
+
+ /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */
+ epi->sent += pkt_len;
+ epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize);
+ TOGGLE_TX_PID (ep_ref[ep].pid);
+
+ if (epi->sent >= epi->tx_urb->actual_length) {
+
+ epi->tx_urb->actual_length = 0;
+ epi->sent = 0;
+
+ if (ep_ref[ep].sc & EP_SEND_ZLP) {
+ ep_ref[ep].sc &= ~EP_SEND_ZLP;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent,
+ epi->tx_urb->actual_length);
+
+ return -1;
+}
+
+/* mpc8xx_udc_dump_request
+ *
+ * Dump a control request to console
+ */
+static void mpc8xx_udc_dump_request (struct usb_device_request *request)
+{
+ DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x "
+ "wIndex:%04x wLength:%04x ?\n",
+ request->bmRequestType,
+ request->bRequest,
+ request->wValue, request->wIndex, request->wLength);
+
+ return;
+}
+
+/* mpc8xx_udc_ep0_rx_setup
+ *
+ * Decode received ep0 SETUP packet. return non-zero on error
+ */
+static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp)
+{
+ unsigned int x = 0;
+ struct urb *purb = ep_ref[0].urb;
+ struct usb_endpoint_instance *epi =
+ &udc_device->bus->endpoint_array[0];
+
+ for (; x < rx_cbdp->cbd_datlen; x++) {
+ *(((unsigned char *) &ep_ref[0].urb->device_request) + x) =
+ *((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+ }
+
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+ if (ep0_recv_setup (purb)) {
+ mpc8xx_udc_dump_request (&purb->device_request);
+ return -1;
+ }
+
+ if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK)
+ == USB_REQ_HOST2DEVICE) {
+
+ switch (purb->device_request.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ /* Send the Status OUT ZLP */
+ ep_ref[0].pid = TX_BD_PID_DATA1;
+ purb->actual_length = 0;
+ mpc8xx_udc_init_tx (epi, purb);
+ mpc8xx_udc_ep_tx (epi);
+
+ /* Move to the addressed state */
+ usbp->usaddr = udc_device->address;
+ mpc8xx_udc_state_transition_up (udc_device->device_state,
+ STATE_ADDRESSED);
+ return 0;
+
+ case USB_REQ_SET_CONFIGURATION:
+ if (!purb->device_request.wValue) {
+ /* Respond at default address */
+ usbp->usaddr = 0x00;
+ mpc8xx_udc_state_transition_down (udc_device->device_state,
+ STATE_ADDRESSED);
+ } else {
+ /* TODO: Support multiple configurations */
+ mpc8xx_udc_state_transition_up (udc_device->device_state,
+ STATE_CONFIGURED);
+ for (x = 1; x < MAX_ENDPOINTS; x++) {
+ if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) {
+ ep_ref[x].pid = TX_BD_PID_DATA0;
+ } else {
+ ep_ref[x].pid = RX_BD_PID_DATA0;
+ }
+ /* Set configuration must unstall endpoints */
+ usbp->usep[x] &= ~STALL_BITMASK;
+ }
+ }
+ break;
+ default:
+ /* CDC/Vendor specific */
+ break;
+ }
+
+ /* Send ZLP as ACK in Status OUT phase */
+ ep_ref[0].pid = TX_BD_PID_DATA1;
+ purb->actual_length = 0;
+ mpc8xx_udc_init_tx (epi, purb);
+ mpc8xx_udc_ep_tx (epi);
+
+ } else {
+
+ if (purb->actual_length) {
+ ep_ref[0].pid = TX_BD_PID_DATA1;
+ mpc8xx_udc_init_tx (epi, purb);
+
+ if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) {
+ ep_ref[0].sc |= EP_SEND_ZLP;
+ }
+
+ if (purb->device_request.wValue ==
+ USB_DESCRIPTOR_TYPE_DEVICE) {
+ if (le16_to_cpu (purb->device_request.wLength)
+ > purb->actual_length) {
+ /* Send EP0_MAX_PACKET_SIZE bytes
+ * unless correct size requested.
+ */
+ if (purb->actual_length > epi->tx_packetSize) {
+ purb->actual_length = epi->tx_packetSize;
+ }
+ }
+ }
+ mpc8xx_udc_ep_tx (epi);
+
+ } else {
+ /* Corrupt SETUP packet? */
+ ERR ("Zero length data or SETUP with DATA-IN phase ?\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* mpc8xx_udc_init_tx
+ *
+ * Setup some basic parameters for a TX transaction
+ */
+static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi,
+ struct urb *tx_urb)
+{
+ epi->sent = 0;
+ epi->last = 0;
+ epi->tx_urb = tx_urb;
+}
+
+/* mpc8xx_udc_ep0_rx
+ *
+ * Receive ep0/control USB data. Parse and possibly send a response.
+ */
+static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp)
+{
+ if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) {
+
+ /* Unconditionally accept SETUP packets */
+ if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) {
+ mpc8xx_udc_stall (0);
+ }
+
+ } else {
+
+ mpc8xx_udc_clear_rxbd (rx_cbdp);
+
+ if ((rx_cbdp->cbd_datlen - 2)) {
+ /* SETUP with a DATA phase
+ * outside of SETUP packet.
+ * Reply with STALL.
+ */
+ mpc8xx_udc_stall (0);
+ }
+ }
+}
+
+/* mpc8xx_udc_epn_rx
+ *
+ * Receive some data from cbd into USB system urb data abstraction
+ * Upper layers should NAK if there is insufficient RX data space
+ */
+static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp)
+{
+ struct usb_endpoint_instance *epi = 0;
+ struct urb *urb = 0;
+ unsigned int x = 0;
+
+ if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) {
+ return 0;
+ }
+
+ /* USB 2.0 PDF section 8.6.4
+ * Discard data with invalid PID it is a resend.
+ */
+ if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) {
+ return 1;
+ }
+ TOGGLE_RX_PID (ep_ref[epid].pid);
+
+ epi = &udc_device->bus->endpoint_array[epid];
+ urb = epi->rcv_urb;
+
+ for (; x < (rx_cbdp->cbd_datlen - 2); x++) {
+ *((unsigned char *) (urb->buffer + urb->actual_length + x)) =
+ *((unsigned char *) (rx_cbdp->cbd_bufaddr + x));
+ }
+
+ if (x) {
+ usbd_rcv_complete (epi, x, 0);
+ if (ep_ref[epid].urb->status == RECV_ERROR) {
+ DBG ("RX error unset NAK\n");
+ udc_unset_nak (epid);
+ }
+ }
+ return x;
+}
+
+/* mpc8xx_udc_clock_init
+ *
+ * Obtain a clock reference for Full Speed Signaling
+ */
+static void mpc8xx_udc_clock_init (volatile immap_t * immr,
+ volatile cpm8xx_t * cp)
+{
+
+#if defined(CFG_USB_EXTC_CLK)
+
+ /* This has been tested with a 48MHz crystal on CLK6 */
+ switch (CFG_USB_EXTC_CLK) {
+ case 1:
+ immr->im_ioport.iop_papar |= 0x0100;
+ immr->im_ioport.iop_padir &= ~0x0100;
+ cp->cp_sicr |= 0x24;
+ break;
+ case 2:
+ immr->im_ioport.iop_papar |= 0x0200;
+ immr->im_ioport.iop_padir &= ~0x0200;
+ cp->cp_sicr |= 0x2D;
+ break;
+ case 3:
+ immr->im_ioport.iop_papar |= 0x0400;
+ immr->im_ioport.iop_padir &= ~0x0400;
+ cp->cp_sicr |= 0x36;
+ break;
+ case 4:
+ immr->im_ioport.iop_papar |= 0x0800;
+ immr->im_ioport.iop_padir &= ~0x0800;
+ cp->cp_sicr |= 0x3F;
+ break;
+ default:
+ udc_state = STATE_ERROR;
+ break;
+ }
+
+#elif defined(CFG_USB_BRGCLK)
+
+ /* This has been tested with brgclk == 50MHz */
+ DECLARE_GLOBAL_DATA_PTR;
+ int divisor = 0;
+
+ if (gd->cpu_clk < 48000000L) {
+ ERR ("brgclk is too slow for full-speed USB!\n");
+ udc_state = STATE_ERROR;
+ return;
+ }
+
+ /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48Mhz)
+ * but, can /probably/ live with close-ish alternative rates.
+ */
+ divisor = (gd->cpu_clk / 48000000L) - 1;
+ cp->cp_sicr &= ~0x0000003F;
+
+ switch (CFG_USB_BRGCLK) {
+ case 1:
+ cp->cp_brgc1 |= (divisor | CPM_BRG_EN);
+ cp->cp_sicr &= ~0x2F;
+ break;
+ case 2:
+ cp->cp_brgc2 |= (divisor | CPM_BRG_EN);
+ cp->cp_sicr |= 0x00000009;
+ break;
+ case 3:
+ cp->cp_brgc3 |= (divisor | CPM_BRG_EN);
+ cp->cp_sicr |= 0x00000012;
+ break;
+ case 4:
+ cp->cp_brgc4 = (divisor | CPM_BRG_EN);
+ cp->cp_sicr |= 0x0000001B;
+ break;
+ default:
+ udc_state = STATE_ERROR;
+ break;
+ }
+
+#else
+#error "CFG_USB_EXTC_CLK or CFG_USB_BRGCLK must be defined"
+#endif
+
+}
+
+/* mpc8xx_udc_cbd_attach
+ *
+ * attach a cbd to and endpoint
+ */
+static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size)
+{
+
+ if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) {
+ udc_state = STATE_ERROR;
+ return;
+ }
+
+ if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT ||
+ (!tx_size && !rx_size)) {
+ udc_state = STATE_ERROR;
+ return;
+ }
+
+ /* Attach CBD to appropiate Parameter RAM Endpoint data structure */
+ if (rx_size) {
+ endpoints[ep]->rbase = (u32) rx_cbd[rx_ct];
+ endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+ rx_ct++;
+
+ if (!ep) {
+
+ endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+ rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+ rx_ct++;
+
+ } else {
+ rx_ct += 2;
+ endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct];
+ rx_cbd[rx_ct]->cbd_sc |= RX_BD_W;
+ rx_ct++;
+ }
+
+ /* Where we expect to RX data on this endpoint */
+ ep_ref[ep].prx = rx_cbd[rx_ct - 1];
+ } else {
+
+ ep_ref[ep].prx = 0;
+ endpoints[ep]->rbase = 0;
+ endpoints[ep]->rbptr = 0;
+ }
+
+ if (tx_size) {
+ endpoints[ep]->tbase = (u32) tx_cbd[tx_ct];
+ endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct];
+ tx_ct++;
+ } else {
+ endpoints[ep]->tbase = 0;
+ endpoints[ep]->tbptr = 0;
+ }
+
+ endpoints[ep]->tstate = 0;
+ endpoints[ep]->tbcnt = 0;
+ endpoints[ep]->mrblr = EP_MAX_PKT;
+ endpoints[ep]->rfcr = 0x18;
+ endpoints[ep]->tfcr = 0x18;
+ ep_ref[ep].sc |= EP_ATTACHED;
+
+ DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n",
+ ep, endpoints[ep]->rbase, endpoints[ep]->rbptr,
+ endpoints[ep]->tbase, endpoints[ep]->tbptr,
+ ep_ref[ep].prx);
+
+ return;
+}
+
+/* mpc8xx_udc_cbd_init
+ *
+ * Allocate space for a cbd and allocate TX/RX data space
+ */
+static void mpc8xx_udc_cbd_init (void)
+{
+ int i = 0;
+
+ for (; i < TX_RING_SIZE; i++) {
+ tx_cbd[i] = (cbd_t *)
+ mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+ }
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_cbd[i] = (cbd_t *)
+ mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int));
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tx_cbd[i]->cbd_bufaddr =
+ mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+
+ tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W);
+ tx_cbd[i]->cbd_datlen = 0x0000;
+ }
+
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_cbd[i]->cbd_bufaddr =
+ mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int));
+ rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E);
+ rx_cbd[i]->cbd_datlen = 0x0000;
+
+ }
+
+ return;
+}
+
+/* mpc8xx_udc_endpoint_init
+ *
+ * Attach an endpoint to some dpram
+ */
+static void mpc8xx_udc_endpoint_init (void)
+{
+ int i = 0;
+
+ for (; i < MAX_ENDPOINTS; i++) {
+ endpoints[i] = (usb_epb_t *)
+ mpc8xx_udc_alloc (sizeof (usb_epb_t), 32);
+ }
+}
+
+/* mpc8xx_udc_alloc
+ *
+ * Grab the address of some dpram
+ */
+static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment)
+{
+ u32 retaddr = address_base;
+
+ while (retaddr % alignment) {
+ retaddr++;
+ }
+ address_base += data_size;
+
+ return retaddr;
+}
+
+#endif /* CONFIG_MPC885_FAMILY && CONFIG_USB_DEVICE) */
diff --git a/drivers/usbdcore_omap1510.c b/drivers/usbdcore_omap1510.c
index 1d54a63575..84bb936d86 100644
--- a/drivers/usbdcore_omap1510.c
+++ b/drivers/usbdcore_omap1510.c
@@ -1517,4 +1517,31 @@ void udc_startup_events (struct usb_device_instance *device)
udc_enable (device);
}
+/**
+ * udc_irq - do pseudo interrupts
+ */
+void udc_irq(void)
+{
+ /* Loop while we have interrupts.
+ * If we don't do this, the input chain
+ * polling delay is likely to miss
+ * host requests.
+ */
+ while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) {
+ /* Handle any new IRQs */
+ omap1510_udc_irq ();
+ omap1510_udc_noniso_irq ();
+ }
+}
+
+/* Flow control */
+void udc_set_nak(int epid)
+{
+ /* TODO: implement this functionality in omap1510 */
+}
+
+void udc_unset_nak (int epid)
+{
+ /* TODO: implement this functionality in omap1510 */
+}
#endif
diff --git a/drivers/usbtty.c b/drivers/usbtty.c
index ce4a12e16e..a3b50131df 100644
--- a/drivers/usbtty.c
+++ b/drivers/usbtty.c
@@ -2,6 +2,9 @@
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, bodonoghue@codehermit.ie
+ *
* 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
@@ -25,20 +28,42 @@
#include <circbuf.h>
#include <devices.h>
#include "usbtty.h"
+#include "usb_cdc_acm.h"
+#include "usbdescriptors.h"
+#include <config.h> /* If defined, override Linux identifiers with
+ * vendor specific ones */
#if 0
-#define TTYDBG(fmt,args...) serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
+#define TTYDBG(fmt,args...)\
+ serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
#else
#define TTYDBG(fmt,args...) do{}while(0)
#endif
-#if 0
-#define TTYERR(fmt,args...) serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
+#if 1
+#define TTYERR(fmt,args...)\
+ serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\
+ __LINE__,##args)
#else
#define TTYERR(fmt,args...) do{}while(0)
#endif
/*
+ * Defines
+ */
+#define NUM_CONFIGS 1
+#define MAX_INTERFACES 2
+#define NUM_ENDPOINTS 3
+#define ACM_TX_ENDPOINT 3
+#define ACM_RX_ENDPOINT 2
+#define GSERIAL_TX_ENDPOINT 2
+#define GSERIAL_RX_ENDPOINT 1
+#define NUM_ACM_INTERFACES 2
+#define NUM_GSERIAL_INTERFACES 1
+#define CONFIG_USBD_DATA_INTERFACE_STR "Bulk Data Interface"
+#define CONFIG_USBD_CTRL_INTERFACE_STR "Control Interface"
+
+/*
* Buffers to hold input and output data
*/
#define USBTTY_BUFFER_SIZE 256
@@ -50,157 +75,336 @@ static circbuf_t usbtty_output;
* Instance variables
*/
static device_t usbttydev;
-static struct usb_device_instance device_instance[1];
-static struct usb_bus_instance bus_instance[1];
+static struct usb_device_instance device_instance[1];
+static struct usb_bus_instance bus_instance[1];
static struct usb_configuration_instance config_instance[NUM_CONFIGS];
-static struct usb_interface_instance interface_instance[NUM_INTERFACES];
-static struct usb_alternate_instance alternate_instance[NUM_INTERFACES];
-static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; /* one extra for control endpoint */
-
-/*
- * Static allocation of urbs
- */
-#define RECV_ENDPOINT 1
-#define TX_ENDPOINT 2
+static struct usb_interface_instance interface_instance[MAX_INTERFACES];
+static struct usb_alternate_instance alternate_instance[MAX_INTERFACES];
+/* one extra for control endpoint */
+static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1];
/*
* Global flag
*/
int usbtty_configured_flag = 0;
-
/*
* Serial number
*/
static char serial_number[16];
+
/*
- * Descriptors
+ * Descriptors, Strings, Local variables.
*/
+
+/* defined and used by usbdcore_ep0.c */
+extern struct usb_string_descriptor **usb_strings;
+
+/* Indicies, References */
+static unsigned short rx_endpoint = 0;
+static unsigned short tx_endpoint = 0;
+static unsigned short interface_count = 0;
+static struct usb_string_descriptor *usbtty_string_table[STR_COUNT];
+
+/* USB Descriptor Strings */
static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)];
static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)];
static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)];
static u8 wstrConfiguration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)];
-static u8 wstrInterface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)];
-
-static struct usb_string_descriptor *usbtty_string_table[] = {
- (struct usb_string_descriptor*)wstrLang,
- (struct usb_string_descriptor*)wstrManufacturer,
- (struct usb_string_descriptor*)wstrProduct,
- (struct usb_string_descriptor*)wstrSerial,
- (struct usb_string_descriptor*)wstrConfiguration,
- (struct usb_string_descriptor*)wstrInterface
-};
-extern struct usb_string_descriptor **usb_strings; /* defined and used by omap1510_ep0.c */
+static u8 wstrDataInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)];
+static u8 wstrCtrlInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)];
+/* Standard USB Data Structures */
+static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES];
+static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];
+static struct usb_configuration_descriptor *configuration_descriptor = 0;
static struct usb_device_descriptor device_descriptor = {
- bLength: sizeof(struct usb_device_descriptor),
- bDescriptorType: USB_DT_DEVICE,
- bcdUSB: USB_BCD_VERSION,
- bDeviceClass: USBTTY_DEVICE_CLASS,
- bDeviceSubClass: USBTTY_DEVICE_SUBCLASS,
- bDeviceProtocol: USBTTY_DEVICE_PROTOCOL,
- bMaxPacketSize0: EP0_MAX_PACKET_SIZE,
- idVendor: CONFIG_USBD_VENDORID,
- idProduct: CONFIG_USBD_PRODUCTID,
- bcdDevice: USBTTY_BCD_DEVICE,
- iManufacturer: STR_MANUFACTURER,
- iProduct: STR_PRODUCT,
- iSerialNumber: STR_SERIAL,
- bNumConfigurations: NUM_CONFIGS
- };
-static struct usb_configuration_descriptor config_descriptors[NUM_CONFIGS] = {
- {
- bLength: sizeof(struct usb_configuration_descriptor),
- bDescriptorType: USB_DT_CONFIG,
- wTotalLength: (sizeof(struct usb_configuration_descriptor)*NUM_CONFIGS) +
- (sizeof(struct usb_interface_descriptor)*NUM_INTERFACES) +
- (sizeof(struct usb_endpoint_descriptor)*NUM_ENDPOINTS),
- bNumInterfaces: NUM_INTERFACES,
- bConfigurationValue: 1,
- iConfiguration: STR_CONFIG,
- bmAttributes: BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED,
- bMaxPower: USBTTY_MAXPOWER
- },
-};
-static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES] = {
- {
- bLength: sizeof(struct usb_interface_descriptor),
- bDescriptorType: USB_DT_INTERFACE,
- bInterfaceNumber: 0,
- bAlternateSetting: 0,
- bNumEndpoints: NUM_ENDPOINTS,
- bInterfaceClass: USBTTY_INTERFACE_CLASS,
- bInterfaceSubClass: USBTTY_INTERFACE_SUBCLASS,
- bInterfaceProtocol: USBTTY_INTERFACE_PROTOCOL,
- iInterface: STR_INTERFACE
- },
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(USB_BCD_VERSION),
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
+ .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID),
+ .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE),
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIAL,
+ .bNumConfigurations = NUM_CONFIGS
};
-static struct usb_endpoint_descriptor ep_descriptors[NUM_ENDPOINTS] = {
- {
- bLength: sizeof(struct usb_endpoint_descriptor),
- bDescriptorType: USB_DT_ENDPOINT,
- bEndpointAddress: CONFIG_USBD_SERIAL_OUT_ENDPOINT | USB_DIR_OUT,
- bmAttributes: USB_ENDPOINT_XFER_BULK,
- wMaxPacketSize: CONFIG_USBD_SERIAL_OUT_PKTSIZE,
- bInterval: 0
- },
- {
- bLength: sizeof(struct usb_endpoint_descriptor),
- bDescriptorType: USB_DT_ENDPOINT,
- bEndpointAddress: CONFIG_USBD_SERIAL_IN_ENDPOINT | USB_DIR_IN,
- bmAttributes: USB_ENDPOINT_XFER_BULK,
- wMaxPacketSize: CONFIG_USBD_SERIAL_IN_PKTSIZE,
- bInterval: 0
- },
- {
- bLength: sizeof(struct usb_endpoint_descriptor),
- bDescriptorType: USB_DT_ENDPOINT,
- bEndpointAddress: CONFIG_USBD_SERIAL_INT_ENDPOINT | USB_DIR_IN,
- bmAttributes: USB_ENDPOINT_XFER_INT,
- wMaxPacketSize: CONFIG_USBD_SERIAL_INT_PKTSIZE,
- bInterval: 0
- },
+
+
+/*
+ * Static CDC ACM specific descriptors
+ */
+
+struct acm_config_desc {
+ struct usb_configuration_descriptor configuration_desc;
+
+ /* Master Interface */
+ struct usb_interface_descriptor interface_desc;
+
+ struct usb_class_header_function_descriptor usb_class_header;
+ struct usb_class_call_management_descriptor usb_class_call_mgt;
+ struct usb_class_abstract_control_descriptor usb_class_acm;
+ struct usb_class_union_function_descriptor usb_class_union;
+ struct usb_endpoint_descriptor notification_endpoint;
+
+ /* Slave Interface */
+ struct usb_interface_descriptor data_class_interface;
+ struct usb_endpoint_descriptor
+ data_endpoints[NUM_ENDPOINTS-1] __attribute__((packed));
+} __attribute__((packed));
+
+static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
+ {
+ .configuration_desc ={
+ .bLength =
+ sizeof(struct usb_configuration_descriptor),
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength =
+ cpu_to_le16(sizeof(struct acm_config_desc)),
+ .bNumInterfaces = NUM_ACM_INTERFACES,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG,
+ .bmAttributes =
+ BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
+ .bMaxPower = USBTTY_MAXPOWER
+ },
+ /* Interface 1 */
+ .interface_desc = {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0x01,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
+ .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
+ .iInterface = STR_CTRL_INTERFACE,
+ },
+ .usb_class_header = {
+ .bFunctionLength =
+ sizeof(struct usb_class_header_function_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_HEADER,
+ .bcdCDC = cpu_to_le16(110),
+ },
+ .usb_class_call_mgt = {
+ .bFunctionLength =
+ sizeof(struct usb_class_call_management_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_CMF,
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
+ },
+ .usb_class_acm = {
+ .bFunctionLength =
+ sizeof(struct usb_class_abstract_control_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_ACMF,
+ .bmCapabilities = 0x00,
+ },
+ .usb_class_union = {
+ .bFunctionLength =
+ sizeof(struct usb_class_union_function_descriptor),
+ .bDescriptorType = CS_INTERFACE,
+ .bDescriptorSubtype = USB_ST_UF,
+ .bMasterInterface = 0x00,
+ .bSlaveInterface0 = 0x01,
+ },
+ .notification_endpoint = {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x01 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize
+ = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+
+ /* Interface 2 */
+ .data_class_interface = {
+ .bLength =
+ sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x01,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_DATA,
+ .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE,
+ .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE,
+ .iInterface = STR_DATA_INTERFACE,
+ },
+ .data_endpoints = {
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x02 | USB_DIR_OUT,
+ .bmAttributes =
+ USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x03 | USB_DIR_IN,
+ .bmAttributes =
+ USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ },
+ },
};
-static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS] = {
- &(ep_descriptors[0]),
- &(ep_descriptors[1]),
- &(ep_descriptors[2]),
+
+static struct rs232_emu rs232_desc={
+ .dter = 115200,
+ .stop_bits = 0x00,
+ .parity = 0x00,
+ .data_bits = 0x08
};
-/* utility function for converting char* to wide string used by USB */
-static void str2wide (char *str, u16 * wide)
-{
- int i;
- for (i = 0; i < strlen (str) && str[i]; i++)
- wide[i] = (u16) str[i];
-}
+/*
+ * Static Generic Serial specific data
+ */
+
+
+struct gserial_config_desc {
+
+ struct usb_configuration_descriptor configuration_desc;
+ struct usb_interface_descriptor
+ interface_desc[NUM_GSERIAL_INTERFACES] __attribute__((packed));
+ struct usb_endpoint_descriptor
+ data_endpoints[NUM_ENDPOINTS] __attribute__((packed));
+
+} __attribute__((packed));
+
+static struct gserial_config_desc
+gserial_configuration_descriptors[NUM_CONFIGS] ={
+ {
+ .configuration_desc ={
+ .bLength = sizeof(struct usb_configuration_descriptor),
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength =
+ cpu_to_le16(sizeof(struct gserial_config_desc)),
+ .bNumInterfaces = NUM_GSERIAL_INTERFACES,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG,
+ .bmAttributes =
+ BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
+ .bMaxPower = USBTTY_MAXPOWER
+ },
+ .interface_desc = {
+ {
+ .bLength =
+ sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = NUM_ENDPOINTS,
+ .bInterfaceClass =
+ COMMUNICATIONS_INTERFACE_CLASS_VENDOR,
+ .bInterfaceSubClass =
+ COMMUNICATIONS_NO_SUBCLASS,
+ .bInterfaceProtocol =
+ COMMUNICATIONS_NO_PROTOCOL,
+ .iInterface = STR_DATA_INTERFACE
+ },
+ },
+ .data_endpoints = {
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x01 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_OUT_PKTSIZE),
+ .bInterval= 0xFF,
+ },
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x02 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_IN_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x03 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE),
+ .bInterval = 0xFF,
+ },
+ },
+ },
+};
/*
- * Prototypes
+ * Static Function Prototypes
*/
+
static void usbtty_init_strings (void);
static void usbtty_init_instances (void);
static void usbtty_init_endpoints (void);
-
+static void usbtty_init_terminal_type(short type);
static void usbtty_event_handler (struct usb_device_instance *device,
- usb_device_event_t event, int data);
+ usb_device_event_t event, int data);
+static int usbtty_cdc_setup(struct usb_device_request *request,
+ struct urb *urb);
static int usbtty_configured (void);
-
static int write_buffer (circbuf_t * buf);
static int fill_buffer (circbuf_t * buf);
void usbtty_poll (void);
-static void pretend_interrupts (void);
+/* utility function for converting char* to wide string used by USB */
+static void str2wide (char *str, u16 * wide)
+{
+ int i;
+ for (i = 0; i < strlen (str) && str[i]; i++){
+ #if defined(__LITTLE_ENDIAN)
+ wide[i] = (u16) str[i];
+ #elif defined(__BIG_ENDIAN)
+ wide[i] = ((u16)(str[i])<<8);
+ #else
+ #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined"
+ #endif
+ }
+}
/*
* Test whether a character is in the RX buffer
*/
+
int usbtty_tstc (void)
{
+ struct usb_endpoint_instance *endpoint =
+ &endpoint_instance[rx_endpoint];
+
+ /* If no input data exists, allow more RX to be accepted */
+ if(usbtty_input.size <= 0){
+ udc_unset_nak(endpoint->endpoint_address&0x03);
+ }
+
usbtty_poll ();
return (usbtty_input.size > 0);
}
@@ -210,15 +414,21 @@ int usbtty_tstc (void)
* otherwise. When the function is succesfull, the character read is
* written into its argument c.
*/
+
int usbtty_getc (void)
{
char c;
+ struct usb_endpoint_instance *endpoint =
+ &endpoint_instance[rx_endpoint];
while (usbtty_input.size <= 0) {
+ udc_unset_nak(endpoint->endpoint_address&0x03);
usbtty_poll ();
}
buf_pop (&usbtty_input, &c, 1);
+ udc_set_nak(endpoint->endpoint_address&0x03);
+
return c;
}
@@ -238,7 +448,6 @@ void usbtty_putc (const char c)
}
}
-
/* usbtty_puts() helper function for finding the next '\n' in a string */
static int next_nl_pos (const char *s)
{
@@ -252,8 +461,9 @@ static int next_nl_pos (const char *s)
}
/*
- * Output a string to the usb client port.
+ * Output a string to the usb client port - implementing flow control
*/
+
static void __usbtty_puts (const char *str, int len)
{
int maxlen = usbtty_output.totalsize;
@@ -261,22 +471,19 @@ static void __usbtty_puts (const char *str, int len)
/* break str into chunks < buffer size, if needed */
while (len > 0) {
- space = maxlen - usbtty_output.size;
+ usbtty_poll ();
+ space = maxlen - usbtty_output.size;
/* Empty buffer here, if needed, to ensure space... */
- if (space <= 0) {
+ if (space) {
write_buffer (&usbtty_output);
- space = maxlen - usbtty_output.size;
- if (space <= 0) {
- space = len; /* allow old data to be overwritten. */
- }
- }
- n = MIN (space, MIN (len, maxlen));
- buf_push (&usbtty_output, str, n);
+ n = MIN (space, MIN (len, maxlen));
+ buf_push (&usbtty_output, str, n);
- str += n;
- len -= n;
+ str += n;
+ len -= n;
+ }
}
}
@@ -313,8 +520,10 @@ int drv_usbtty_init (void)
{
int rc;
char * sn;
+ char * tt;
int snlen;
+ /* Ger seiral number */
if (!(sn = getenv("serial#"))) {
sn = "000000000000";
}
@@ -327,6 +536,14 @@ int drv_usbtty_init (void)
memcpy (serial_number, sn, snlen);
serial_number[snlen] = '\0';
+ /* Decide on which type of UDC device to be.
+ */
+
+ if(!(tt = getenv("usbtty"))) {
+ tt = "generic";
+ }
+ usbtty_init_terminal_type(strcmp(tt,"cdc_acm"));
+
/* prepare buffers... */
buf_init (&usbtty_input, USBTTY_BUFFER_SIZE);
buf_init (&usbtty_output, USBTTY_BUFFER_SIZE);
@@ -337,7 +554,7 @@ int drv_usbtty_init (void)
usbtty_init_strings ();
usbtty_init_instances ();
- udc_startup_events (device_instance); /* Enable our device, initialize udc pointers */
+ udc_startup_events (device_instance);/* Enable dev, init udc pointers */
udc_connect (); /* Enable pullup for host detection */
usbtty_init_endpoints ();
@@ -362,30 +579,48 @@ static void usbtty_init_strings (void)
{
struct usb_string_descriptor *string;
+ usbtty_string_table[STR_LANG] =
+ (struct usb_string_descriptor*)wstrLang;
+
string = (struct usb_string_descriptor *) wstrManufacturer;
- string->bLength = sizeof (wstrManufacturer);
+ string->bLength = sizeof(wstrManufacturer);
string->bDescriptorType = USB_DT_STRING;
str2wide (CONFIG_USBD_MANUFACTURER, string->wData);
+ usbtty_string_table[STR_MANUFACTURER]=string;
+
string = (struct usb_string_descriptor *) wstrProduct;
- string->bLength = sizeof (wstrProduct);
+ string->bLength = sizeof(wstrProduct);
string->bDescriptorType = USB_DT_STRING;
str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData);
+ usbtty_string_table[STR_PRODUCT]=string;
+
string = (struct usb_string_descriptor *) wstrSerial;
- string->bLength = 2 + 2*strlen(serial_number);
+ string->bLength = sizeof(serial_number);
string->bDescriptorType = USB_DT_STRING;
str2wide (serial_number, string->wData);
+ usbtty_string_table[STR_SERIAL]=string;
+
string = (struct usb_string_descriptor *) wstrConfiguration;
- string->bLength = sizeof (wstrConfiguration);
+ string->bLength = sizeof(wstrConfiguration);
string->bDescriptorType = USB_DT_STRING;
str2wide (CONFIG_USBD_CONFIGURATION_STR, string->wData);
+ usbtty_string_table[STR_CONFIG]=string;
- string = (struct usb_string_descriptor *) wstrInterface;
- string->bLength = sizeof (wstrInterface);
+
+ string = (struct usb_string_descriptor *) wstrDataInterface;
+ string->bLength = sizeof(wstrDataInterface);
string->bDescriptorType = USB_DT_STRING;
- str2wide (CONFIG_USBD_INTERFACE_STR, string->wData);
+ str2wide (CONFIG_USBD_DATA_INTERFACE_STR, string->wData);
+ usbtty_string_table[STR_DATA_INTERFACE]=string;
+
+ string = (struct usb_string_descriptor *) wstrCtrlInterface;
+ string->bLength = sizeof(wstrCtrlInterface);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide (CONFIG_USBD_CTRL_INTERFACE_STR, string->wData);
+ usbtty_string_table[STR_CTRL_INTERFACE]=string;
/* Now, initialize the string table for ep0 handling */
usb_strings = usbtty_string_table;
@@ -400,6 +635,7 @@ static void usbtty_init_instances (void)
device_instance->device_state = STATE_INIT;
device_instance->device_descriptor = &device_descriptor;
device_instance->event = usbtty_event_handler;
+ device_instance->cdc_recv_setup = usbtty_cdc_setup;
device_instance->bus = bus_instance;
device_instance->configurations = NUM_CONFIGS;
device_instance->configuration_instance_array = config_instance;
@@ -415,8 +651,8 @@ static void usbtty_init_instances (void)
/* configuration instance */
memset (config_instance, 0,
sizeof (struct usb_configuration_instance));
- config_instance->interfaces = NUM_INTERFACES;
- config_instance->configuration_descriptor = config_descriptors;
+ config_instance->interfaces = interface_count;
+ config_instance->configuration_descriptor = configuration_descriptor;
config_instance->interface_instance_array = interface_instance;
/* interface instance */
@@ -447,17 +683,22 @@ static void usbtty_init_instances (void)
sizeof (struct usb_endpoint_instance));
endpoint_instance[i].endpoint_address =
- ep_descriptors[i - 1].bEndpointAddress;
+ ep_descriptor_ptrs[i - 1]->bEndpointAddress;
- endpoint_instance[i].rcv_packetSize =
- ep_descriptors[i - 1].wMaxPacketSize;
endpoint_instance[i].rcv_attributes =
- ep_descriptors[i - 1].bmAttributes;
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+ endpoint_instance[i].rcv_packetSize =
+ le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
+
+ endpoint_instance[i].tx_attributes =
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
endpoint_instance[i].tx_packetSize =
- ep_descriptors[i - 1].wMaxPacketSize;
+ le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
+
endpoint_instance[i].tx_attributes =
- ep_descriptors[i - 1].bmAttributes;
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
urb_link_init (&endpoint_instance[i].rcv);
urb_link_init (&endpoint_instance[i].rdy);
@@ -480,13 +721,79 @@ static void usbtty_init_endpoints (void)
int i;
bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
- for (i = 0; i <= NUM_ENDPOINTS; i++) {
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
udc_setup_ep (device_instance, i, &endpoint_instance[i]);
}
}
+/* usbtty_init_terminal_type
+ *
+ * Do some late binding for our device type.
+ */
+static void usbtty_init_terminal_type(short type)
+{
+ switch(type){
+ /* CDC ACM */
+ case 0:
+ /* Assign endpoint descriptors */
+ ep_descriptor_ptrs[0] =
+ &acm_configuration_descriptors[0].notification_endpoint;
+ ep_descriptor_ptrs[1] =
+ &acm_configuration_descriptors[0].data_endpoints[0];
+ ep_descriptor_ptrs[2] =
+ &acm_configuration_descriptors[0].data_endpoints[1];
+
+ /* Enumerate Device Descriptor */
+ device_descriptor.bDeviceClass =
+ COMMUNICATIONS_DEVICE_CLASS;
+ device_descriptor.idProduct =
+ cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM);
+
+ /* Assign endpoint indices */
+ tx_endpoint = ACM_TX_ENDPOINT;
+ rx_endpoint = ACM_RX_ENDPOINT;
+
+ /* Configuration Descriptor */
+ configuration_descriptor =
+ (struct usb_configuration_descriptor*)
+ &acm_configuration_descriptors;
+
+ /* Interface count */
+ interface_count = NUM_ACM_INTERFACES;
+ break;
+
+ /* BULK IN/OUT & Default */
+ case 1:
+ default:
+ /* Assign endpoint descriptors */
+ ep_descriptor_ptrs[0] =
+ &gserial_configuration_descriptors[0].data_endpoints[0];
+ ep_descriptor_ptrs[1] =
+ &gserial_configuration_descriptors[0].data_endpoints[1];
+ ep_descriptor_ptrs[2] =
+ &gserial_configuration_descriptors[0].data_endpoints[2];
+
+ /* Enumerate Device Descriptor */
+ device_descriptor.bDeviceClass = 0xFF;
+ device_descriptor.idProduct =
+ cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL);
+
+ /* Assign endpoint indices */
+ tx_endpoint = GSERIAL_TX_ENDPOINT;
+ rx_endpoint = GSERIAL_RX_ENDPOINT;
+
+ /* Configuration Descriptor */
+ configuration_descriptor =
+ (struct usb_configuration_descriptor*)
+ &gserial_configuration_descriptors;
+
+ /* Interface count */
+ interface_count = NUM_GSERIAL_INTERFACES;
+ break;
+ }
+}
-/*********************************************************************************/
+/******************************************************************************/
static struct urb *next_urb (struct usb_device_instance *device,
struct usb_endpoint_instance *endpoint)
@@ -526,27 +833,39 @@ static int write_buffer (circbuf_t * buf)
return 0;
}
- if (buf->size) {
+ struct usb_endpoint_instance *endpoint =
+ &endpoint_instance[tx_endpoint];
+ struct urb *current_urb = NULL;
- struct usb_endpoint_instance *endpoint =
- &endpoint_instance[TX_ENDPOINT];
- struct urb *current_urb = NULL;
+ current_urb = next_urb (device_instance, endpoint);
+ /* TX data still exists - send it now
+ */
+ if(endpoint->sent < current_urb->actual_length){
+ if(udc_endpoint_write (endpoint)){
+ /* Write pre-empted by RX */
+ return -1;
+ }
+ }
+
+ if (buf->size) {
char *dest;
int space_avail;
int popnum, popped;
int total = 0;
- /* Break buffer into urb sized pieces, and link each to the endpoint */
+ /* Break buffer into urb sized pieces,
+ * and link each to the endpoint
+ */
while (buf->size > 0) {
- current_urb = next_urb (device_instance, endpoint);
+
if (!current_urb) {
TTYERR ("current_urb is NULL, buf->size %d\n",
buf->size);
return total;
}
- dest = current_urb->buffer +
+ dest = (char*)current_urb->buffer +
current_urb->actual_length;
space_avail =
@@ -562,14 +881,19 @@ static int write_buffer (circbuf_t * buf)
current_urb->actual_length += popped;
total += popped;
- /* If endpoint->last == 0, then transfers have not started on this endpoint */
+ /* If endpoint->last == 0, then transfers have
+ * not started on this endpoint
+ */
if (endpoint->last == 0) {
- udc_endpoint_write (endpoint);
+ if(udc_endpoint_write (endpoint)){
+ /* Write pre-empted by RX */
+ return -1;
+ }
}
- } /* end while */
+ }/* end while */
return total;
- } /* end if tx_urb */
+ }
return 0;
}
@@ -577,18 +901,22 @@ static int write_buffer (circbuf_t * buf)
static int fill_buffer (circbuf_t * buf)
{
struct usb_endpoint_instance *endpoint =
- &endpoint_instance[RECV_ENDPOINT];
+ &endpoint_instance[rx_endpoint];
if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) {
- unsigned int nb = endpoint->rcv_urb->actual_length;
+ unsigned int nb = 0;
char *src = (char *) endpoint->rcv_urb->buffer;
+ unsigned int rx_avail = buf->totalsize - buf->size;
+
+ if(rx_avail >= endpoint->rcv_urb->actual_length){
- buf_push (buf, src, nb);
- endpoint->rcv_urb->actual_length = 0;
+ nb = endpoint->rcv_urb->actual_length;
+ buf_push (buf, src, nb);
+ endpoint->rcv_urb->actual_length = 0;
+ }
return nb;
}
-
return 0;
}
@@ -597,7 +925,7 @@ static int usbtty_configured (void)
return usbtty_configured_flag;
}
-/*********************************************************************************/
+/******************************************************************************/
static void usbtty_event_handler (struct usb_device_instance *device,
usb_device_event_t event, int data)
@@ -619,8 +947,34 @@ static void usbtty_event_handler (struct usb_device_instance *device,
}
}
-/*********************************************************************************/
+/******************************************************************************/
+
+int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb)
+{
+ switch (request->bRequest){
+
+ case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */
+ break;
+ case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */
+ break;
+ case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits
+ * per character */
+ break;
+ case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */
+ break;
+ case ACM_GET_LINE_ENCODING : /* request DTE rate,
+ * stop/parity bits */
+ memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc));
+ urb->actual_length = sizeof(rs232_desc);
+
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+/******************************************************************************/
/*
* Since interrupt handling has not yet been implemented, we use this function
@@ -630,36 +984,29 @@ static void usbtty_event_handler (struct usb_device_instance *device,
void usbtty_poll (void)
{
/* New interrupts? */
- pretend_interrupts ();
+ udc_irq();
- /* Write any output data to host buffer (do this before checking interrupts to avoid missing one) */
+ /* Write any output data to host buffer
+ * (do this before checking interrupts to avoid missing one)
+ */
if (usbtty_configured ()) {
write_buffer (&usbtty_output);
}
/* New interrupts? */
- pretend_interrupts ();
+ udc_irq();
- /* Check for new data from host.. (do this after checking interrupts to get latest data) */
+ /* Check for new data from host..
+ * (do this after checking interrupts to get latest data)
+ */
if (usbtty_configured ()) {
fill_buffer (&usbtty_input);
}
/* New interrupts? */
- pretend_interrupts ();
-}
+ udc_irq();
-static void pretend_interrupts (void)
-{
- /* Loop while we have interrupts.
- * If we don't do this, the input chain
- * polling delay is likely to miss
- * host requests.
- */
- while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) {
- /* Handle any new IRQs */
- omap1510_udc_irq ();
- omap1510_udc_noniso_irq ();
- }
}
+
+
#endif
diff --git a/drivers/usbtty.h b/drivers/usbtty.h
index 79c2fe57d7..8154e3072e 100644
--- a/drivers/usbtty.h
+++ b/drivers/usbtty.h
@@ -2,6 +2,9 @@
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
+ * (C) Copyright 2006
+ * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit
+ *
* 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
@@ -21,44 +24,47 @@
#ifndef __USB_TTY_H__
#define __USB_TTY_H__
-
#include "usbdcore.h"
+#if defined(CONFIG_PPC)
+#include "usbdcore_mpc8xx.h"
+#elif defined(CONFIG_ARM)
#include "usbdcore_omap1510.h"
+#endif
+#include <version_autogenerated.h>
-#define NUM_CONFIGS 1
-#define NUM_INTERFACES 1
-#define NUM_ENDPOINTS 3
+/* If no VendorID/ProductID is defined in config.h, pretend to be Linux
+ * DO NOT Reuse this Vendor/Product setup with protocol incompatible devices */
-#define EP0_MAX_PACKET_SIZE 64
+#define CONFIG_USBD_VENDORID 0x0525 /* Linux/NetChip */
+#define CONFIG_USBD_PRODUCTID_GSERIAL 0xa4a6 /* gserial */
+#define CONFIG_USBD_PRODUCTID_CDCACM 0xa4a7 /* CDC ACM */
+#define CONFIG_USBD_MANUFACTURER "Das U-Boot"
+#define CONFIG_USBD_PRODUCT_NAME U_BOOT_VERSION
-#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB"
-#define CONFIG_USBD_INTERFACE_STR "Simple Serial Data Interface - Bulk Mode"
+#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB"
-#define CONFIG_USBD_SERIAL_OUT_ENDPOINT 2
-#define CONFIG_USBD_SERIAL_OUT_PKTSIZE 64
-#define CONFIG_USBD_SERIAL_IN_ENDPOINT 1
-#define CONFIG_USBD_SERIAL_IN_PKTSIZE 64
-#define CONFIG_USBD_SERIAL_INT_ENDPOINT 5
-#define CONFIG_USBD_SERIAL_INT_PKTSIZE 16
-
+#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT
+#define CONFIG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT
+#define CONFIG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT
+#define CONFIG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE
+#define CONFIG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE
#define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS
-#define USBTTY_DEVICE_SUBCLASS COMMUNICATIONS_NO_SUBCLASS
-#define USBTTY_DEVICE_PROTOCOL COMMUNICATIONS_NO_PROTOCOL
-
-#define USBTTY_INTERFACE_CLASS 0xFF /* Vendor Specific */
-#define USBTTY_INTERFACE_SUBCLASS 0x02
-#define USBTTY_INTERFACE_PROTOCOL 0x01
-#define USBTTY_BCD_DEVICE 0x0
-#define USBTTY_MAXPOWER 0x0
+#define USBTTY_BCD_DEVICE 0x00
+#define USBTTY_MAXPOWER 0x00
-#define STR_MANUFACTURER 1
-#define STR_PRODUCT 2
-#define STR_SERIAL 3
-#define STR_CONFIG 4
-#define STR_INTERFACE 5
+#define STR_LANG 0x00
+#define STR_MANUFACTURER 0x01
+#define STR_PRODUCT 0x02
+#define STR_SERIAL 0x03
+#define STR_CONFIG 0x04
+#define STR_DATA_INTERFACE 0x05
+#define STR_CTRL_INTERFACE 0x06
+#define STR_COUNT 0x07
#endif
OpenPOWER on IntegriCloud