usb device controller driver for port 1 of isp1760

Siddarth Gore gores at marvell.com
Fri Apr 16 06:25:06 EDT 2010


Hello All,

I did not find any code to get port 1 of Philips isp1760 to work as an
upstream port. so i am trying to write a isp1760-udc driver for it.

I am using ARM PB1176 board (which has isp1760 onboard) for debugging.
After initialization I get a SUSPEND interrupt and after that no
interrupt is generated even when I insert the usb cable.

Any idea what might be going wrong or how I can debug this? The register
initializations are done in udc_init_regs() function.

I am attaching the work so far, just for reference. but it is very raw
and far from finished, so please bare with me.

-siddarth

diff --git a/arch/arm/mach-realview/core.c
b/arch/arm/mach-realview/core.c
index 90bd4ef..3b58d4d 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c
@@ -175,7 +175,7 @@ int realview_eth_register(const char *name, struct
resource *res)
 }
 
 struct platform_device realview_usb_device = {
-	.name			= "isp1760",
+	.name			= "isp1760-udc",
 	.num_resources		= 2,
 };
 
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 11a3e0f..e373140 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -504,6 +504,18 @@ config USB_LANGWELL
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_ISP1760_UDC
+	boolean "ISP 1760 USB Device Controller"
+	select USB_GADGET_DUALSPEED
+	help
+	   ISP 1760 USB Device Controller.
+
+config USB_ISP1760_UDC
+	tristate
+	depends on USB_GADGET_ISP1760_UDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 
 #
 # LAST -- dummy/emulated controller
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 43b51da..02adaed 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)	+= ci13xxx_udc.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
+obj-$(CONFIG_USB_ISP1760_UDC)	+= isp1760-udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/gadget_chips.h
b/drivers/usb/gadget/gadget_chips.h
index e511fec..50c5c8e 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -142,6 +142,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_ISP1760_UDC
+#define gadget_is_isp1760_udc(g)    (!strcmp("isp1760-udc", (g)->name))
+#else
+#define gadget_is_isp1760_udc(g)    0
+#endif
+
 
 /**
  * usb_gadget_controller_number - support bcdDevice id convention
@@ -200,6 +206,8 @@ static inline int
usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x25;
 	else if (gadget_is_s3c_hsotg(gadget))
 		return 0x26;
+	else if (gadget_is_isp1760_udc(gadget))
+		return 0x27;
 	return -ENOENT;
 }
 
diff --git a/drivers/usb/gadget/isp1760-udc.c
b/drivers/usb/gadget/isp1760-udc.c
new file mode 100644
index 0000000..b5f18d0
--- /dev/null
+++ b/drivers/usb/gadget/isp1760-udc.c
@@ -0,0 +1,858 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/byteorder.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "isp1760-udc.h"
+
+
+#define	DRIVER_VERSION	"15 Apr 2010"
+
+static const char driver_name [] = "isp1760-udc";
+static const char ep0name[] = "ep0-setup";
+
+static void udc_init_regs(struct isp1760_udc *udc)
+{
+	u32 i;
+
+	/* unlock */
+	writel(0xaa37, udc->regs + DC_UNLOCK_DEV_REG);
+
+	/* reset */
+	writel(0x1 << 4, udc->regs + DC_MODE_REG);
+	mdelay(10);
+	writel(0x0, udc->regs + DC_MODE_REG);
+
+	writel(0xa5a5, udc->regs + DC_SCRATCH_REG);
+	readl(udc->regs + 0x304);
+	i = readl(udc->regs + DC_SCRATCH_REG);
+	printk("isp1760-udc: ret = %x\n", i);
+
+	/* global interrupt enabled, clock set to
+	 * always on */
+	writel(0x88, udc->regs + DC_MODE_REG);
+
+	/* interrupt on ack/nak for control and IN pipe,
+	 * ack/nyet/nak for OUT pipe, level triggered,
+	 * active low */
+	writel(0x0, udc->regs + DC_INT_CFG_REG);
+
+	/* enable all interrupts */
+//	writel(0x3ffffff, udc->regs + DC_INT_EN_REG);
+	writel(0xfb, udc->regs + DC_INT_EN_REG);
+
+	/* mode 0, 32-bit data bus */
+	writel(0x0, udc->regs + DC_DMA_CFG_REG);
+
+	/* DACK active low, DREQ active high */
+	writel(0x4, udc->regs + DC_DMA_HW_REG);
+
+	/* DREQ will drop at every DMA read/write */
+	writel(0x4, udc->regs + DC_DMA_BURST_CNT_REG);
+
+	/* enable all DMA interrupts */
+//	writel(0x1d00, udc->regs + DC_DMA_INT_EN_REG);
+
+	for (i = 1; i < NUM_ENDPOINTS; i++) {
+		/* max packet size for OUT ep */
+		writel(i << 1, udc->regs + DC_EP_INDEX_REG);
+		writel(udc->ep[i].maxpacket,
+			udc->regs + DC_EP_MAX_PKT_SIZE_REG);
+
+		/* max packet size for IN ep */
+		writel((i << 1) | 0x1, udc->regs + DC_EP_INDEX_REG);
+		writel(udc->ep[i].maxpacket,
+			udc->regs + DC_EP_MAX_PKT_SIZE_REG);
+	}
+}
+
+static int isp1760_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct isp1760_ep *ep = container_of(_ep, struct isp1760_ep, ep);
+	struct isp1760_udc *udc = ep->udc;
+	u16 maxpacket;
+	u32 tmp;
+	unsigned long flags;
+
+	if (!_ep || !ep
+			|| !desc || ep->desc
+			|| _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0
+			|| maxpacket > ep->maxpacket) {
+		printk("isp1760-udc: bad ep or descriptor\n");
+		return -EINVAL;
+	}
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		printk("isp1760-udc: bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	tmp = usb_endpoint_type(desc);
+	switch (tmp) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		printk("isp1760-udc: only one control endpoint\n");
+		return -EINVAL;
+	case USB_ENDPOINT_XFER_INT:
+		if (maxpacket > 64)
+			goto bogus_max;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		switch (maxpacket) {
+		case 8:
+		case 16:
+		case 32:
+		case 64:
+			goto ok;
+		}
+bogus_max:
+		printk("isp1760-udc: bogus maxpacket %d\n", maxpacket);
+		return -EINVAL;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (!ep->is_pingpong) {
+			printk("isp1760-udc: iso requires double buffering\n");
+			return -EINVAL;
+		}
+		break;
+	}
+
+ok:
+	local_irq_save(flags);
+
+	/* initialize endpoint to match this descriptor */
+	ep->is_in = usb_endpoint_dir_in(desc);
+	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
+	ep->stopped = 0;
+	ep->desc = desc;
+	ep->ep.maxpacket = maxpacket;
+
+	/* set type, double buffering and enable ep */
+	writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+	writel(maxpacket, udc->regs + DC_EP_MAX_PKT_SIZE_REG);
+	writel(ep->is_pingpong << 2 | tmp, udc->regs + DC_EP_TYPE_REG);
+	writel(1 << 3, udc->regs + DC_EP_TYPE_REG);
+
+	/* enable interrupt */
+	writel(ep->int_mask, udc->regs + DC_INT_EN_REG);
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+static void done(struct isp1760_ep *ep, struct isp1760_request *req,
int status)
+{
+	unsigned stopped = ep->stopped;
+	struct isp1760_udc *udc = ep->udc;
+	struct isp1760_ep *_ep;
+
+	list_del_init(&req->queue);
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+	if (status && status != -ESHUTDOWN)
+		printk("isp1760-udc: %s done %p, status %d\n", ep->ep.name, req,
status);
+
+	ep->stopped = 1;
+	req->req.complete(&ep->ep, &req->req);
+	ep->stopped = stopped;
+
+	/* terminate setup token */
+	if (ep == &udc->ep[1]) {
+		_ep = &udc->ep[2];
+		writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+		writel(1 << 1, udc->regs + DC_CTRL_FUNC_REG);
+	} else if (ep == &udc->ep[2]) {
+		_ep = &udc->ep[1];
+		writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+		writel(1 << 1, udc->regs + DC_CTRL_FUNC_REG);
+	}
+}
+
+static void nuke(struct isp1760_ep *ep, int status)
+{
+	struct isp1760_request *req;
+
+	ep->stopped = 1;
+	if (list_empty(&ep->queue))
+		return;
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct isp1760_request, queue);
+		done(ep, req, status);
+	}
+}
+
+static int isp1760_ep_disable (struct usb_ep * _ep)
+{
+	struct isp1760_ep *ep = container_of(_ep, struct isp1760_ep, ep);
+	struct isp1760_udc *udc = ep->udc;
+	unsigned long flags;
+	u32 reg;
+
+	if (ep == &ep->udc->ep[0])
+		return -EINVAL;
+
+	local_irq_save(flags);
+
+	nuke(ep, -ESHUTDOWN);
+
+	/* disable interrupt for this ep */
+	reg = readl(udc->regs + DC_INT_EN_REG);
+	writel(reg & ~ep->int_mask, udc->regs + DC_INT_EN_REG);
+	/* restore the endpoint's pristine config */
+	ep->desc = NULL;
+	ep->ep.maxpacket = ep->maxpacket;
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+static struct usb_request *
+isp1760_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct isp1760_request *req;
+
+	req = kzalloc(sizeof (struct isp1760_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+static void isp1760_ep_free_request(struct usb_ep *_ep, struct
usb_request *_req)
+{
+	struct isp1760_request *req;
+
+	req = container_of(_req, struct isp1760_request, req);
+	BUG_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static int read_fifo(struct isp1760_ep *ep, struct isp1760_request
*req)
+{
+	struct isp1760_udc *udc = ep->udc;
+	u8 *buf;
+	u32 reg;
+	unsigned int buflen, count;
+
+	buf = req->req.buf + req->req.actual;
+	buflen = req->req.length - req->req.actual;
+
+	writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+	reg = readl(udc->regs + DC_BUF_STATUS_REG);
+	if (!reg) {
+		printk("isp1760-udc: buffers not filled\n");
+		return 0;
+	}
+
+	count = readl(udc->regs + DC_BUF_LEN_REG);
+	if (count > ep->ep.maxpacket)
+		count = ep->ep.maxpacket;
+	if (count > buflen) {
+		printk("isp1760-udc: %s buffer overflow\n", ep->ep.name);
+		req->req.status = -EOVERFLOW;
+		count = buflen;
+	}
+	insl((unsigned long)(udc->regs + DC_DATA_PORT_REG), buf, count);
+
+	req->req.actual += count;
+
+	if (count == buflen)
+		done(ep, req, 0);
+
+	return 1;
+}
+
+static int write_fifo(struct isp1760_ep *ep, struct isp1760_request
*req)
+{
+	struct isp1760_udc *udc = ep->udc;
+	u8 *buf;
+	unsigned int buflen, count, is_last;
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+	buflen = req->req.length - req->req.actual;
+	if (ep->ep.maxpacket < buflen) {
+		count = ep->ep.maxpacket;
+		is_last = 0;
+	} else {
+		count = buflen;
+		is_last = (count < ep->ep.maxpacket) || !req->req.zero;
+	}
+	writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+	writel(count, udc->regs + DC_BUF_LEN_REG);
+
+	outsl((unsigned long)(udc->regs + DC_DATA_PORT_REG), buf, count);
+
+	req->req.actual += count;
+	if (is_last)
+		done(ep, req, 0);
+	return is_last;
+}
+
+static int isp1760_ep_queue(struct usb_ep *_ep,
+			struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct isp1760_request *req;
+	struct isp1760_ep *ep;
+	struct isp1760_udc *udc;
+	int status;
+	unsigned long flags;
+
+	req = container_of(_req, struct isp1760_request, req);
+	ep = container_of(_ep, struct isp1760_ep, ep);
+
+	if (!_req || !_req->complete
+			|| !_req->buf || !list_empty(&req->queue)) {
+		printk("isp1760-udc: invalid request\n");
+		return -EINVAL;
+	}
+
+	if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+		printk("isp1760-udc: invalid ep\n");
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+
+	if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		printk("isp1760-udc: invalid device\n");
+		return -EINVAL;
+	}
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	local_irq_save(flags);
+
+	if (list_empty(&ep->queue) && !ep->stopped) {
+		if (ep->is_in)
+			status = write_fifo(ep, req);
+		else
+			status = read_fifo(ep, req);
+	} else
+		status = 0;
+
+	if (req && !status)
+		list_add_tail (&req->queue, &ep->queue);
+	local_irq_restore(flags);
+	return (status < 0) ? status : 0;
+}
+
+static int isp1760_ep_dequeue(struct usb_ep *_ep, struct usb_request
*_req)
+{
+	struct isp1760_ep *ep;
+	struct isp1760_request *req;
+
+	ep = container_of(_ep, struct isp1760_ep, ep);
+	if (!_ep || ep->ep.name == ep0name)
+		return -EINVAL;
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req)
+		return -EINVAL;
+
+	done(ep, req, -ECONNRESET);
+	return 0;
+}
+
+static int isp1760_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct isp1760_ep *ep = container_of(_ep, struct isp1760_ep, ep);
+	struct isp1760_udc *udc = ep->udc;
+	unsigned long flags;
+	int status = 0;
+	u32 reg;
+
+	if (!_ep || ep->is_iso)
+		return -EINVAL;
+
+	local_irq_save(flags);
+
+	writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+	reg = readl(udc->regs + DC_BUF_STATUS_REG);
+	if (ep->is_in && (!list_empty(&ep->queue) || (reg & 0x3)))
+		status = -EAGAIN;
+	else {
+		/* stall ep */
+		reg = readl(udc->regs + DC_CTRL_FUNC_REG);
+		writel(reg | 1, udc->regs + DC_CTRL_FUNC_REG);
+		if (!value) {
+			/* reset ep */
+			reg = readl(udc->regs + DC_EP_TYPE_REG);
+			writel(reg & ~(1 << 3), udc->regs + DC_EP_TYPE_REG);
+			writel(reg | 1 << 3, udc->regs + DC_EP_TYPE_REG);
+		}
+	}
+
+	local_irq_restore(flags);
+	return status;
+}
+
+/*static int isp1760_ep_fifo_status(struct usb_ep *_ep)
+{
+}
+
+static void isp1760_ep_fifo_flush(struct usb_ep *_ep)
+{
+}*/
+
+static const struct usb_ep_ops isp1760_ep_ops = {
+	.enable		= isp1760_ep_enable,
+	.disable	= isp1760_ep_disable,
+	.alloc_request	= isp1760_ep_alloc_request,
+	.free_request	= isp1760_ep_free_request,
+	.queue		= isp1760_ep_queue,
+	.dequeue	= isp1760_ep_dequeue,
+	.set_halt	= isp1760_ep_set_halt,
+/*	.fifo_status	= isp1760_ep_fifo_status,
+	.fifo_flush	= isp1760_ep_fifo_flush,*/
+};
+
+static int isp1760_get_frame(struct usb_gadget *gadget)
+{
+	struct isp1760_udc *udc = to_udc(gadget);
+
+	return readl(udc->regs + DC_FRM_NUM_REG) & 0x7ff;
+}
+
+static const struct usb_gadget_ops isp1760_udc_ops = {
+	.get_frame		= isp1760_get_frame,
+};
+
+static void nop_release(struct device *dev)
+{
+	/* nothing to free */
+}
+
+static void udc_reinit(struct isp1760_udc *udc)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+	INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct isp1760_ep *ep = &udc->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+		ep->desc = NULL;
+		ep->stopped = 0;
+		ep->fifo_bank = 0;
+		ep->ep.maxpacket = ep->maxpacket;
+
+		INIT_LIST_HEAD(&ep->queue);
+	}
+}
+
+static void stop_activity(struct isp1760_udc *udc)
+{
+	struct usb_gadget_driver *driver = udc->driver;
+	int i;
+
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct isp1760_ep *ep = &udc->ep[i];
+		nuke(ep, -ESHUTDOWN);
+	}
+	if (driver)
+		driver->disconnect(&udc->gadget);
+
+	udc_reinit(udc);
+}
+
+union setup {
+	u8			raw[8];
+	struct usb_ctrlrequest	r;
+};
+
+static void handle_setup(struct isp1760_ep *ep)
+{
+	struct isp1760_udc *udc = ep->udc;
+	struct isp1760_ep *_ep;
+	u32 reg;
+	unsigned int count;
+	union setup pkt;
+
+	nuke(ep, 0);
+
+	/* read token */
+	writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+	reg = readl(udc->regs + DC_BUF_STATUS_REG);
+	if (!reg) {
+		printk("isp1760-udc: setup token buffer not filled\n");
+		return;
+	}
+
+	count = readl(udc->regs + DC_BUF_LEN_REG);
+	if (count != 8) {
+		printk("isp1760-udc: invalid token\n");
+		return;
+	}
+	insl((unsigned long)(udc->regs + DC_DATA_PORT_REG), pkt.raw, count);
+
+	if (!pkt.r.wLength) {
+		/* no data stage */
+		_ep = &udc->ep[2];
+		writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+		writel(1 << 1, udc->regs + DC_CTRL_FUNC_REG);
+	} else if (pkt.r.bRequestType & USB_DIR_IN) {
+		/* data IN stage */
+		_ep = &udc->ep[2];
+		writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+		writel(1 << 2, udc->regs + DC_CTRL_FUNC_REG);
+	} else {
+		/* data OUT stage */
+		_ep = &udc->ep[1];
+		writel(ep->index, udc->regs + DC_EP_INDEX_REG);
+		writel(1 << 2, udc->regs + DC_CTRL_FUNC_REG);
+	}
+
+	/* pass request up to the gadget driver */
+	if (udc->driver)
+		udc->driver->setup(&udc->gadget, &pkt.r);
+
+	/* clear interrupt */
+	writel(ep->int_mask, udc->regs + DC_INT_REG);
+}
+
+static int handle_ep(struct isp1760_ep *ep)
+{
+	struct isp1760_udc *udc = ep->udc;
+	struct isp1760_request *req;
+	int ret = 0;
+
+	if (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next,
+			struct isp1760_request, queue);
+
+		if (ep->is_in)
+			ret = write_fifo(ep, req);
+		else
+			ret = read_fifo(ep, req);
+	}
+
+	/* clear interrupt */
+	writel(ep->int_mask, udc->regs + DC_INT_REG);
+
+	return ret;
+}
+
+static irqreturn_t isp1760_udc_irq (int irq, void *_udc)
+{
+	struct isp1760_udc *udc = _udc;
+	u32 rescans = 5;
+
+	while (rescans--) {
+		u32 status;
+
+		status = readl(udc->regs + DC_INT_REG)
+				& readl(udc->regs + DC_INT_EN_REG);
+
+		if (!status)
+			break;
+
+		if (status & 1) {
+			/* bus reset */
+			stop_activity(udc);
+			writel(1, udc->regs + DC_INT_REG);
+		} else if (status & 1 << 3) {
+			/* suspend */
+			printk("isp1760-udc: in suspend\n");
+			writel(1 << 3, udc->regs + DC_INT_REG);
+		} else if (status & 1 << 4) {
+			/* suspend */
+			printk("isp1760-udc: in resume\n");
+			writel(1 << 4, udc->regs + DC_INT_REG);
+		} else if (status & 1 << 7) {
+			/* vbus */
+			printk("isp1760-udc: USB device connected\n");
+			writel(1 << 7, udc->regs + DC_INT_REG);
+		} else if (status & 1 << 5) {
+			/* hs_stat */
+			printk("isp1760-udc: device high speed mode\n");
+			writel(1 << 5, udc->regs + DC_INT_REG);
+		} else if (status & 1 << 6) {
+			/* dma */
+			printk("isp1760-udc: dma interrupt\n");
+			writel(1 << 6, udc->regs + DC_INT_REG);
+		} else {
+			int i;
+			unsigned mask = 1 << 10;
+			struct isp1760_ep *ep = &udc->ep[1];
+
+			if (status & 1 << 8)
+				handle_setup(&udc->ep[0]);
+			for (i = 1; i < NUM_ENDPOINTS; i++) {
+				mask <<= 1;
+				if (status & mask)
+					handle_ep(ep);
+				ep++;
+			}
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct isp1760_udc controller = {
+	.gadget = {
+		.ops	= &isp1760_udc_ops,
+		.ep0	= &controller.ep[0].ep,
+		.name	= driver_name,
+		.dev	= {
+			.init_name = "gadget",
+			.release = nop_release,
+		}
+	},
+	.ep[0] = {
+		.ep = {
+			.name	= ep0name,
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.maxpacket	= 8,
+		.index		= 1 << 5,
+		.int_mask	= 1 << 8,
+	},
+	.ep[1] = {
+		.ep = {
+			.name	= "ep0-out",
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.is_pingpong	= 0,
+		.maxpacket	= 64,
+		.index		= 0,
+		.int_mask	= 1 << 10,
+	},
+	.ep[2] = {
+		.ep = {
+			.name	= "ep0-in",
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.is_pingpong	= 0,
+		.maxpacket	= 64,
+		.index		= 1,
+		.int_mask	= 1 << 11,
+	},
+	.ep[3] = {
+		.ep = {
+			.name	= "ep1out-bulk",
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.is_pingpong	= 1,
+		.maxpacket	= 64,
+		.index		= 1 << 1,
+		.int_mask	= 1 << 12,
+	},
+	.ep[4] = {
+		.ep = {
+			.name	= "ep1in-bulk",
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.is_pingpong	= 1,
+		.maxpacket	= 64,
+		.index		= 1 << 1 | 1,
+		.int_mask	= 1 << 13,
+	},
+	.ep[5] = {
+		.ep = {
+			.name	= "ep2out-bulk",
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.is_pingpong	= 1,
+		.maxpacket	= 64,
+		.index		= 2 << 1,
+		.int_mask	= 1 << 14,
+	},
+	.ep[6] = {
+		.ep = {
+			.name	= "ep2in-bulk",
+			.ops	= &isp1760_ep_ops,
+		},
+		.udc		= &controller,
+		.is_pingpong	= 1,
+		.maxpacket	= 64,
+		.index		= 1 << 2 | 1,
+		.int_mask	= 1 << 15,
+	},
+
+
+};
+
+static int __init isp1760_udc_probe(struct platform_device *pdev)
+{
+	struct device	*dev = &pdev->dev;
+	struct resource	*res;
+	struct isp1760_udc *udc;
+	int retval;
+
+	if (pdev->num_resources != 2) {
+		printk("isp1760-udc: invalid num_resources\n");
+		return -ENODEV;
+	}
+	if ((pdev->resource[0].flags != IORESOURCE_MEM)
+			|| (pdev->resource[1].flags != IORESOURCE_IRQ)) {
+		printk("isp1760-udc: invalid resource type\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	if (!request_mem_region(res->start,
+			res->end - res->start + 1,
+			driver_name)) {
+		printk("isp1760-udc: someone's using UDC memory\n");
+		return -EBUSY;
+	}
+
+
+
+	udc = &controller;
+	udc->gadget.dev.parent = dev;
+	udc->pdev = pdev;
+
+	udc->regs = ioremap(res->start, res->end - res->start + 1);
+	if (!udc->regs) {
+		retval = -ENOMEM;
+		goto fail0;
+	}
+
+	udc_reinit(udc);
+	udc_init_regs(udc);
+	retval = device_register(&udc->gadget.dev);
+	if (retval < 0)
+		goto fail1;
+
+	udc->irq = platform_get_irq(pdev, 0);
+	retval = request_irq(udc->irq, isp1760_udc_irq,
+			IRQF_SHARED | IRQF_TRIGGER_MASK, driver_name, udc);
+	if (retval < 0) {
+		printk("isp1760-udc: request irq %d failed\n", udc->irq);
+		goto fail2;
+	}
+
+	return 0;
+
+fail2:
+	device_unregister(&udc->gadget.dev);
+fail1:
+	iounmap(udc->regs);
+fail0:
+	release_mem_region(res->start, res->end - res->start + 1);
+	printk("isp1760-udc: %s probe failed, %d\n", driver_name, retval);
+	return retval;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct isp1760_udc *udc = &controller;
+	int retval;
+
+	if (!driver
+			|| driver->speed < USB_SPEED_FULL
+			|| !driver->bind
+			|| !driver->setup) {
+		printk("isp1760-udc: bad parameter.\n");
+		return -EINVAL;
+	}
+
+	if (udc->driver) {
+		printk("isp1760-udc: UDC already has a gadget driver\n");
+		return -EBUSY;
+	}
+
+	udc->driver = driver;
+	udc->gadget.dev.driver = &driver->driver;
+	dev_set_drvdata(&udc->gadget.dev, &driver->driver);
+
+	retval = driver->bind(&udc->gadget);
+	if (retval) {
+		printk("isp1760-udc: driver->bind() returned %d\n", retval);
+		udc->driver = NULL;
+		udc->gadget.dev.driver = NULL;
+		dev_set_drvdata(&udc->gadget.dev, NULL);
+		return retval;
+	}
+
+	printk("isp1760-udc: bound to %s\n", driver->driver.name);
+	return 0;
+
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct isp1760_udc *udc = &controller;
+
+	if (!driver || driver != udc->driver || !driver->unbind)
+		return -EINVAL;
+
+	driver->unbind(&udc->gadget);
+	udc->gadget.dev.driver = NULL;
+	dev_set_drvdata(&udc->gadget.dev, NULL);
+	udc->driver = NULL;
+
+	printk("isp1760-udc: unbound from %s\n", driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static struct platform_driver isp1760_udc_driver = {
+	.remove		= __exit_p(isp1760_udc_remove),
+	.driver		= {
+		.name	= (char *) driver_name,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init udc_init_module(void)
+{
+	return platform_driver_probe(&isp1760_udc_driver, isp1760_udc_probe);
+}
+module_init(udc_init_module);
+
+static void __exit udc_exit_module(void)
+{
+	platform_driver_unregister(&isp1760_udc_driver);
+}
+module_exit(udc_exit_module);
+
+
+MODULE_DESCRIPTION("Driver for the ISP1760 USB device controller from
NXP");
+MODULE_AUTHOR("Siddarth Gore <gores at marvell.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:isp1760-udc");
diff --git a/drivers/usb/gadget/isp1760-udc.h
b/drivers/usb/gadget/isp1760-udc.h
new file mode 100644
index 0000000..91b8db3
--- /dev/null
+++ b/drivers/usb/gadget/isp1760-udc.h
@@ -0,0 +1,63 @@
+#ifndef ISP1760_UDC_H
+#define ISP1760_UDC_H
+
+#define DC_EP_MAX_PKT_SIZE_REG	0x0204
+#define DC_EP_TYPE_REG		0x0208
+#define DC_MODE_REG		0x020c
+#define DC_INT_CFG_REG		0x0210
+#define DC_INT_EN_REG		0x0214
+#define DC_INT_REG		0x0218
+#define DC_BUF_LEN_REG		0x021c
+#define DC_BUF_STATUS_REG	0x021e
+#define DC_DATA_PORT_REG	0x0220
+#define DC_CTRL_FUNC_REG	0x0228
+#define DC_EP_INDEX_REG		0x022c
+#define DC_DMA_CFG_REG		0x0238
+#define DC_DMA_HW_REG		0x023c
+#define DC_DMA_INT_EN_REG	0x0254
+#define DC_DMA_BURST_CNT_REG	0x0264
+#define DC_FRM_NUM_REG		0x0274
+#define DC_SCRATCH_REG		0x0278
+#define DC_UNLOCK_DEV_REG	0x027c
+
+#define	NUM_ENDPOINTS	7
+
+struct isp1760_ep {
+	struct usb_ep			ep;
+	struct list_head		queue;
+	struct isp1760_udc		*udc;
+
+	unsigned			maxpacket:16;
+	u8				index;
+	u32				int_mask;
+	unsigned			is_pingpong:1;
+
+	unsigned			stopped:1;
+	unsigned			is_in:1;
+	unsigned			is_iso:1;
+	unsigned			fifo_bank:1;
+
+	const struct usb_endpoint_descriptor
+					*desc;
+};
+
+
+struct isp1760_udc {
+	struct usb_gadget		gadget;
+	struct isp1760_ep		ep[NUM_ENDPOINTS];
+	struct usb_gadget_driver	*driver;
+	void __iomem			*regs;
+	struct platform_device		*pdev;
+	int				irq;
+};
+
+static inline struct isp1760_udc *to_udc(struct usb_gadget *g)
+{
+	return container_of(g, struct isp1760_udc, gadget);
+}
+
+struct isp1760_request {
+	struct usb_request		req;
+	struct list_head		queue;
+};
+#endif
-- 
1.6.0.3





More information about the linux-arm-kernel mailing list