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

Mohamed Haneef
2012-05-11T06:04:58Z

* Support for usb gadget device.

Signed-off-by: Mohamed Haneef <mohamed.haneef@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