From mboxrd@z Thu Jan 1 00:00:00 1970 From: gores@marvell.com (Siddarth Gore) Date: Fri, 16 Apr 2010 15:55:06 +0530 Subject: usb device controller driver for port 1 of isp1760 Message-ID: <1271413506.9554.39.camel@pe-dt434> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 "); +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