[U-Boot] [PATCH 1/5] MSM7630: USB Gadget support

Srikanth Reddy Vintha srikanth.reddy at lntinfotech.com
Mon Aug 13 10:20:42 CEST 2012


Signed-off-by: Srikanth Reddy Vintha <srikanth.reddy at lntinfotech.com>
---
 arch/arm/include/asm/arch-msm7630/irqs.h |  162 +++++++++
 drivers/serial/usbtty.h                  |    2 +
 drivers/usb/gadget/Makefile              |    1 +
 drivers/usb/gadget/msm_udc.c             |  540 ++++++++++++++++++++++++++++++
 include/configs/msm7630_surf.h           |   11 +-
 include/usb/msm_udc.h                    |  178 ++++++++++
 6 files changed, 891 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-msm7630/irqs.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/irqs.h b/arch/arm/include/asm/arch-msm7630/irqs.h
new file mode 100644
index 0000000..b0c011f
--- /dev/null
+++ b/arch/arm/include/asm/arch-msm7630/irqs.h
@@ -0,0 +1,162 @@
+/*
+ * (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
+ *
+ */
+
+#ifndef __ASM_ARCH_MSM_IRQS_7X30_H
+#define __ASM_ARCH_MSM_IRQS_7X30_H
+
+/* MSM ACPU Interrupt Numbers */
+
+#define INT_DEBUG_TIMER_EXP	0
+#define INT_GPT0_TIMER_EXP	1
+#define INT_GPT1_TIMER_EXP	2
+#define INT_WDT0_ACCSCSSBARK	3
+#define INT_WDT1_ACCSCSSBARK	4
+#define INT_AVS_SVIC		5
+#define INT_AVS_SVIC_SW_DONE	6
+#define INT_SC_DBG_RX_FULL	7
+#define INT_SC_DBG_TX_EMPTY	8
+#define INT_SC_PERF_MON		9
+#define INT_AVS_REQ_DOWN	10
+#define INT_AVS_REQ_UP		11
+#define INT_SC_ACG		12
+/* SCSS_VICFIQSTS1[13:15] are RESERVED */
+#define INT_L2_SVICCPUIRPTREQ	16
+#define INT_L2_SVICDMANSIRPTREQ 17
+#define INT_L2_SVICDMASIRPTREQ  18
+#define INT_L2_SVICSLVIRPTREQ	19
+#define INT_AD5A_MPROC_APPS_0	20
+#define INT_AD5A_MPROC_APPS_1	21
+#define INT_A9_M2A_0		22
+#define INT_A9_M2A_1		23
+#define INT_A9_M2A_2		24
+#define INT_A9_M2A_3		25
+#define INT_A9_M2A_4		26
+#define INT_A9_M2A_5		27
+#define INT_A9_M2A_6		28
+#define INT_A9_M2A_7		29
+#define INT_A9_M2A_8		30
+#define INT_A9_M2A_9		31
+
+#define INT_AXI_EBI1_SC		(32 + 0)
+#define INT_IMEM_ERR		(32 + 1)
+#define INT_AXI_EBI0_SC		(32 + 2)
+#define INT_PBUS_SC_IRQC	(32 + 3)
+#define INT_PERPH_BUS_BPM	(32 + 4)
+#define INT_CC_TEMP_SENSE	(32 + 5)
+#define INT_UXMC_EBI0		(32 + 6)
+#define INT_UXMC_EBI1		(32 + 7)
+#define INT_EBI2_OP_DONE	(32 + 8)
+#define INT_EBI2_WR_ER_DONE	(32 + 9)
+#define INT_TCSR_SPSS_CE	(32 + 10)
+#define INT_EMDH		(32 + 11)
+#define INT_PMDH		(32 + 12)
+#define INT_MDC			(32 + 13)
+#define INT_MIDI_TO_SUPSS	(32 + 14)
+#define INT_LPA_2		(32 + 15)
+#define INT_GPIO_GROUP1_SECURE	(32 + 16)
+#define INT_GPIO_GROUP2_SECURE	(32 + 17)
+#define INT_GPIO_GROUP1		(32 + 18)
+#define INT_GPIO_GROUP2		(32 + 19)
+#define INT_MPRPH_SOFTRESET	(32 + 20)
+#define INT_PWB_I2C		(32 + 21)
+#define INT_PWB_I2C_2		(32 + 22)
+#define INT_TSSC_SAMPLE		(32 + 23)
+#define INT_TSSC_PENUP		(32 + 24)
+#define INT_TCHSCRN_SSBI	(32 + 25)
+#define INT_FM_RDS		(32 + 26)
+#define INT_KEYSENSE		(32 + 27)
+#define INT_USB_OTG_HS		(32 + 28)
+#define INT_USB_OTG_HS2		(32 + 29)
+#define INT_USB_OTG_HS3		(32 + 30)
+#define INT_RESERVED_BIT31	(32 + 31)
+
+#define INT_SPI_OUTPUT		(64 + 0)
+#define INT_SPI_INPUT		(64 + 1)
+#define INT_SPI_ERROR		(64 + 2)
+#define INT_UART1		(64 + 3)
+#define INT_UART1_RX		(64 + 4)
+#define INT_UART2		(64 + 5)
+#define INT_UART2_RX		(64 + 6)
+#define INT_UART3		(64 + 7)
+#define INT_UART3_RX		(64 + 8)
+#define INT_UART1DM_IRQ		(64 + 9)
+#define INT_UART1DM_RX		(64 + 10)
+#define INT_UART2DM_IRQ		(64 + 11)
+#define INT_UART2DM_RX		(64 + 12)
+#define INT_TSIF		(64 + 13)
+#define INT_ADM_SC1		(64 + 14)
+#define INT_ADM_SC2		(64 + 15)
+#define INT_MDP			(64 + 16)
+#define INT_VPE			(64 + 17)
+#define INT_GRP_2D		(64 + 18)
+#define INT_GRP_3D		(64 + 19)
+#define INT_ROTATOR		(64 + 20)
+#define INT_MFC720		(64 + 21)
+#define INT_JPEG		(64 + 22)
+#define INT_VFE			(64 + 23)
+#define INT_TV_ENC		(64 + 24)
+#define INT_PMIC_SSBI		(64 + 25)
+#define INT_MPM_1		(64 + 26)
+#define INT_TCSR_SPSS_SAMPLE	(64 + 27)
+#define INT_TCSR_SPSS_PENUP	(64 + 28)
+#define INT_MPM_2		(64 + 29)
+#define INT_SDC1_0		(64 + 30)
+#define INT_SDC1_1		(64 + 31)
+
+#define INT_SDC3_0		(96 + 0)
+#define INT_SDC3_1		(96 + 1)
+#define INT_SDC2_0		(96 + 2)
+#define INT_SDC2_1		(96 + 3)
+#define INT_SDC4_0		(96 + 4)
+#define INT_SDC4_1		(96 + 5)
+#define INT_PWB_QUP_IN		(96 + 6)
+#define INT_PWB_QUP_OUT		(96 + 7)
+#define INT_PWB_QUP_ERR		(96 + 8)
+/* SCSS_VICFIQSTS3[6:31] are RESERVED */
+
+/* Retrofit universal macro names */
+#define INT_ADM_AARM		INT_ADM_SC2
+#define INT_USB_HS		INT_USB_OTG_HS
+#define INT_USB_OTG		INT_USB_OTG_HS
+#define INT_TCHSCRN1		INT_TSSC_SAMPLE
+#define INT_TCHSCRN2		INT_TSSC_PENUP
+#define INT_GP_TIMER_EXP	INT_GPT0_TIMER_EXP
+#define INT_ADSP_A11		INT_AD5A_MPROC_APPS_0
+#define INT_ADSP_A9_A11		INT_AD5A_MPROC_APPS_1
+#define INT_MDDI_EXT		INT_EMDH
+#define INT_MDDI_PRI		INT_PMDH
+#define INT_MDDI_CLIENT		INT_MDC
+#define INT_NAND_WR_ER_DONE	INT_EBI2_WR_ER_DONE
+#define INT_NAND_OP_DONE	INT_EBI2_OP_DONE
+
+#define NR_MSM_IRQS		128
+#define NR_GPIO_IRQS		182
+#define PMIC8058_IRQ_BASE	(NR_MSM_IRQS + NR_GPIO_IRQS)
+#define NR_PMIC8058_GPIO_IRQS	40
+#define NR_PMIC8058_MPP_IRQS	12
+#define NR_PMIC8058_MISC_IRQS	8
+#define NR_PMIC8058_IRQS	(NR_PMIC8058_GPIO_IRQS +\
+				NR_PMIC8058_MPP_IRQS +\
+				NR_PMIC8058_MISC_IRQS)
+#define NR_BOARD_IRQS		NR_PMIC8058_IRQS
+
+#define NR_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS)
+
+#endif	/* __ASM_ARCH_MSM_IRQS_7X30_H */
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..35e9b20
--- /dev/null
+++ b/drivers/usb/gadget/msm_udc.c
@@ -0,0 +1,540 @@
+/*
+ * (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/irqs.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 = (struct ept_queue_item *)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);
+		}
+	}
+}
+
+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 */
+	/* Handle the case where there are completed dTDs waiting */
+	if (dTD_list[num] != NULL) {
+		struct ept_queue_item *dTDlast =
+			(struct ept_queue_item *)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);
+	}
+
+	return 0;
+}
+
+void udc_irq(void) /* Handle any USB events */
+{
+	unsigned num;
+	unsigned waited = 0;
+	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);
+	}
+	/* 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(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);
+}
+
+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..0539748 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
diff --git a/include/usb/msm_udc.h b/include/usb/msm_udc.h
new file mode 100644
index 0000000..782f9ac
--- /dev/null
+++ b/include/usb/msm_udc.h
@@ -0,0 +1,178 @@
+/*
+ * (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>
+#include <asm/arch/iomap.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
+
+#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 INFO_BYTES(n)         ((n) << 16)
+#define TERMINATE 1
+
+#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 PORTSC_PTC           (0xF << 16)
+#define PORTSC_PTC_TST_PKT   (0x4 << 16)
+
+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