[U-Boot] [PATCH 2/3] MSM7630 gadget usb support

Mohamed Haneef mohamed.haneef at lntinfotech.com
Fri May 11 08:04:58 CEST 2012


	* Support for usb gadget device.

Signed-off-by: Mohamed Haneef <mohamed.haneef at lntinfotech.com>
---
 arch/arm/include/asm/arch-msm7630/hsusb.h |  182 ++++++++++
 drivers/serial/usbtty.h                   |    2 +
 drivers/usb/gadget/Makefile               |    1 +
 drivers/usb/gadget/msm_udc.c              |  560 +++++++++++++++++++++++++++++
 include/configs/msm7630_surf.h            |   12 +-
 include/usb/msm_udc.h                     |   46 +++
 6 files changed, 800 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-msm7630/hsusb.h
 create mode 100644 drivers/usb/gadget/msm_udc.c
 create mode 100644 include/usb/msm_udc.h

diff --git a/arch/arm/include/asm/arch-msm7630/hsusb.h b/arch/arm/include/asm/arch-msm7630/hsusb.h
new file mode 100644
index 0000000..a20f907
--- /dev/null
+++ b/arch/arm/include/asm/arch-msm7630/hsusb.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ * (C) Copyright 2012
+ * Larsen & Toubro Infotech Ltd. <www.lntinfotech.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HSUSB_H_
+#define _HSUSB_H_
+
+#include <asm/arch/iomap.h>
+
+#ifndef MSM_USB_BASE
+#error Must define base address of USB controller MSM_USB_BASE
+#endif
+
+#define USB_ID               (MSM_USB_BASE + 0x0000)
+#define USB_HWGENERAL        (MSM_USB_BASE + 0x0004)
+#define USB_HWHOST           (MSM_USB_BASE + 0x0008)
+#define USB_HWDEVICE         (MSM_USB_BASE + 0x000C)
+#define USB_HWTXBUF          (MSM_USB_BASE + 0x0010)
+#define USB_HWRXBUF          (MSM_USB_BASE + 0x0014)
+#define USB_SBUSCFG          (MSM_USB_BASE + 0x0090)
+
+#define USB_AHB_BURST        (MSM_USB_BASE + 0x0090)
+#define USB_AHB_MODE         (MSM_USB_BASE + 0x0098)
+#define USB_CAPLENGTH        (MSM_USB_BASE + 0x0100) /* 8 bit */
+#define USB_HCIVERSION       (MSM_USB_BASE + 0x0102) /* 16 bit */
+#define USB_HCSPARAMS        (MSM_USB_BASE + 0x0104)
+#define USB_HCCPARAMS        (MSM_USB_BASE + 0x0108)
+#define USB_DCIVERSION       (MSM_USB_BASE + 0x0120)	/* 16 bit */
+#define USB_USBCMD           (MSM_USB_BASE + 0x0140)
+#define USB_USBSTS           (MSM_USB_BASE + 0x0144)
+#define USB_USBINTR          (MSM_USB_BASE + 0x0148)
+#define USB_FRINDEX          (MSM_USB_BASE + 0x014C)
+#define USB_DEVICEADDR       (MSM_USB_BASE + 0x0154)
+#define USB_ENDPOINTLISTADDR (MSM_USB_BASE + 0x0158)
+#define USB_BURSTSIZE        (MSM_USB_BASE + 0x0160)
+#define USB_TXFILLTUNING     (MSM_USB_BASE + 0x0164)
+#define USB_ULPI_VIEWPORT    (MSM_USB_BASE + 0x0170)
+#define USB_ENDPTNAK         (MSM_USB_BASE + 0x0178)
+#define USB_ENDPTNAKEN       (MSM_USB_BASE + 0x017C)
+#define USB_PORTSC           (MSM_USB_BASE + 0x0184)
+#define USB_OTGSC            (MSM_USB_BASE + 0x01A4)
+#define USB_USBMODE          (MSM_USB_BASE + 0x01A8)
+#define USB_ENDPTSETUPSTAT   (MSM_USB_BASE + 0x01AC)
+#define USB_ENDPTPRIME       (MSM_USB_BASE + 0x01B0)
+#define USB_ENDPTFLUSH       (MSM_USB_BASE + 0x01B4)
+#define USB_ENDPTSTAT        (MSM_USB_BASE + 0x01B8)
+#define USB_ENDPTCOMPLETE    (MSM_USB_BASE + 0x01BC)
+#define USB_ENDPTCTRL(n)     (MSM_USB_BASE + 0x01C0 + (4 * (n)))
+
+#define USBCMD_RESET   2
+#define USBCMD_ATTACH  1
+
+#define USBMODE_DEVICE 2
+#define USBMODE_HOST   3
+
+
+/* Endpoint Txfer Descriptor dTD as defined by DC */
+struct ept_queue_item {
+	struct ept_queue_item *next;
+#define UDC_DTD_TOTAL_BYTES(info) (((info) >> 16) & 0x7FFF)
+	unsigned info; /* 30:16-Total Bytes,7:0-Status */
+	unsigned page0;
+	unsigned page1;
+	unsigned page2;
+	unsigned page3;
+	unsigned page4;
+	unsigned reserved;
+};
+
+/* Endpoint Queue Head (dQH) as defined by the DC must take up 64K.
+ * There must be one dQH for every endpoint and direction. */
+struct ept_queue_head {
+	unsigned config;
+	unsigned current; /* read-only */
+	unsigned next;
+	unsigned info;
+	unsigned page0;
+	unsigned page1;
+	unsigned page2;
+	unsigned page3;
+	unsigned page4;
+	unsigned reserved_0;
+	unsigned char setup_data[8];
+	unsigned reserved_1;
+	unsigned reserved_2;
+	unsigned reserved_3;
+	unsigned reserved_4;
+};
+
+#define CONFIG_MAX_PKT(n)     ((n) << 16)
+#define CONFIG_ZLT            (1 << 29)    /* stop on zero-len xfer */
+#define CONFIG_IOS            (1 << 15)    /* IRQ on setup */
+
+
+
+#define TERMINATE 1
+
+#define INFO_BYTES(n)         ((n) << 16)
+#define INFO_IOC              (1 << 15)
+#define INFO_ACTIVE           (1 << 7)
+#define INFO_HALTED           (1 << 6)
+#define INFO_BUFFER_ERROR     (1 << 5)
+#define INFO_TX_ERROR         (1 << 3)
+
+#define STS_NAKI              (1 << 16)	/* */
+#define STS_SLI               (1 << 8)	/* R/WC - suspend state entered */
+#define STS_SRI               (1 << 7)	/* R/WC - SOF recv'd */
+#define STS_URI               (1 << 6)	/* R/WC - RESET recv'd - write to clear */
+#define STS_FRI               (1 << 3)	/* R/WC - Frame List Rollover */
+#define STS_PCI               (1 << 2)	/* R/WC - Port Change Detect */
+#define STS_UEI               (1 << 1)	/* R/WC - USB Error */
+#define STS_UI                (1 << 0)	/* R/WC - USB Transaction Complete */
+
+/* bits used in all the endpoint status registers */
+#define EPT_TX(n) (1 << ((n) + 16))
+#define EPT_RX(n) (1 << (n))
+
+
+#define CTRL_TXE              (1 << 23)	/* TX Endpoint Enable */
+#define CTRL_TXR              (1 << 22)	/* TX Data Toggle Reset */
+#define CTRL_TXI              (1 << 21)	/* TX Data Toggle Inhibit */
+#define CTRL_TXT(n)           ((n) << 18)	/* TX Endpoint Type */
+#define CTRL_TXD              (1 << 17)	/* TX Endpoint Data Source */
+#define CTRL_TXS              (1 << 16)	/* TX Endpoint Stall */
+#define CTRL_RXE              (1 << 7)	/* RX Endpoint Enable */
+#define CTRL_RXR              (1 << 6)	/* RX Data Toggle Reset */
+#define CTRL_RXI              (1 << 5)	/* RX Data Toggle Inhibit */
+#define CTRL_RXT(n)           ((n) << 2)	/* RX Endpoint Type */
+#define CTRL_RXD              (1 << 1)	/* RX Endpoint Data Source */
+#define CTRL_RXS              (1 << 0)	/* RX Endpint Stall */
+
+#define CTRL_TXT_CTRL         (0 << 18)
+#define CTRL_TXT_ISOCH        (1 << 18)
+#define CTRL_TXT_BULK         (2 << 18)
+#define CTRL_TXT_INT          (3 << 18)
+
+#define CTRL_RXT_CTRL         (0 << 2)
+#define CTRL_RXT_ISOCH        (1 << 2)
+#define CTRL_RXT_BULK         (2 << 2)
+#define CTRL_RXT_INT          (3 << 2)
+
+#define ULPI_WAKEUP           (1 << 31)
+#define ULPI_RUN              (1 << 30)
+#define ULPI_WRITE            (1 << 29)
+#define ULPI_READ             (0 << 29)
+#define ULPI_STATE_NORMAL     (1 << 27)
+#define ULPI_ADDR(n)          (((n) & 255) << 16)
+#define ULPI_DATA(n)          ((n) & 255)
+#define ULPI_DATA_READ(n)     (((n) >> 8) & 255)
+#define PORTSC_PTC           (0xF << 16)
+#define PORTSC_PTC_TST_PKT   (0x4 << 16)
+
+
+#endif
diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h
index e449cd7..8321447 100644
--- a/drivers/serial/usbtty.h
+++ b/drivers/serial/usbtty.h
@@ -35,6 +35,8 @@
 #include <usb/pxa27x_udc.h>
 #elif defined(CONFIG_SPEAR3XX) || defined(CONFIG_SPEAR600)
 #include <usb/spr_udc.h>
+#elif defined(CONFIG_MSM_UDC)
+#include <usb/msm_udc.h>
 #endif
 
 #include <version.h>
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 64b091f..44ffa8a 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -44,6 +44,7 @@ COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o
 COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o
 COBJS-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o
 COBJS-$(CONFIG_SPEARUDC) += spr_udc.o
+COBJS-$(CONFIG_MSM_UDC) += msm_udc.o
 endif
 endif
 
diff --git a/drivers/usb/gadget/msm_udc.c b/drivers/usb/gadget/msm_udc.c
new file mode 100644
index 0000000..7c2a14a
--- /dev/null
+++ b/drivers/usb/gadget/msm_udc.c
@@ -0,0 +1,560 @@
+/*
+ * (C) Copyright 2012
+ * Larsen & Toubro Infotech Ltd. <www.lntinfotech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <usbdevice.h>
+#include <usb/msm_udc.h>
+#include <asm/arch/hsusb.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/exclusion.h>
+#include <asm/arch/interrupts.h>
+#include "ep0.h"
+
+#define mdelay(ms) udelay((ms) * 1000)
+#define USB_EP_NUM(ep)	((ep)->endpoint_address & USB_ENDPOINT_NUMBER_MASK)
+#define USB_EP_DIR(ep)	((ep)->endpoint_address & USB_ENDPOINT_DIR_MASK)
+#define USB_EP_MAX_PKT(ep)	(USB_EP_DIR(ep) ? (ep)->tx_packetSize : \
+					(ep)->rcv_packetSize)
+#ifdef DEBUG
+/* Debug output must not use stdout as that can cause an infinite loop when
+ * stdout is usbtty */
+#define udcdbg serial_printf
+#else
+#define udcdbg(fmt, args...)
+#endif
+
+static struct ept_queue_head *dQHs;
+static int usb_highspeed; /* Binary value indicating supported speed */
+static struct usb_device_instance *udc_device;
+volatile static struct ept_queue_item *dTD_list[USB_MAX_EPTS];
+
+static struct urb *msm_udc_get_urb_by_dir(unsigned num, unsigned direction)
+{
+	struct usb_endpoint_instance *epi =
+		&udc_device->bus->endpoint_array[num];
+	struct urb **urb = (direction == USB_DIR_IN) ? &epi->tx_urb :
+		&epi->rcv_urb;
+
+	if (!*urb) {
+		*urb = usbd_alloc_urb(udc_device, epi);
+		udcdbg("ep%i%s allocating new URB %#08x\n", num,
+			USB_EP_DIR(epi) ? "in" : "out", (unsigned) *urb);
+	}
+
+	return *urb;
+}
+
+static struct urb *msm_udc_get_urb(unsigned num)
+{
+	struct usb_endpoint_instance *epi =
+		&udc_device->bus->endpoint_array[num];
+	return msm_udc_get_urb_by_dir(num, USB_EP_DIR(epi));
+}
+
+/**
+ * Returns the dQH from the list
+ * @param num 0..15 where 0 is the control endpoint
+ * @param direction = [USB_DIR_IN, USB_DIR_OUT]
+ */
+static struct ept_queue_head *msm_udc_get_dQH(unsigned num, unsigned direction)
+{
+	/* Even elements of dQHs are for RX and odd for TX */
+	return (struct ept_queue_head *) (dQHs + (num * 2) +
+		((direction == USB_DIR_IN) ? 1 : 0));
+}
+
+static void msm_udc_ep_enable(unsigned num, unsigned enable)
+{
+	unsigned n = readl(USB_ENDPTCTRL(num));
+
+	if (enable) {
+		struct usb_endpoint_instance *epi =
+			&udc_device->bus->endpoint_array[num];
+		unsigned direction = USB_EP_DIR(epi);
+
+		if (direction == USB_DIR_IN)
+			n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
+		else
+			n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
+
+		if (num != 0) {
+			struct ept_queue_head *dQH =
+				msm_udc_get_dQH(num, direction);
+			unsigned highspeed =
+				(((readl(USB_PORTSC) >> 26) & 3) == 2) ? 1 : 0;
+			unsigned max_packet = highspeed ? 512 : 64;
+			/* The Mult field is 0 for all but isochronous EPs */
+			dQH->config = CONFIG_MAX_PKT(max_packet) | CONFIG_ZLT;
+		}
+	}
+	writel(n, USB_ENDPTCTRL(num));
+}
+
+static void msm_udc_ep0_send_ack(void)
+{
+	struct usb_endpoint_instance *epi =
+		&udc_device->bus->endpoint_array[0];
+	struct urb *urb;
+
+	/* Send ACK in opposite direction to request */
+	epi->endpoint_address = USB_EP_DIR(epi) ? USB_DIR_OUT : USB_DIR_IN;
+	urb = msm_udc_get_urb(0);
+	urb->actual_length = 0;
+	udc_endpoint_write(epi);
+}
+
+#define SETUP(type, request) (((type) << 8) | (request))
+static void msm_udc_handle_setup(void)
+{
+	int err;
+	struct usb_device_request s;
+	struct ept_queue_head *dQH = msm_udc_get_dQH(0, USB_DIR_OUT);
+	struct usb_endpoint_instance *epi =
+		&udc_device->bus->endpoint_array[0];
+	struct urb *urb;
+
+	/* Setup Packet Handling (if Setup Lockout is used):
+	 * - Backup dQH.SsetupBuffer
+	 * - Write a 1 to USB_ENDPTSETUPSTAT which deactivates Setup Lockout
+	 * - Process the packet using the copy (flush/dealloc stale handshake
+	 *   phases)
+	 * - Before priming for handshake phase, wait for ENDPTSETUPSTAT to
+	 *   transition to zero */
+	memcpy(&s, dQH->setup_data, sizeof(struct usb_device_request));
+	writel(EPT_RX(0), USB_ENDPTSETUPSTAT);
+
+	epi->endpoint_address = s.bmRequestType & USB_REQ_DIRECTION_MASK;
+	urb = msm_udc_get_urb(0); /* URB is dependent on EP direction */
+	urb->device_request = s;
+
+	print_usb_device_request(&s);
+
+	/* Handle gadget internals and for IN requests setup urb */
+	err = ep0_recv_setup(urb);
+	if (err)
+		return;
+
+	switch (SETUP(s.bmRequestType, s.bRequest)) {
+	case SETUP(USB_REQ_RECIPIENT_DEVICE, USB_REQ_SET_CONFIGURATION):
+		udcdbg("set config\n");
+		if (s.wValue == 1) { /* Online */
+			unsigned i;
+			for (i = 1; i < udc_device->bus->max_endpoints; i++)
+				msm_udc_ep_enable(i, s.wValue);
+			usbd_device_event_irq(udc_device,
+				DEVICE_CONFIGURED, 0);
+		} else { /* Offline */
+			writel(0, USB_ENDPTCTRL(1));
+			usbd_device_event_irq(udc_device,
+				DEVICE_DE_CONFIGURED, 0);
+		}
+		msm_udc_ep0_send_ack();
+		break;
+	case SETUP(USB_REQ_RECIPIENT_ENDPOINT, USB_REQ_CLEAR_FEATURE):
+		udcdbg("clear feature\n");
+		if ((s.wValue == 0) && (s.wLength == 0)) {
+			unsigned num = s.wIndex & USB_ENDPOINT_NUMBER_MASK;
+			unsigned direction = s.wIndex & USB_ENDPOINT_DIR_MASK;
+
+			udcdbg("Clear feature ep%i%s\n", num, direction ?
+				"in" : "out");
+			msm_udc_ep_enable(num, 1);
+			msm_udc_ep0_send_ack();
+		} else {
+			err = -1; /* stall */
+		}
+		break;
+	case SETUP(USB_REQ_RECIPIENT_DEVICE, USB_REQ_SET_ADDRESS):
+		udcdbg("set address\n");
+		writel((s.wValue << 25) | (1 << 24), USB_DEVICEADDR);
+		usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
+		msm_udc_ep0_send_ack();
+		break;
+	case SETUP(USB_REQ_RECIPIENT_INTERFACE, USB_REQ_SET_INTERFACE):
+		udcdbg("set interface\n");
+		err = -1; /* stall */
+		break;
+	case SETUP(USB_REQ_RECIPIENT_DEVICE, USB_REQ_SET_FEATURE):
+		udcdbg("set feature\n");
+		if (s.wIndex == 0x400) {
+			udcdbg("Entering test mode for TST_PKT\n");
+			unsigned mode = readl(USB_PORTSC) & (~PORTSC_PTC);
+			writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
+		}
+		msm_udc_ep0_send_ack();
+		break;
+	}
+
+	if (!err) {
+		if ((s.bmRequestType & USB_REQ_DIRECTION_MASK) ==
+			USB_REQ_DEVICE2HOST)
+			udc_endpoint_write(epi); /* Request returns data */
+		/* else we already sent ACK as needed */
+	} else {
+		udcdbg("STALL\n");
+		writel((1<<16) | (1 << 0), USB_ENDPTCTRL(0));
+	}
+}
+
+static void msm_udc_ep_complete(unsigned num, unsigned direction)
+{
+	struct ept_queue_item *dTD = dTD_list[num];
+	struct usb_endpoint_instance *epi =
+		&udc_device->bus->endpoint_array[num];
+	struct urb *urb = msm_udc_get_urb_by_dir(num, direction);
+	unsigned left;
+	unsigned actual; /* urb->actual_length modified by _complete() */
+	int urb_bad = 0;
+
+	udcdbg("ep%i%s complete dTD=%p ", num, direction ? "in" : "out", dTD);
+	if (dTD_list[num] == NULL) {
+		udcdbg("FAIL no xfer has been queued\n");
+		return;
+	} else if (dTD->info & INFO_ACTIVE) {
+		udcdbg("BAIL transfer descriptor is active\n");
+		return;
+	} else if (dTD_list[num]->next == (struct ept_queue_item *) TERMINATE)
+		dTD_list[num] = NULL;
+	else {
+		udcdbg("popping dTD ");
+		dTD_list[num] = dTD_list[num]->next;
+	}
+
+	left = UDC_DTD_TOTAL_BYTES(dTD->info);
+	actual = ((num != 0) && (direction == USB_DIR_OUT)) ?
+		urb->buffer_length : urb->actual_length;
+	epi->last = actual - left;
+
+	if (dTD->info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TX_ERROR)) {
+		udcdbg("FAIL info=%x,page0=%x\n", dTD->info, dTD->page0);
+		urb_bad = 1;
+	} else
+		udcdbg("SUCCESS left=%i,sent=%i,last=%i,len=%i\n", left,
+			epi->sent, epi->last, urb->actual_length);
+
+	if (direction == USB_DIR_OUT)
+		usbd_rcv_complete(epi, epi->last, urb_bad);
+	else
+		usbd_tx_complete(epi);
+
+	if (!urb_bad && (num == 0) && (actual != 0))
+		msm_udc_ep0_send_ack();
+	free(dTD);
+}
+
+static void msm_udc_handle_usbsts(unsigned status)
+{
+	if (status & STS_SLI) {
+		udcdbg("STS_SLI = Suspend\n");
+		/* Could keep track and subsequently use Resume signalling but
+		 * this is optional */
+	}
+	if (status & STS_URI) {
+		unsigned num;
+		udcdbg("STS_URI = Reset\n");
+		/* Clear setup token semaphores */
+		writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
+		/* Clear endpoint complete status bits */
+		writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
+		writel(0xffffffff, USB_ENDPTFLUSH);
+		/* After reset, all EPs but ep0 are uninit and disabled */
+		writel(0, USB_ENDPTCTRL(1));
+		/* error out any pending reqs */
+		for (num = 0; num < udc_device->bus->max_endpoints; num++) {
+			while (dTD_list[num] != NULL) {
+				struct usb_endpoint_instance *epi =
+					&udc_device->bus->endpoint_array[num];
+				dTD_list[num]->info = INFO_HALTED;
+				msm_udc_ep_complete(num, USB_EP_DIR(epi));
+			}
+		}
+	}
+	if (status & STS_PCI) {
+		/* Comes after a Reset to indicate the transition into the
+		 * Default state and comes as part of the Reset process.  The
+		 * device speed can now be read out of PORTSC */
+		usb_highspeed = (((readl(USB_PORTSC) >> 26) & 3) == 2) ? 1 : 0;
+		udcdbg("STS_PCI = Port Change Detect HS=%i\n", usb_highspeed);
+		usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+	}
+	if ((status & STS_UI) || (status & STS_UEI)) {
+		unsigned n, num;
+		/* USB Interrupt and USB Error Interrupt */
+		udcdbg("STS_UI and STS_UEI = TX(s) Done\n");
+		if (status & STS_UEI)
+			udcdbg("Error %#x\n", readl(USB_ENDPTCOMPLETE));
+
+		n = readl(USB_ENDPTSETUPSTAT);
+		if (n & EPT_RX(0)) /* Handle setup first */
+			msm_udc_handle_setup();
+
+		n = readl(USB_ENDPTCOMPLETE);
+		writel(n, USB_ENDPTCOMPLETE);
+		udcdbg("USB_ENDPTCOMPLETE = %#x\n", n);
+		for (num = 0; num < udc_device->bus->max_endpoints; num++) {
+			if (EPT_TX(num) & n)
+				msm_udc_ep_complete(num, USB_DIR_IN);
+			if (EPT_RX(num) & n)
+				msm_udc_ep_complete(num, USB_DIR_OUT);
+		}
+	}
+}
+
+static enum handler_return msm_udc_interrupt_handler(void *arg)
+{
+	unsigned sts = readl(USB_USBSTS);
+	writel(sts, USB_USBSTS);
+	sts &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
+
+	udcdbg("\nmsm_udc_interrupt_handler(USBSTS=%#0x):\n", sts);
+
+	while (sts != 0) {
+		msm_udc_handle_usbsts(sts);
+
+		sts = readl(USB_USBSTS);
+		writel(sts, USB_USBSTS);
+		sts &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
+		udcdbg("USBSTS = %#x\n", sts);
+	}
+
+	return INT_RESCHEDULE;
+}
+
+int udc_endpoint_write(struct usb_endpoint_instance *epi)
+{
+	unsigned num = USB_EP_NUM(epi);
+	unsigned direction = USB_EP_DIR(epi);
+	struct urb *urb = msm_udc_get_urb(num);
+	struct ept_queue_head *dQH = msm_udc_get_dQH(num, direction);
+	struct ept_queue_item *dTD = memalign(sizeof(*dTD), sizeof(*dTD));
+	unsigned length = ((num == 0) | (direction == USB_DIR_IN)) ?
+		urb->actual_length : urb->buffer_length;
+
+	udcdbg("ep%i%sWrite buffer=%p,length=%i,actual_length=%i,dTD=%p",
+		num, direction ? "in" : "out", urb->buffer, length,
+		urb->actual_length, dTD);
+
+	if (length > 0x4000)
+		return -1;
+
+	epi->sent = 0;
+	/* From 6.6.2 Building a Transfer Descriptor:
+	 * 1. Initialize first 7 DWords to 0.
+	 * 2. Set the terminate bit to 1
+	 * 3. Fill in total bytes with transfer size.
+	 * 4. Set the interrupt on complete if desired.
+	 * 5. Initialize the status field with the active bit set to 1 and
+	 *	all remaining status bits set to 0
+	 * 6. Fill in buffer pointer page 0 and the current offset to
+	 *	point to the start of the data buffer.
+	 * 7. Initialize buffer pointer page 1 through page 4 to be one
+	 *	greater than each of the previous buffer pointer. */
+	memset(dTD, 0, sizeof(*dTD));
+	dTD->next = (struct ept_queue_item *) TERMINATE;
+	dTD->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE;
+	dTD->page0 = (unsigned) urb->buffer;
+	dTD->page1 = ((unsigned) urb->buffer & 0xfffff000) + 0x1000;
+	dTD->page2 = ((unsigned) urb->buffer & 0xfffff000) + 0x2000;
+	dTD->page3 = ((unsigned) urb->buffer & 0xfffff000) + 0x3000;
+
+	/* To Prime:
+	 * 1. Fetch the dTD for the transaction pointed to by dQH
+	 * 2. Store the dTD in the dQH
+	 * 3. Indicate primed in ENDPTPRIME */
+	enter_critical_section();
+	/* Handle the case where there are completed dTDs waiting */
+	if (dTD_list[num] != NULL) {
+		struct ept_queue_item *dTDlast = dTD_list[num];
+		unsigned qDepth = 1;
+		while (dTDlast->next != (struct ept_queue_item *) TERMINATE) {
+			dTDlast = dTDlast->next;
+			qDepth++;
+		}
+		udcdbg(",qing size=%i", qDepth);
+		dTDlast->next = dTD;
+	} else /* dQH only references active and queued dTDs */
+		dTD_list[num] = dTD;
+	if ((dQH->next != TERMINATE) && (dQH->next != 0))
+		udcdbg(",dQH busy\n");
+	else {
+		int bit = direction ? EPT_TX(num) : EPT_RX(num);
+		dQH->next = (unsigned) dTD;
+		dQH->info = 0;
+		udcdbg(",bit=%x,dQH=%p\n", bit, dQH);
+		writel(bit, USB_ENDPTPRIME);
+	}
+	exit_critical_section();
+
+	return 0;
+}
+
+void udc_irq(void) /* Handle any USB events */
+{
+	unsigned num;
+	unsigned waited = 0;
+	/* OUT endpoints must be primed to receive data from the host */
+	if (udc_device && (udc_device->device_state == STATE_CONFIGURED)) {
+		for (num = 1; num < udc_device->bus->max_endpoints; num++) {
+			struct usb_endpoint_instance *epi =
+				&udc_device->bus->endpoint_array[num];
+			if ((USB_EP_DIR(epi) == USB_DIR_OUT) &&
+				(dTD_list[num] == NULL)) {
+				struct urb *urb = msm_udc_get_urb(num);
+
+				if (urb->actual_length == 0)
+					udc_endpoint_write(epi);
+			}
+		}
+	}
+	/* Actual packet processing handled in IRQs */
+	/* Delay the main thread so only one TX can be queued at a tuime */
+	for (num = 0; num < udc_device->bus->max_endpoints; num++) {
+		struct usb_endpoint_instance *epi =
+			&udc_device->bus->endpoint_array[num];
+		if ((USB_EP_DIR(epi) == USB_DIR_IN) &&
+			(dTD_list[num] != NULL)) {
+			udcdbg("Waiting on epIn dTD_list[%i]=%p\n", num,
+				dTD_list[num]);
+			waited = 1;
+			while (dTD_list[num] != NULL)
+				/* Wait for interrupt to run */;
+			num = 0; /* Reset loop */
+		}
+	}
+	if (waited)
+		udcdbg("Done waiting\n");
+}
+
+void udc_set_nak(int epid)
+{
+	/* TODO */
+}
+
+void udc_unset_nak(int epid)
+{
+	/* TODO */
+}
+
+void udc_disconnect(void)
+{
+	udcdbg("Disconnect USB\n");
+	writel(0, USB_USBINTR);
+	mask_interrupt(INT_USB_HS);
+	writel(0x00080000, USB_USBCMD); /* disable pullup */
+	mdelay(10);
+}
+
+void udc_connect(void)
+{
+	udcdbg("Connect USB\n");
+	/* Enable the Run/Stop bit (D+ pullup enable): this initiates a restart
+	 * that the DCD must monitor for.  ep0 q heads must be setup */
+	writel(0x00080001, USB_USBCMD);
+	usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0);
+	/* Setup interrupts to handle incoming RESET on the bus */
+	enter_critical_section();
+	register_int_handler(INT_USB_HS, msm_udc_interrupt_handler, NULL);
+	writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
+	unmask_interrupt(INT_USB_HS);
+	exit_critical_section();
+}
+
+void udc_startup_events(struct usb_device_instance *device)
+{
+	udcdbg("Startup events\n");
+	usbd_device_event_irq(device, DEVICE_INIT, 0);
+	usbd_device_event_irq(device, DEVICE_CREATE, 0);
+	udc_device = device;
+}
+
+void udc_setup_ep(struct usb_device_instance *device,
+	unsigned int num, struct usb_endpoint_instance *epi)
+{
+	unsigned direction = USB_EP_DIR(epi);
+	struct ept_queue_head *dQH;
+	int cfg = CONFIG_MAX_PKT(USB_EP_MAX_PKT(epi)) | CONFIG_ZLT;
+
+	if (num >= USB_MAX_EPTS) {
+		udcdbg("ERROR DC only supports %i endpoints!\n", USB_MAX_EPTS);
+		return;
+	}
+
+	udcdbg("Init ep%i%s max=%i,", num, direction ? "in" : "out",
+		USB_EP_MAX_PKT(epi));
+
+	if (num == 0) {
+		/* For EP0 both directions must be setup */
+		direction = USB_DIR_IN;
+		dQH = msm_udc_get_dQH(num, direction);
+		udcdbg("dQH0in=%#08x,", (unsigned) dQH);
+		dQH->config = cfg;
+		direction = USB_DIR_OUT;
+		cfg |= CONFIG_IOS;
+	}
+	dQH = msm_udc_get_dQH(num, direction);
+	udcdbg("dQH%i%s=%#08x\n", num, direction ? "in" : "out",
+		(unsigned) dQH);
+	dQH->config = cfg;
+}
+
+int udc_init(void)
+{
+	hsusb_clock_init();
+	writel(0x81000000, USB_PORTSC); /* select ULPI phy */
+
+	udcdbg("udc_init():Read USB_ID %#010x from %#010x\n",
+		readl(USB_ID), USB_ID);
+
+	/* Init globals */
+	dQHs = NULL;
+	usb_highspeed = 0;
+	udc_device = NULL;
+	memset(dTD_list, 0, sizeof(dTD_list));
+
+	/* Reset */
+	writel(0x00080002, USB_USBCMD);
+	mdelay(20);
+
+	writel(0, USB_AHB_MODE);
+	writel(0, USB_AHB_BURST);
+
+	/* According to the CI13611 Programmers Guide, the software must ensure
+	 * that no interface data-structure reachable by the Device Controller
+	 * spans a 4K page boundary */
+	dQHs = memalign(4096, 4096);
+	if (dQHs == NULL)
+		return -1;
+	memset(dQHs, 0, 4096);
+	/* ENDPOINTLISTADDR must point to a contiguous chunk of memory where
+	 * the even numbered addresses are RX endpoints and odd TX endpoints.
+	 * The UDC indexes into this list based on the endpoint number.
+	 * Eg.	Offset	Queue-Head
+	 *	0	0 - Out
+	 *	4	0 - In
+	 *	8	1 - Out
+	 *	etc.	etc. */
+	writel((unsigned) dQHs, USB_ENDPOINTLISTADDR);
+	writel(0x02, USB_USBMODE); /* select DEVICE mode */
+	writel(0xffffffff, USB_ENDPTFLUSH);
+	mdelay(20);
+	return 0;
+}
diff --git a/include/configs/msm7630_surf.h b/include/configs/msm7630_surf.h
index 5aa6be0..e334a6f 100644
--- a/include/configs/msm7630_surf.h
+++ b/include/configs/msm7630_surf.h
@@ -36,8 +36,12 @@
 /*
  * Size of malloc() pool
  */
-#define CONFIG_SYS_MALLOC_LEN           (4 * 1024)	/* 4KB  */
-#define CONFIG_MSM7630
+#define CONFIG_SYS_MALLOC_LEN           (4 * 1024)     /* 4KB  */
+
+/* Enable USB */
+#define CONFIG_MSM_UDC
+#define CONFIG_USB_TTY
+#define CONFIG_USB_DEVICE
 
 /*
  * select serial console configuration
@@ -62,8 +66,9 @@
 #undef CONFIG_SYS_MAX_FLASH_SECT
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	"console=ttyS0,115200n8\0" \
+	"usbtty=cdc_acm\0"
 
-#define CONFIG_BOOTDELAY        2	/* -1 to disable auto boot */
+#define CONFIG_BOOTDELAY	-1	/* -1 to disable auto boot */
 
 /*
  * Miscellaneous configurable options
@@ -101,6 +106,7 @@
  * IRQ Settings
  */
 
+#define CONFIG_USE_IRQ
 #define CONFIG_STACKSIZE_IRQ    (4*1024)	/* IRQ stack */
 #define CONFIG_STACKSIZE_FIQ    (4*1024)	/* FIQ stack */
 
diff --git a/include/usb/msm_udc.h b/include/usb/msm_udc.h
new file mode 100644
index 0000000..c8b09c6
--- /dev/null
+++ b/include/usb/msm_udc.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 2012
+ * Larsen & Toubro Infotech Ltd. <www.lntinfotech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <usbdevice.h>
+
+#define USB_MAX_EPTS 16
+
+/* Define the EP number and packet sizes */
+#define EP0_MAX_PACKET_SIZE 64
+#define UDC_INT_ENDPOINT 1
+#define UDC_INT_PACKET_SIZE 64
+#define UDC_OUT_ENDPOINT 2
+#define UDC_OUT_PACKET_SIZE 512
+#define UDC_IN_ENDPOINT 3
+#define UDC_IN_PACKET_SIZE 512
+#define UDC_BULK_PACKET_SIZE 512
+
+extern void hsusb_clock_init(void);
+
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint);
+void udc_irq(void);
+void udc_set_nak(int epid);
+void udc_unset_nak(int epid);
+void udc_disconnect(void);
+void udc_connect(void);
+void udc_startup_events(struct usb_device_instance *device);
+void udc_setup_ep(struct usb_device_instance *device,
+	unsigned int epid, struct usb_endpoint_instance *ep);
+int udc_init(void);
-- 
1.7.1



More information about the U-Boot mailing list