15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 290fccb52SAndrzej Pietrasiewicz /* 390fccb52SAndrzej Pietrasiewicz * omap_udc.c -- for OMAP full speed udc; most chips support OTG. 490fccb52SAndrzej Pietrasiewicz * 590fccb52SAndrzej Pietrasiewicz * Copyright (C) 2004 Texas Instruments, Inc. 690fccb52SAndrzej Pietrasiewicz * Copyright (C) 2004-2005 David Brownell 790fccb52SAndrzej Pietrasiewicz * 890fccb52SAndrzej Pietrasiewicz * OMAP2 & DMA support by Kyungmin Park <kyungmin.park@samsung.com> 990fccb52SAndrzej Pietrasiewicz */ 1090fccb52SAndrzej Pietrasiewicz 1190fccb52SAndrzej Pietrasiewicz #undef DEBUG 1290fccb52SAndrzej Pietrasiewicz #undef VERBOSE 1390fccb52SAndrzej Pietrasiewicz 1490fccb52SAndrzej Pietrasiewicz #include <linux/module.h> 1590fccb52SAndrzej Pietrasiewicz #include <linux/kernel.h> 1690fccb52SAndrzej Pietrasiewicz #include <linux/ioport.h> 1790fccb52SAndrzej Pietrasiewicz #include <linux/types.h> 1890fccb52SAndrzej Pietrasiewicz #include <linux/errno.h> 1990fccb52SAndrzej Pietrasiewicz #include <linux/delay.h> 2090fccb52SAndrzej Pietrasiewicz #include <linux/slab.h> 2190fccb52SAndrzej Pietrasiewicz #include <linux/timer.h> 2290fccb52SAndrzej Pietrasiewicz #include <linux/list.h> 2390fccb52SAndrzej Pietrasiewicz #include <linux/interrupt.h> 2490fccb52SAndrzej Pietrasiewicz #include <linux/proc_fs.h> 2590fccb52SAndrzej Pietrasiewicz #include <linux/mm.h> 2690fccb52SAndrzej Pietrasiewicz #include <linux/moduleparam.h> 2790fccb52SAndrzej Pietrasiewicz #include <linux/platform_device.h> 2890fccb52SAndrzej Pietrasiewicz #include <linux/usb/ch9.h> 2990fccb52SAndrzej Pietrasiewicz #include <linux/usb/gadget.h> 3090fccb52SAndrzej Pietrasiewicz #include <linux/usb/otg.h> 3190fccb52SAndrzej Pietrasiewicz #include <linux/dma-mapping.h> 3290fccb52SAndrzej Pietrasiewicz #include <linux/clk.h> 3390fccb52SAndrzej Pietrasiewicz #include <linux/err.h> 3490fccb52SAndrzej Pietrasiewicz #include <linux/prefetch.h> 3590fccb52SAndrzej Pietrasiewicz #include <linux/io.h> 3690fccb52SAndrzej Pietrasiewicz 3790fccb52SAndrzej Pietrasiewicz #include <asm/byteorder.h> 3890fccb52SAndrzej Pietrasiewicz #include <asm/irq.h> 3990fccb52SAndrzej Pietrasiewicz #include <asm/unaligned.h> 4090fccb52SAndrzej Pietrasiewicz #include <asm/mach-types.h> 4190fccb52SAndrzej Pietrasiewicz 4290fccb52SAndrzej Pietrasiewicz #include <linux/omap-dma.h> 43e8e77e97SArnd Bergmann #include <linux/platform_data/usb-omap1.h> 4490fccb52SAndrzej Pietrasiewicz 45e8e77e97SArnd Bergmann #include <linux/soc/ti/omap1-usb.h> 4611e00292SArnd Bergmann #include <linux/soc/ti/omap1-soc.h> 4711e00292SArnd Bergmann #include <linux/soc/ti/omap1-io.h> 4890fccb52SAndrzej Pietrasiewicz 4990fccb52SAndrzej Pietrasiewicz #include "omap_udc.h" 5090fccb52SAndrzej Pietrasiewicz 5190fccb52SAndrzej Pietrasiewicz #undef USB_TRACE 5290fccb52SAndrzej Pietrasiewicz 5390fccb52SAndrzej Pietrasiewicz /* bulk DMA seems to be behaving for both IN and OUT */ 5490fccb52SAndrzej Pietrasiewicz #define USE_DMA 5590fccb52SAndrzej Pietrasiewicz 5690fccb52SAndrzej Pietrasiewicz /* ISO too */ 5790fccb52SAndrzej Pietrasiewicz #define USE_ISO 5890fccb52SAndrzej Pietrasiewicz 5990fccb52SAndrzej Pietrasiewicz #define DRIVER_DESC "OMAP UDC driver" 6090fccb52SAndrzej Pietrasiewicz #define DRIVER_VERSION "4 October 2004" 6190fccb52SAndrzej Pietrasiewicz 6290fccb52SAndrzej Pietrasiewicz #define OMAP_DMA_USB_W2FC_TX0 29 6390fccb52SAndrzej Pietrasiewicz #define OMAP_DMA_USB_W2FC_RX0 26 6490fccb52SAndrzej Pietrasiewicz 6590fccb52SAndrzej Pietrasiewicz /* 6690fccb52SAndrzej Pietrasiewicz * The OMAP UDC needs _very_ early endpoint setup: before enabling the 6790fccb52SAndrzej Pietrasiewicz * D+ pullup to allow enumeration. That's too early for the gadget 6890fccb52SAndrzej Pietrasiewicz * framework to use from usb_endpoint_enable(), which happens after 6990fccb52SAndrzej Pietrasiewicz * enumeration as part of activating an interface. (But if we add an 7090fccb52SAndrzej Pietrasiewicz * optional new "UDC not yet running" state to the gadget driver model, 7190fccb52SAndrzej Pietrasiewicz * even just during driver binding, the endpoint autoconfig logic is the 7290fccb52SAndrzej Pietrasiewicz * natural spot to manufacture new endpoints.) 7390fccb52SAndrzej Pietrasiewicz * 7490fccb52SAndrzej Pietrasiewicz * So instead of using endpoint enable calls to control the hardware setup, 7590fccb52SAndrzej Pietrasiewicz * this driver defines a "fifo mode" parameter. It's used during driver 7690fccb52SAndrzej Pietrasiewicz * initialization to choose among a set of pre-defined endpoint configs. 7790fccb52SAndrzej Pietrasiewicz * See omap_udc_setup() for available modes, or to add others. That code 7890fccb52SAndrzej Pietrasiewicz * lives in an init section, so use this driver as a module if you need 7990fccb52SAndrzej Pietrasiewicz * to change the fifo mode after the kernel boots. 8090fccb52SAndrzej Pietrasiewicz * 8190fccb52SAndrzej Pietrasiewicz * Gadget drivers normally ignore endpoints they don't care about, and 8290fccb52SAndrzej Pietrasiewicz * won't include them in configuration descriptors. That means only 8390fccb52SAndrzej Pietrasiewicz * misbehaving hosts would even notice they exist. 8490fccb52SAndrzej Pietrasiewicz */ 8590fccb52SAndrzej Pietrasiewicz #ifdef USE_ISO 8690fccb52SAndrzej Pietrasiewicz static unsigned fifo_mode = 3; 8790fccb52SAndrzej Pietrasiewicz #else 8890fccb52SAndrzej Pietrasiewicz static unsigned fifo_mode; 8990fccb52SAndrzej Pietrasiewicz #endif 9090fccb52SAndrzej Pietrasiewicz 9190fccb52SAndrzej Pietrasiewicz /* "modprobe omap_udc fifo_mode=42", or else as a kernel 9290fccb52SAndrzej Pietrasiewicz * boot parameter "omap_udc:fifo_mode=42" 9390fccb52SAndrzej Pietrasiewicz */ 9490fccb52SAndrzej Pietrasiewicz module_param(fifo_mode, uint, 0); 9590fccb52SAndrzej Pietrasiewicz MODULE_PARM_DESC(fifo_mode, "endpoint configuration"); 9690fccb52SAndrzej Pietrasiewicz 9790fccb52SAndrzej Pietrasiewicz #ifdef USE_DMA 9890fccb52SAndrzej Pietrasiewicz static bool use_dma = 1; 9990fccb52SAndrzej Pietrasiewicz 10090fccb52SAndrzej Pietrasiewicz /* "modprobe omap_udc use_dma=y", or else as a kernel 10190fccb52SAndrzej Pietrasiewicz * boot parameter "omap_udc:use_dma=y" 10290fccb52SAndrzej Pietrasiewicz */ 10390fccb52SAndrzej Pietrasiewicz module_param(use_dma, bool, 0); 10490fccb52SAndrzej Pietrasiewicz MODULE_PARM_DESC(use_dma, "enable/disable DMA"); 10590fccb52SAndrzej Pietrasiewicz #else /* !USE_DMA */ 10690fccb52SAndrzej Pietrasiewicz 10790fccb52SAndrzej Pietrasiewicz /* save a bit of code */ 10890fccb52SAndrzej Pietrasiewicz #define use_dma 0 10990fccb52SAndrzej Pietrasiewicz #endif /* !USE_DMA */ 11090fccb52SAndrzej Pietrasiewicz 11190fccb52SAndrzej Pietrasiewicz 11290fccb52SAndrzej Pietrasiewicz static const char driver_name[] = "omap_udc"; 11390fccb52SAndrzej Pietrasiewicz static const char driver_desc[] = DRIVER_DESC; 11490fccb52SAndrzej Pietrasiewicz 11590fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 11690fccb52SAndrzej Pietrasiewicz 11790fccb52SAndrzej Pietrasiewicz /* there's a notion of "current endpoint" for modifying endpoint 11890fccb52SAndrzej Pietrasiewicz * state, and PIO access to its FIFO. 11990fccb52SAndrzej Pietrasiewicz */ 12090fccb52SAndrzej Pietrasiewicz 12190fccb52SAndrzej Pietrasiewicz static void use_ep(struct omap_ep *ep, u16 select) 12290fccb52SAndrzej Pietrasiewicz { 12390fccb52SAndrzej Pietrasiewicz u16 num = ep->bEndpointAddress & 0x0f; 12490fccb52SAndrzej Pietrasiewicz 12590fccb52SAndrzej Pietrasiewicz if (ep->bEndpointAddress & USB_DIR_IN) 12690fccb52SAndrzej Pietrasiewicz num |= UDC_EP_DIR; 12790fccb52SAndrzej Pietrasiewicz omap_writew(num | select, UDC_EP_NUM); 12890fccb52SAndrzej Pietrasiewicz /* when select, MUST deselect later !! */ 12990fccb52SAndrzej Pietrasiewicz } 13090fccb52SAndrzej Pietrasiewicz 13190fccb52SAndrzej Pietrasiewicz static inline void deselect_ep(void) 13290fccb52SAndrzej Pietrasiewicz { 13390fccb52SAndrzej Pietrasiewicz u16 w; 13490fccb52SAndrzej Pietrasiewicz 13590fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_EP_NUM); 13690fccb52SAndrzej Pietrasiewicz w &= ~UDC_EP_SEL; 13790fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_EP_NUM); 13890fccb52SAndrzej Pietrasiewicz /* 6 wait states before TX will happen */ 13990fccb52SAndrzej Pietrasiewicz } 14090fccb52SAndrzej Pietrasiewicz 14190fccb52SAndrzej Pietrasiewicz static void dma_channel_claim(struct omap_ep *ep, unsigned preferred); 14290fccb52SAndrzej Pietrasiewicz 14390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 14490fccb52SAndrzej Pietrasiewicz 14590fccb52SAndrzej Pietrasiewicz static int omap_ep_enable(struct usb_ep *_ep, 14690fccb52SAndrzej Pietrasiewicz const struct usb_endpoint_descriptor *desc) 14790fccb52SAndrzej Pietrasiewicz { 14890fccb52SAndrzej Pietrasiewicz struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); 14990fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 15090fccb52SAndrzej Pietrasiewicz unsigned long flags; 15190fccb52SAndrzej Pietrasiewicz u16 maxp; 15290fccb52SAndrzej Pietrasiewicz 15390fccb52SAndrzej Pietrasiewicz /* catch various bogus parameters */ 15490fccb52SAndrzej Pietrasiewicz if (!_ep || !desc 15590fccb52SAndrzej Pietrasiewicz || desc->bDescriptorType != USB_DT_ENDPOINT 15690fccb52SAndrzej Pietrasiewicz || ep->bEndpointAddress != desc->bEndpointAddress 15790fccb52SAndrzej Pietrasiewicz || ep->maxpacket < usb_endpoint_maxp(desc)) { 15890fccb52SAndrzej Pietrasiewicz DBG("%s, bad ep or descriptor\n", __func__); 15990fccb52SAndrzej Pietrasiewicz return -EINVAL; 16090fccb52SAndrzej Pietrasiewicz } 16190fccb52SAndrzej Pietrasiewicz maxp = usb_endpoint_maxp(desc); 16290fccb52SAndrzej Pietrasiewicz if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK 16390fccb52SAndrzej Pietrasiewicz && maxp != ep->maxpacket) 16490fccb52SAndrzej Pietrasiewicz || usb_endpoint_maxp(desc) > ep->maxpacket 16590fccb52SAndrzej Pietrasiewicz || !desc->wMaxPacketSize) { 16690fccb52SAndrzej Pietrasiewicz DBG("%s, bad %s maxpacket\n", __func__, _ep->name); 16790fccb52SAndrzej Pietrasiewicz return -ERANGE; 16890fccb52SAndrzej Pietrasiewicz } 16990fccb52SAndrzej Pietrasiewicz 17090fccb52SAndrzej Pietrasiewicz #ifdef USE_ISO 17190fccb52SAndrzej Pietrasiewicz if ((desc->bmAttributes == USB_ENDPOINT_XFER_ISOC 17290fccb52SAndrzej Pietrasiewicz && desc->bInterval != 1)) { 17390fccb52SAndrzej Pietrasiewicz /* hardware wants period = 1; USB allows 2^(Interval-1) */ 17490fccb52SAndrzej Pietrasiewicz DBG("%s, unsupported ISO period %dms\n", _ep->name, 17590fccb52SAndrzej Pietrasiewicz 1 << (desc->bInterval - 1)); 17690fccb52SAndrzej Pietrasiewicz return -EDOM; 17790fccb52SAndrzej Pietrasiewicz } 17890fccb52SAndrzej Pietrasiewicz #else 17990fccb52SAndrzej Pietrasiewicz if (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { 18090fccb52SAndrzej Pietrasiewicz DBG("%s, ISO nyet\n", _ep->name); 18190fccb52SAndrzej Pietrasiewicz return -EDOM; 18290fccb52SAndrzej Pietrasiewicz } 18390fccb52SAndrzej Pietrasiewicz #endif 18490fccb52SAndrzej Pietrasiewicz 18590fccb52SAndrzej Pietrasiewicz /* xfer types must match, except that interrupt ~= bulk */ 18690fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes != desc->bmAttributes 18790fccb52SAndrzej Pietrasiewicz && ep->bmAttributes != USB_ENDPOINT_XFER_BULK 18890fccb52SAndrzej Pietrasiewicz && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { 18990fccb52SAndrzej Pietrasiewicz DBG("%s, %s type mismatch\n", __func__, _ep->name); 19090fccb52SAndrzej Pietrasiewicz return -EINVAL; 19190fccb52SAndrzej Pietrasiewicz } 19290fccb52SAndrzej Pietrasiewicz 19390fccb52SAndrzej Pietrasiewicz udc = ep->udc; 19490fccb52SAndrzej Pietrasiewicz if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { 19590fccb52SAndrzej Pietrasiewicz DBG("%s, bogus device state\n", __func__); 19690fccb52SAndrzej Pietrasiewicz return -ESHUTDOWN; 19790fccb52SAndrzej Pietrasiewicz } 19890fccb52SAndrzej Pietrasiewicz 19990fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 20090fccb52SAndrzej Pietrasiewicz 20190fccb52SAndrzej Pietrasiewicz ep->ep.desc = desc; 20290fccb52SAndrzej Pietrasiewicz ep->irqs = 0; 20390fccb52SAndrzej Pietrasiewicz ep->stopped = 0; 20490fccb52SAndrzej Pietrasiewicz ep->ep.maxpacket = maxp; 20590fccb52SAndrzej Pietrasiewicz 20690fccb52SAndrzej Pietrasiewicz /* set endpoint to initial state */ 20790fccb52SAndrzej Pietrasiewicz ep->dma_channel = 0; 20890fccb52SAndrzej Pietrasiewicz ep->has_dma = 0; 20990fccb52SAndrzej Pietrasiewicz ep->lch = -1; 21090fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 21190fccb52SAndrzej Pietrasiewicz omap_writew(udc->clr_halt, UDC_CTRL); 21290fccb52SAndrzej Pietrasiewicz ep->ackwait = 0; 21390fccb52SAndrzej Pietrasiewicz deselect_ep(); 21490fccb52SAndrzej Pietrasiewicz 21590fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) 21690fccb52SAndrzej Pietrasiewicz list_add(&ep->iso, &udc->iso); 21790fccb52SAndrzej Pietrasiewicz 21890fccb52SAndrzej Pietrasiewicz /* maybe assign a DMA channel to this endpoint */ 21990fccb52SAndrzej Pietrasiewicz if (use_dma && desc->bmAttributes == USB_ENDPOINT_XFER_BULK) 22090fccb52SAndrzej Pietrasiewicz /* FIXME ISO can dma, but prefers first channel */ 22190fccb52SAndrzej Pietrasiewicz dma_channel_claim(ep, 0); 22290fccb52SAndrzej Pietrasiewicz 22390fccb52SAndrzej Pietrasiewicz /* PIO OUT may RX packets */ 22490fccb52SAndrzej Pietrasiewicz if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC 22590fccb52SAndrzej Pietrasiewicz && !ep->has_dma 22690fccb52SAndrzej Pietrasiewicz && !(ep->bEndpointAddress & USB_DIR_IN)) { 22790fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 22890fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 22990fccb52SAndrzej Pietrasiewicz } 23090fccb52SAndrzej Pietrasiewicz 23190fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 23290fccb52SAndrzej Pietrasiewicz VDBG("%s enabled\n", _ep->name); 23390fccb52SAndrzej Pietrasiewicz return 0; 23490fccb52SAndrzej Pietrasiewicz } 23590fccb52SAndrzej Pietrasiewicz 23690fccb52SAndrzej Pietrasiewicz static void nuke(struct omap_ep *, int status); 23790fccb52SAndrzej Pietrasiewicz 23890fccb52SAndrzej Pietrasiewicz static int omap_ep_disable(struct usb_ep *_ep) 23990fccb52SAndrzej Pietrasiewicz { 24090fccb52SAndrzej Pietrasiewicz struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); 24190fccb52SAndrzej Pietrasiewicz unsigned long flags; 24290fccb52SAndrzej Pietrasiewicz 24390fccb52SAndrzej Pietrasiewicz if (!_ep || !ep->ep.desc) { 24490fccb52SAndrzej Pietrasiewicz DBG("%s, %s not enabled\n", __func__, 24590fccb52SAndrzej Pietrasiewicz _ep ? ep->ep.name : NULL); 24690fccb52SAndrzej Pietrasiewicz return -EINVAL; 24790fccb52SAndrzej Pietrasiewicz } 24890fccb52SAndrzej Pietrasiewicz 24990fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->udc->lock, flags); 25090fccb52SAndrzej Pietrasiewicz ep->ep.desc = NULL; 25190fccb52SAndrzej Pietrasiewicz nuke(ep, -ESHUTDOWN); 25290fccb52SAndrzej Pietrasiewicz ep->ep.maxpacket = ep->maxpacket; 25390fccb52SAndrzej Pietrasiewicz ep->has_dma = 0; 25490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_HALT, UDC_CTRL); 25590fccb52SAndrzej Pietrasiewicz list_del_init(&ep->iso); 25690fccb52SAndrzej Pietrasiewicz del_timer(&ep->timer); 25790fccb52SAndrzej Pietrasiewicz 25890fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 25990fccb52SAndrzej Pietrasiewicz 26090fccb52SAndrzej Pietrasiewicz VDBG("%s disabled\n", _ep->name); 26190fccb52SAndrzej Pietrasiewicz return 0; 26290fccb52SAndrzej Pietrasiewicz } 26390fccb52SAndrzej Pietrasiewicz 26490fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 26590fccb52SAndrzej Pietrasiewicz 26690fccb52SAndrzej Pietrasiewicz static struct usb_request * 26790fccb52SAndrzej Pietrasiewicz omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) 26890fccb52SAndrzej Pietrasiewicz { 26990fccb52SAndrzej Pietrasiewicz struct omap_req *req; 27090fccb52SAndrzej Pietrasiewicz 27190fccb52SAndrzej Pietrasiewicz req = kzalloc(sizeof(*req), gfp_flags); 27290fccb52SAndrzej Pietrasiewicz if (!req) 27390fccb52SAndrzej Pietrasiewicz return NULL; 27490fccb52SAndrzej Pietrasiewicz 27590fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&req->queue); 27690fccb52SAndrzej Pietrasiewicz 27790fccb52SAndrzej Pietrasiewicz return &req->req; 27890fccb52SAndrzej Pietrasiewicz } 27990fccb52SAndrzej Pietrasiewicz 28090fccb52SAndrzej Pietrasiewicz static void 28190fccb52SAndrzej Pietrasiewicz omap_free_request(struct usb_ep *ep, struct usb_request *_req) 28290fccb52SAndrzej Pietrasiewicz { 28390fccb52SAndrzej Pietrasiewicz struct omap_req *req = container_of(_req, struct omap_req, req); 28490fccb52SAndrzej Pietrasiewicz 28590fccb52SAndrzej Pietrasiewicz kfree(req); 28690fccb52SAndrzej Pietrasiewicz } 28790fccb52SAndrzej Pietrasiewicz 28890fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 28990fccb52SAndrzej Pietrasiewicz 29090fccb52SAndrzej Pietrasiewicz static void 29190fccb52SAndrzej Pietrasiewicz done(struct omap_ep *ep, struct omap_req *req, int status) 29290fccb52SAndrzej Pietrasiewicz { 29390fccb52SAndrzej Pietrasiewicz struct omap_udc *udc = ep->udc; 29490fccb52SAndrzej Pietrasiewicz unsigned stopped = ep->stopped; 29590fccb52SAndrzej Pietrasiewicz 29690fccb52SAndrzej Pietrasiewicz list_del_init(&req->queue); 29790fccb52SAndrzej Pietrasiewicz 29890fccb52SAndrzej Pietrasiewicz if (req->req.status == -EINPROGRESS) 29990fccb52SAndrzej Pietrasiewicz req->req.status = status; 30090fccb52SAndrzej Pietrasiewicz else 30190fccb52SAndrzej Pietrasiewicz status = req->req.status; 30290fccb52SAndrzej Pietrasiewicz 30390fccb52SAndrzej Pietrasiewicz if (use_dma && ep->has_dma) 30490fccb52SAndrzej Pietrasiewicz usb_gadget_unmap_request(&udc->gadget, &req->req, 30590fccb52SAndrzej Pietrasiewicz (ep->bEndpointAddress & USB_DIR_IN)); 30690fccb52SAndrzej Pietrasiewicz 30790fccb52SAndrzej Pietrasiewicz #ifndef USB_TRACE 30890fccb52SAndrzej Pietrasiewicz if (status && status != -ESHUTDOWN) 30990fccb52SAndrzej Pietrasiewicz #endif 31090fccb52SAndrzej Pietrasiewicz VDBG("complete %s req %p stat %d len %u/%u\n", 31190fccb52SAndrzej Pietrasiewicz ep->ep.name, &req->req, status, 31290fccb52SAndrzej Pietrasiewicz req->req.actual, req->req.length); 31390fccb52SAndrzej Pietrasiewicz 31490fccb52SAndrzej Pietrasiewicz /* don't modify queue heads during completion callback */ 31590fccb52SAndrzej Pietrasiewicz ep->stopped = 1; 31690fccb52SAndrzej Pietrasiewicz spin_unlock(&ep->udc->lock); 317304f7e5eSMichal Sojka usb_gadget_giveback_request(&ep->ep, &req->req); 31890fccb52SAndrzej Pietrasiewicz spin_lock(&ep->udc->lock); 31990fccb52SAndrzej Pietrasiewicz ep->stopped = stopped; 32090fccb52SAndrzej Pietrasiewicz } 32190fccb52SAndrzej Pietrasiewicz 32290fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 32390fccb52SAndrzej Pietrasiewicz 32490fccb52SAndrzej Pietrasiewicz #define UDC_FIFO_FULL (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL) 32590fccb52SAndrzej Pietrasiewicz #define UDC_FIFO_UNWRITABLE (UDC_EP_HALTED | UDC_FIFO_FULL) 32690fccb52SAndrzej Pietrasiewicz 32790fccb52SAndrzej Pietrasiewicz #define FIFO_EMPTY (UDC_NON_ISO_FIFO_EMPTY | UDC_ISO_FIFO_EMPTY) 32890fccb52SAndrzej Pietrasiewicz #define FIFO_UNREADABLE (UDC_EP_HALTED | FIFO_EMPTY) 32990fccb52SAndrzej Pietrasiewicz 33090fccb52SAndrzej Pietrasiewicz static inline int 33190fccb52SAndrzej Pietrasiewicz write_packet(u8 *buf, struct omap_req *req, unsigned max) 33290fccb52SAndrzej Pietrasiewicz { 33390fccb52SAndrzej Pietrasiewicz unsigned len; 33490fccb52SAndrzej Pietrasiewicz u16 *wp; 33590fccb52SAndrzej Pietrasiewicz 33690fccb52SAndrzej Pietrasiewicz len = min(req->req.length - req->req.actual, max); 33790fccb52SAndrzej Pietrasiewicz req->req.actual += len; 33890fccb52SAndrzej Pietrasiewicz 33990fccb52SAndrzej Pietrasiewicz max = len; 34090fccb52SAndrzej Pietrasiewicz if (likely((((int)buf) & 1) == 0)) { 34190fccb52SAndrzej Pietrasiewicz wp = (u16 *)buf; 34290fccb52SAndrzej Pietrasiewicz while (max >= 2) { 34390fccb52SAndrzej Pietrasiewicz omap_writew(*wp++, UDC_DATA); 34490fccb52SAndrzej Pietrasiewicz max -= 2; 34590fccb52SAndrzej Pietrasiewicz } 34690fccb52SAndrzej Pietrasiewicz buf = (u8 *)wp; 34790fccb52SAndrzej Pietrasiewicz } 34890fccb52SAndrzej Pietrasiewicz while (max--) 34990fccb52SAndrzej Pietrasiewicz omap_writeb(*buf++, UDC_DATA); 35090fccb52SAndrzej Pietrasiewicz return len; 35190fccb52SAndrzej Pietrasiewicz } 35290fccb52SAndrzej Pietrasiewicz 35390fccb52SAndrzej Pietrasiewicz /* FIXME change r/w fifo calling convention */ 35490fccb52SAndrzej Pietrasiewicz 35590fccb52SAndrzej Pietrasiewicz 35690fccb52SAndrzej Pietrasiewicz /* return: 0 = still running, 1 = completed, negative = errno */ 35790fccb52SAndrzej Pietrasiewicz static int write_fifo(struct omap_ep *ep, struct omap_req *req) 35890fccb52SAndrzej Pietrasiewicz { 35990fccb52SAndrzej Pietrasiewicz u8 *buf; 36090fccb52SAndrzej Pietrasiewicz unsigned count; 36190fccb52SAndrzej Pietrasiewicz int is_last; 36290fccb52SAndrzej Pietrasiewicz u16 ep_stat; 36390fccb52SAndrzej Pietrasiewicz 36490fccb52SAndrzej Pietrasiewicz buf = req->req.buf + req->req.actual; 36590fccb52SAndrzej Pietrasiewicz prefetch(buf); 36690fccb52SAndrzej Pietrasiewicz 36790fccb52SAndrzej Pietrasiewicz /* PIO-IN isn't double buffered except for iso */ 36890fccb52SAndrzej Pietrasiewicz ep_stat = omap_readw(UDC_STAT_FLG); 36990fccb52SAndrzej Pietrasiewicz if (ep_stat & UDC_FIFO_UNWRITABLE) 37090fccb52SAndrzej Pietrasiewicz return 0; 37190fccb52SAndrzej Pietrasiewicz 37290fccb52SAndrzej Pietrasiewicz count = ep->ep.maxpacket; 37390fccb52SAndrzej Pietrasiewicz count = write_packet(buf, req, count); 37490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 37590fccb52SAndrzej Pietrasiewicz ep->ackwait = 1; 37690fccb52SAndrzej Pietrasiewicz 37790fccb52SAndrzej Pietrasiewicz /* last packet is often short (sometimes a zlp) */ 37890fccb52SAndrzej Pietrasiewicz if (count != ep->ep.maxpacket) 37990fccb52SAndrzej Pietrasiewicz is_last = 1; 38090fccb52SAndrzej Pietrasiewicz else if (req->req.length == req->req.actual 38190fccb52SAndrzej Pietrasiewicz && !req->req.zero) 38290fccb52SAndrzej Pietrasiewicz is_last = 1; 38390fccb52SAndrzej Pietrasiewicz else 38490fccb52SAndrzej Pietrasiewicz is_last = 0; 38590fccb52SAndrzej Pietrasiewicz 38690fccb52SAndrzej Pietrasiewicz /* NOTE: requests complete when all IN data is in a 38790fccb52SAndrzej Pietrasiewicz * FIFO (or sometimes later, if a zlp was needed). 38890fccb52SAndrzej Pietrasiewicz * Use usb_ep_fifo_status() where needed. 38990fccb52SAndrzej Pietrasiewicz */ 39090fccb52SAndrzej Pietrasiewicz if (is_last) 39190fccb52SAndrzej Pietrasiewicz done(ep, req, 0); 39290fccb52SAndrzej Pietrasiewicz return is_last; 39390fccb52SAndrzej Pietrasiewicz } 39490fccb52SAndrzej Pietrasiewicz 39590fccb52SAndrzej Pietrasiewicz static inline int 39690fccb52SAndrzej Pietrasiewicz read_packet(u8 *buf, struct omap_req *req, unsigned avail) 39790fccb52SAndrzej Pietrasiewicz { 39890fccb52SAndrzej Pietrasiewicz unsigned len; 39990fccb52SAndrzej Pietrasiewicz u16 *wp; 40090fccb52SAndrzej Pietrasiewicz 40190fccb52SAndrzej Pietrasiewicz len = min(req->req.length - req->req.actual, avail); 40290fccb52SAndrzej Pietrasiewicz req->req.actual += len; 40390fccb52SAndrzej Pietrasiewicz avail = len; 40490fccb52SAndrzej Pietrasiewicz 40590fccb52SAndrzej Pietrasiewicz if (likely((((int)buf) & 1) == 0)) { 40690fccb52SAndrzej Pietrasiewicz wp = (u16 *)buf; 40790fccb52SAndrzej Pietrasiewicz while (avail >= 2) { 40890fccb52SAndrzej Pietrasiewicz *wp++ = omap_readw(UDC_DATA); 40990fccb52SAndrzej Pietrasiewicz avail -= 2; 41090fccb52SAndrzej Pietrasiewicz } 41190fccb52SAndrzej Pietrasiewicz buf = (u8 *)wp; 41290fccb52SAndrzej Pietrasiewicz } 41390fccb52SAndrzej Pietrasiewicz while (avail--) 41490fccb52SAndrzej Pietrasiewicz *buf++ = omap_readb(UDC_DATA); 41590fccb52SAndrzej Pietrasiewicz return len; 41690fccb52SAndrzej Pietrasiewicz } 41790fccb52SAndrzej Pietrasiewicz 41890fccb52SAndrzej Pietrasiewicz /* return: 0 = still running, 1 = queue empty, negative = errno */ 41990fccb52SAndrzej Pietrasiewicz static int read_fifo(struct omap_ep *ep, struct omap_req *req) 42090fccb52SAndrzej Pietrasiewicz { 42190fccb52SAndrzej Pietrasiewicz u8 *buf; 42290fccb52SAndrzej Pietrasiewicz unsigned count, avail; 42390fccb52SAndrzej Pietrasiewicz int is_last; 42490fccb52SAndrzej Pietrasiewicz 42590fccb52SAndrzej Pietrasiewicz buf = req->req.buf + req->req.actual; 42690fccb52SAndrzej Pietrasiewicz prefetchw(buf); 42790fccb52SAndrzej Pietrasiewicz 42890fccb52SAndrzej Pietrasiewicz for (;;) { 42990fccb52SAndrzej Pietrasiewicz u16 ep_stat = omap_readw(UDC_STAT_FLG); 43090fccb52SAndrzej Pietrasiewicz 43190fccb52SAndrzej Pietrasiewicz is_last = 0; 43290fccb52SAndrzej Pietrasiewicz if (ep_stat & FIFO_EMPTY) { 43390fccb52SAndrzej Pietrasiewicz if (!ep->double_buf) 43490fccb52SAndrzej Pietrasiewicz break; 43590fccb52SAndrzej Pietrasiewicz ep->fnf = 1; 43690fccb52SAndrzej Pietrasiewicz } 43790fccb52SAndrzej Pietrasiewicz if (ep_stat & UDC_EP_HALTED) 43890fccb52SAndrzej Pietrasiewicz break; 43990fccb52SAndrzej Pietrasiewicz 44090fccb52SAndrzej Pietrasiewicz if (ep_stat & UDC_FIFO_FULL) 44190fccb52SAndrzej Pietrasiewicz avail = ep->ep.maxpacket; 44290fccb52SAndrzej Pietrasiewicz else { 44390fccb52SAndrzej Pietrasiewicz avail = omap_readw(UDC_RXFSTAT); 44490fccb52SAndrzej Pietrasiewicz ep->fnf = ep->double_buf; 44590fccb52SAndrzej Pietrasiewicz } 44690fccb52SAndrzej Pietrasiewicz count = read_packet(buf, req, avail); 44790fccb52SAndrzej Pietrasiewicz 44890fccb52SAndrzej Pietrasiewicz /* partial packet reads may not be errors */ 44990fccb52SAndrzej Pietrasiewicz if (count < ep->ep.maxpacket) { 45090fccb52SAndrzej Pietrasiewicz is_last = 1; 45190fccb52SAndrzej Pietrasiewicz /* overflowed this request? flush extra data */ 45290fccb52SAndrzej Pietrasiewicz if (count != avail) { 45390fccb52SAndrzej Pietrasiewicz req->req.status = -EOVERFLOW; 45490fccb52SAndrzej Pietrasiewicz avail -= count; 45590fccb52SAndrzej Pietrasiewicz while (avail--) 45690fccb52SAndrzej Pietrasiewicz omap_readw(UDC_DATA); 45790fccb52SAndrzej Pietrasiewicz } 45890fccb52SAndrzej Pietrasiewicz } else if (req->req.length == req->req.actual) 45990fccb52SAndrzej Pietrasiewicz is_last = 1; 46090fccb52SAndrzej Pietrasiewicz else 46190fccb52SAndrzej Pietrasiewicz is_last = 0; 46290fccb52SAndrzej Pietrasiewicz 46390fccb52SAndrzej Pietrasiewicz if (!ep->bEndpointAddress) 46490fccb52SAndrzej Pietrasiewicz break; 46590fccb52SAndrzej Pietrasiewicz if (is_last) 46690fccb52SAndrzej Pietrasiewicz done(ep, req, 0); 46790fccb52SAndrzej Pietrasiewicz break; 46890fccb52SAndrzej Pietrasiewicz } 46990fccb52SAndrzej Pietrasiewicz return is_last; 47090fccb52SAndrzej Pietrasiewicz } 47190fccb52SAndrzej Pietrasiewicz 47290fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 47390fccb52SAndrzej Pietrasiewicz 47490fccb52SAndrzej Pietrasiewicz static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) 47590fccb52SAndrzej Pietrasiewicz { 47690fccb52SAndrzej Pietrasiewicz dma_addr_t end; 47790fccb52SAndrzej Pietrasiewicz 47890fccb52SAndrzej Pietrasiewicz /* IN-DMA needs this on fault/cancel paths, so 15xx misreports 47990fccb52SAndrzej Pietrasiewicz * the last transfer's bytecount by more than a FIFO's worth. 48090fccb52SAndrzej Pietrasiewicz */ 48190fccb52SAndrzej Pietrasiewicz if (cpu_is_omap15xx()) 48290fccb52SAndrzej Pietrasiewicz return 0; 48390fccb52SAndrzej Pietrasiewicz 48490fccb52SAndrzej Pietrasiewicz end = omap_get_dma_src_pos(ep->lch); 48590fccb52SAndrzej Pietrasiewicz if (end == ep->dma_counter) 48690fccb52SAndrzej Pietrasiewicz return 0; 48790fccb52SAndrzej Pietrasiewicz 48890fccb52SAndrzej Pietrasiewicz end |= start & (0xffff << 16); 48990fccb52SAndrzej Pietrasiewicz if (end < start) 49090fccb52SAndrzej Pietrasiewicz end += 0x10000; 49190fccb52SAndrzej Pietrasiewicz return end - start; 49290fccb52SAndrzej Pietrasiewicz } 49390fccb52SAndrzej Pietrasiewicz 49490fccb52SAndrzej Pietrasiewicz static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) 49590fccb52SAndrzej Pietrasiewicz { 49690fccb52SAndrzej Pietrasiewicz dma_addr_t end; 49790fccb52SAndrzej Pietrasiewicz 49890fccb52SAndrzej Pietrasiewicz end = omap_get_dma_dst_pos(ep->lch); 49990fccb52SAndrzej Pietrasiewicz if (end == ep->dma_counter) 50090fccb52SAndrzej Pietrasiewicz return 0; 50190fccb52SAndrzej Pietrasiewicz 50290fccb52SAndrzej Pietrasiewicz end |= start & (0xffff << 16); 50390fccb52SAndrzej Pietrasiewicz if (cpu_is_omap15xx()) 50490fccb52SAndrzej Pietrasiewicz end++; 50590fccb52SAndrzej Pietrasiewicz if (end < start) 50690fccb52SAndrzej Pietrasiewicz end += 0x10000; 50790fccb52SAndrzej Pietrasiewicz return end - start; 50890fccb52SAndrzej Pietrasiewicz } 50990fccb52SAndrzej Pietrasiewicz 51090fccb52SAndrzej Pietrasiewicz 51190fccb52SAndrzej Pietrasiewicz /* Each USB transfer request using DMA maps to one or more DMA transfers. 51290fccb52SAndrzej Pietrasiewicz * When DMA completion isn't request completion, the UDC continues with 51390fccb52SAndrzej Pietrasiewicz * the next DMA transfer for that USB transfer. 51490fccb52SAndrzej Pietrasiewicz */ 51590fccb52SAndrzej Pietrasiewicz 51690fccb52SAndrzej Pietrasiewicz static void next_in_dma(struct omap_ep *ep, struct omap_req *req) 51790fccb52SAndrzej Pietrasiewicz { 51890fccb52SAndrzej Pietrasiewicz u16 txdma_ctrl, w; 51990fccb52SAndrzej Pietrasiewicz unsigned length = req->req.length - req->req.actual; 52090fccb52SAndrzej Pietrasiewicz const int sync_mode = cpu_is_omap15xx() 52190fccb52SAndrzej Pietrasiewicz ? OMAP_DMA_SYNC_FRAME 52290fccb52SAndrzej Pietrasiewicz : OMAP_DMA_SYNC_ELEMENT; 52390fccb52SAndrzej Pietrasiewicz int dma_trigger = 0; 52490fccb52SAndrzej Pietrasiewicz 52590fccb52SAndrzej Pietrasiewicz /* measure length in either bytes or packets */ 52690fccb52SAndrzej Pietrasiewicz if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC) 52790fccb52SAndrzej Pietrasiewicz || (cpu_is_omap15xx() && length < ep->maxpacket)) { 52890fccb52SAndrzej Pietrasiewicz txdma_ctrl = UDC_TXN_EOT | length; 52990fccb52SAndrzej Pietrasiewicz omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, 53090fccb52SAndrzej Pietrasiewicz length, 1, sync_mode, dma_trigger, 0); 53190fccb52SAndrzej Pietrasiewicz } else { 53290fccb52SAndrzej Pietrasiewicz length = min(length / ep->maxpacket, 53390fccb52SAndrzej Pietrasiewicz (unsigned) UDC_TXN_TSC + 1); 53490fccb52SAndrzej Pietrasiewicz txdma_ctrl = length; 53590fccb52SAndrzej Pietrasiewicz omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, 53690fccb52SAndrzej Pietrasiewicz ep->ep.maxpacket >> 1, length, sync_mode, 53790fccb52SAndrzej Pietrasiewicz dma_trigger, 0); 53890fccb52SAndrzej Pietrasiewicz length *= ep->maxpacket; 53990fccb52SAndrzej Pietrasiewicz } 54090fccb52SAndrzej Pietrasiewicz omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, 54190fccb52SAndrzej Pietrasiewicz OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 54290fccb52SAndrzej Pietrasiewicz 0, 0); 54390fccb52SAndrzej Pietrasiewicz 54490fccb52SAndrzej Pietrasiewicz omap_start_dma(ep->lch); 54590fccb52SAndrzej Pietrasiewicz ep->dma_counter = omap_get_dma_src_pos(ep->lch); 54690fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_DMA_IRQ_EN); 54790fccb52SAndrzej Pietrasiewicz w |= UDC_TX_DONE_IE(ep->dma_channel); 54890fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_DMA_IRQ_EN); 54990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_TXN_START | txdma_ctrl, UDC_TXDMA(ep->dma_channel)); 55090fccb52SAndrzej Pietrasiewicz req->dma_bytes = length; 55190fccb52SAndrzej Pietrasiewicz } 55290fccb52SAndrzej Pietrasiewicz 55390fccb52SAndrzej Pietrasiewicz static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) 55490fccb52SAndrzej Pietrasiewicz { 55590fccb52SAndrzej Pietrasiewicz u16 w; 55690fccb52SAndrzej Pietrasiewicz 55790fccb52SAndrzej Pietrasiewicz if (status == 0) { 55890fccb52SAndrzej Pietrasiewicz req->req.actual += req->dma_bytes; 55990fccb52SAndrzej Pietrasiewicz 56090fccb52SAndrzej Pietrasiewicz /* return if this request needs to send data or zlp */ 56190fccb52SAndrzej Pietrasiewicz if (req->req.actual < req->req.length) 56290fccb52SAndrzej Pietrasiewicz return; 56390fccb52SAndrzej Pietrasiewicz if (req->req.zero 56490fccb52SAndrzej Pietrasiewicz && req->dma_bytes != 0 56590fccb52SAndrzej Pietrasiewicz && (req->req.actual % ep->maxpacket) == 0) 56690fccb52SAndrzej Pietrasiewicz return; 56790fccb52SAndrzej Pietrasiewicz } else 56890fccb52SAndrzej Pietrasiewicz req->req.actual += dma_src_len(ep, req->req.dma 56990fccb52SAndrzej Pietrasiewicz + req->req.actual); 57090fccb52SAndrzej Pietrasiewicz 57190fccb52SAndrzej Pietrasiewicz /* tx completion */ 57290fccb52SAndrzej Pietrasiewicz omap_stop_dma(ep->lch); 57390fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_DMA_IRQ_EN); 57490fccb52SAndrzej Pietrasiewicz w &= ~UDC_TX_DONE_IE(ep->dma_channel); 57590fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_DMA_IRQ_EN); 57690fccb52SAndrzej Pietrasiewicz done(ep, req, status); 57790fccb52SAndrzej Pietrasiewicz } 57890fccb52SAndrzej Pietrasiewicz 57990fccb52SAndrzej Pietrasiewicz static void next_out_dma(struct omap_ep *ep, struct omap_req *req) 58090fccb52SAndrzej Pietrasiewicz { 58190fccb52SAndrzej Pietrasiewicz unsigned packets = req->req.length - req->req.actual; 58290fccb52SAndrzej Pietrasiewicz int dma_trigger = 0; 58390fccb52SAndrzej Pietrasiewicz u16 w; 58490fccb52SAndrzej Pietrasiewicz 58590fccb52SAndrzej Pietrasiewicz /* set up this DMA transfer, enable the fifo, start */ 58690fccb52SAndrzej Pietrasiewicz packets /= ep->ep.maxpacket; 58790fccb52SAndrzej Pietrasiewicz packets = min(packets, (unsigned)UDC_RXN_TC + 1); 58890fccb52SAndrzej Pietrasiewicz req->dma_bytes = packets * ep->ep.maxpacket; 58990fccb52SAndrzej Pietrasiewicz omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, 59090fccb52SAndrzej Pietrasiewicz ep->ep.maxpacket >> 1, packets, 59190fccb52SAndrzej Pietrasiewicz OMAP_DMA_SYNC_ELEMENT, 59290fccb52SAndrzej Pietrasiewicz dma_trigger, 0); 59390fccb52SAndrzej Pietrasiewicz omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, 59490fccb52SAndrzej Pietrasiewicz OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 59590fccb52SAndrzej Pietrasiewicz 0, 0); 59690fccb52SAndrzej Pietrasiewicz ep->dma_counter = omap_get_dma_dst_pos(ep->lch); 59790fccb52SAndrzej Pietrasiewicz 59890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel)); 59990fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_DMA_IRQ_EN); 60090fccb52SAndrzej Pietrasiewicz w |= UDC_RX_EOT_IE(ep->dma_channel); 60190fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_DMA_IRQ_EN); 60290fccb52SAndrzej Pietrasiewicz omap_writew(ep->bEndpointAddress & 0xf, UDC_EP_NUM); 60390fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 60490fccb52SAndrzej Pietrasiewicz 60590fccb52SAndrzej Pietrasiewicz omap_start_dma(ep->lch); 60690fccb52SAndrzej Pietrasiewicz } 60790fccb52SAndrzej Pietrasiewicz 60890fccb52SAndrzej Pietrasiewicz static void 60990fccb52SAndrzej Pietrasiewicz finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one) 61090fccb52SAndrzej Pietrasiewicz { 61190fccb52SAndrzej Pietrasiewicz u16 count, w; 61290fccb52SAndrzej Pietrasiewicz 61390fccb52SAndrzej Pietrasiewicz if (status == 0) 61490fccb52SAndrzej Pietrasiewicz ep->dma_counter = (u16) (req->req.dma + req->req.actual); 61590fccb52SAndrzej Pietrasiewicz count = dma_dest_len(ep, req->req.dma + req->req.actual); 61690fccb52SAndrzej Pietrasiewicz count += req->req.actual; 61790fccb52SAndrzej Pietrasiewicz if (one) 61890fccb52SAndrzej Pietrasiewicz count--; 61990fccb52SAndrzej Pietrasiewicz if (count <= req->req.length) 62090fccb52SAndrzej Pietrasiewicz req->req.actual = count; 62190fccb52SAndrzej Pietrasiewicz 62290fccb52SAndrzej Pietrasiewicz if (count != req->dma_bytes || status) 62390fccb52SAndrzej Pietrasiewicz omap_stop_dma(ep->lch); 62490fccb52SAndrzej Pietrasiewicz 62590fccb52SAndrzej Pietrasiewicz /* if this wasn't short, request may need another transfer */ 62690fccb52SAndrzej Pietrasiewicz else if (req->req.actual < req->req.length) 62790fccb52SAndrzej Pietrasiewicz return; 62890fccb52SAndrzej Pietrasiewicz 62990fccb52SAndrzej Pietrasiewicz /* rx completion */ 63090fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_DMA_IRQ_EN); 63190fccb52SAndrzej Pietrasiewicz w &= ~UDC_RX_EOT_IE(ep->dma_channel); 63290fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_DMA_IRQ_EN); 63390fccb52SAndrzej Pietrasiewicz done(ep, req, status); 63490fccb52SAndrzej Pietrasiewicz } 63590fccb52SAndrzej Pietrasiewicz 63690fccb52SAndrzej Pietrasiewicz static void dma_irq(struct omap_udc *udc, u16 irq_src) 63790fccb52SAndrzej Pietrasiewicz { 63890fccb52SAndrzej Pietrasiewicz u16 dman_stat = omap_readw(UDC_DMAN_STAT); 63990fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 64090fccb52SAndrzej Pietrasiewicz struct omap_req *req; 64190fccb52SAndrzej Pietrasiewicz 64290fccb52SAndrzej Pietrasiewicz /* IN dma: tx to host */ 64390fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_TXN_DONE) { 64490fccb52SAndrzej Pietrasiewicz ep = &udc->ep[16 + UDC_DMA_TX_SRC(dman_stat)]; 64590fccb52SAndrzej Pietrasiewicz ep->irqs++; 64690fccb52SAndrzej Pietrasiewicz /* can see TXN_DONE after dma abort */ 64790fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 64890fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 64990fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 65090fccb52SAndrzej Pietrasiewicz finish_in_dma(ep, req, 0); 65190fccb52SAndrzej Pietrasiewicz } 65290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC); 65390fccb52SAndrzej Pietrasiewicz 65490fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 65590fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 65690fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 65790fccb52SAndrzej Pietrasiewicz next_in_dma(ep, req); 65890fccb52SAndrzej Pietrasiewicz } 65990fccb52SAndrzej Pietrasiewicz } 66090fccb52SAndrzej Pietrasiewicz 66190fccb52SAndrzej Pietrasiewicz /* OUT dma: rx from host */ 66290fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_RXN_EOT) { 66390fccb52SAndrzej Pietrasiewicz ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; 66490fccb52SAndrzej Pietrasiewicz ep->irqs++; 66590fccb52SAndrzej Pietrasiewicz /* can see RXN_EOT after dma abort */ 66690fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 66790fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 66890fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 66990fccb52SAndrzej Pietrasiewicz finish_out_dma(ep, req, 0, dman_stat & UDC_DMA_RX_SB); 67090fccb52SAndrzej Pietrasiewicz } 67190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC); 67290fccb52SAndrzej Pietrasiewicz 67390fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 67490fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 67590fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 67690fccb52SAndrzej Pietrasiewicz next_out_dma(ep, req); 67790fccb52SAndrzej Pietrasiewicz } 67890fccb52SAndrzej Pietrasiewicz } 67990fccb52SAndrzej Pietrasiewicz 68090fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_RXN_CNT) { 68190fccb52SAndrzej Pietrasiewicz ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; 68290fccb52SAndrzej Pietrasiewicz ep->irqs++; 68390fccb52SAndrzej Pietrasiewicz /* omap15xx does this unasked... */ 68490fccb52SAndrzej Pietrasiewicz VDBG("%s, RX_CNT irq?\n", ep->ep.name); 68590fccb52SAndrzej Pietrasiewicz omap_writew(UDC_RXN_CNT, UDC_IRQ_SRC); 68690fccb52SAndrzej Pietrasiewicz } 68790fccb52SAndrzej Pietrasiewicz } 68890fccb52SAndrzej Pietrasiewicz 68990fccb52SAndrzej Pietrasiewicz static void dma_error(int lch, u16 ch_status, void *data) 69090fccb52SAndrzej Pietrasiewicz { 69190fccb52SAndrzej Pietrasiewicz struct omap_ep *ep = data; 69290fccb52SAndrzej Pietrasiewicz 69390fccb52SAndrzej Pietrasiewicz /* if ch_status & OMAP_DMA_DROP_IRQ ... */ 69490fccb52SAndrzej Pietrasiewicz /* if ch_status & OMAP1_DMA_TOUT_IRQ ... */ 69590fccb52SAndrzej Pietrasiewicz ERR("%s dma error, lch %d status %02x\n", ep->ep.name, lch, ch_status); 69690fccb52SAndrzej Pietrasiewicz 69790fccb52SAndrzej Pietrasiewicz /* complete current transfer ... */ 69890fccb52SAndrzej Pietrasiewicz } 69990fccb52SAndrzej Pietrasiewicz 70090fccb52SAndrzej Pietrasiewicz static void dma_channel_claim(struct omap_ep *ep, unsigned channel) 70190fccb52SAndrzej Pietrasiewicz { 70290fccb52SAndrzej Pietrasiewicz u16 reg; 70390fccb52SAndrzej Pietrasiewicz int status, restart, is_in; 70490fccb52SAndrzej Pietrasiewicz int dma_channel; 70590fccb52SAndrzej Pietrasiewicz 70690fccb52SAndrzej Pietrasiewicz is_in = ep->bEndpointAddress & USB_DIR_IN; 70790fccb52SAndrzej Pietrasiewicz if (is_in) 70890fccb52SAndrzej Pietrasiewicz reg = omap_readw(UDC_TXDMA_CFG); 70990fccb52SAndrzej Pietrasiewicz else 71090fccb52SAndrzej Pietrasiewicz reg = omap_readw(UDC_RXDMA_CFG); 71190fccb52SAndrzej Pietrasiewicz reg |= UDC_DMA_REQ; /* "pulse" activated */ 71290fccb52SAndrzej Pietrasiewicz 71390fccb52SAndrzej Pietrasiewicz ep->dma_channel = 0; 71490fccb52SAndrzej Pietrasiewicz ep->lch = -1; 71590fccb52SAndrzej Pietrasiewicz if (channel == 0 || channel > 3) { 71690fccb52SAndrzej Pietrasiewicz if ((reg & 0x0f00) == 0) 71790fccb52SAndrzej Pietrasiewicz channel = 3; 71890fccb52SAndrzej Pietrasiewicz else if ((reg & 0x00f0) == 0) 71990fccb52SAndrzej Pietrasiewicz channel = 2; 72090fccb52SAndrzej Pietrasiewicz else if ((reg & 0x000f) == 0) /* preferred for ISO */ 72190fccb52SAndrzej Pietrasiewicz channel = 1; 72290fccb52SAndrzej Pietrasiewicz else { 72390fccb52SAndrzej Pietrasiewicz status = -EMLINK; 72490fccb52SAndrzej Pietrasiewicz goto just_restart; 72590fccb52SAndrzej Pietrasiewicz } 72690fccb52SAndrzej Pietrasiewicz } 72790fccb52SAndrzej Pietrasiewicz reg |= (0x0f & ep->bEndpointAddress) << (4 * (channel - 1)); 72890fccb52SAndrzej Pietrasiewicz ep->dma_channel = channel; 72990fccb52SAndrzej Pietrasiewicz 73090fccb52SAndrzej Pietrasiewicz if (is_in) { 73190fccb52SAndrzej Pietrasiewicz dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel; 73290fccb52SAndrzej Pietrasiewicz status = omap_request_dma(dma_channel, 73390fccb52SAndrzej Pietrasiewicz ep->ep.name, dma_error, ep, &ep->lch); 73490fccb52SAndrzej Pietrasiewicz if (status == 0) { 73590fccb52SAndrzej Pietrasiewicz omap_writew(reg, UDC_TXDMA_CFG); 73690fccb52SAndrzej Pietrasiewicz /* EMIFF or SDRC */ 73790fccb52SAndrzej Pietrasiewicz omap_set_dma_src_burst_mode(ep->lch, 73890fccb52SAndrzej Pietrasiewicz OMAP_DMA_DATA_BURST_4); 73990fccb52SAndrzej Pietrasiewicz omap_set_dma_src_data_pack(ep->lch, 1); 74090fccb52SAndrzej Pietrasiewicz /* TIPB */ 74190fccb52SAndrzej Pietrasiewicz omap_set_dma_dest_params(ep->lch, 74290fccb52SAndrzej Pietrasiewicz OMAP_DMA_PORT_TIPB, 74390fccb52SAndrzej Pietrasiewicz OMAP_DMA_AMODE_CONSTANT, 74490fccb52SAndrzej Pietrasiewicz UDC_DATA_DMA, 74590fccb52SAndrzej Pietrasiewicz 0, 0); 74690fccb52SAndrzej Pietrasiewicz } 74790fccb52SAndrzej Pietrasiewicz } else { 74890fccb52SAndrzej Pietrasiewicz dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel; 74990fccb52SAndrzej Pietrasiewicz status = omap_request_dma(dma_channel, 75090fccb52SAndrzej Pietrasiewicz ep->ep.name, dma_error, ep, &ep->lch); 75190fccb52SAndrzej Pietrasiewicz if (status == 0) { 75290fccb52SAndrzej Pietrasiewicz omap_writew(reg, UDC_RXDMA_CFG); 75390fccb52SAndrzej Pietrasiewicz /* TIPB */ 75490fccb52SAndrzej Pietrasiewicz omap_set_dma_src_params(ep->lch, 75590fccb52SAndrzej Pietrasiewicz OMAP_DMA_PORT_TIPB, 75690fccb52SAndrzej Pietrasiewicz OMAP_DMA_AMODE_CONSTANT, 75790fccb52SAndrzej Pietrasiewicz UDC_DATA_DMA, 75890fccb52SAndrzej Pietrasiewicz 0, 0); 75990fccb52SAndrzej Pietrasiewicz /* EMIFF or SDRC */ 76090fccb52SAndrzej Pietrasiewicz omap_set_dma_dest_burst_mode(ep->lch, 76190fccb52SAndrzej Pietrasiewicz OMAP_DMA_DATA_BURST_4); 76290fccb52SAndrzej Pietrasiewicz omap_set_dma_dest_data_pack(ep->lch, 1); 76390fccb52SAndrzej Pietrasiewicz } 76490fccb52SAndrzej Pietrasiewicz } 76590fccb52SAndrzej Pietrasiewicz if (status) 76690fccb52SAndrzej Pietrasiewicz ep->dma_channel = 0; 76790fccb52SAndrzej Pietrasiewicz else { 76890fccb52SAndrzej Pietrasiewicz ep->has_dma = 1; 76990fccb52SAndrzej Pietrasiewicz omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); 77090fccb52SAndrzej Pietrasiewicz 77190fccb52SAndrzej Pietrasiewicz /* channel type P: hw synch (fifo) */ 77290fccb52SAndrzej Pietrasiewicz if (!cpu_is_omap15xx()) 77390fccb52SAndrzej Pietrasiewicz omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P); 77490fccb52SAndrzej Pietrasiewicz } 77590fccb52SAndrzej Pietrasiewicz 77690fccb52SAndrzej Pietrasiewicz just_restart: 77790fccb52SAndrzej Pietrasiewicz /* restart any queue, even if the claim failed */ 77890fccb52SAndrzej Pietrasiewicz restart = !ep->stopped && !list_empty(&ep->queue); 77990fccb52SAndrzej Pietrasiewicz 78090fccb52SAndrzej Pietrasiewicz if (status) 78190fccb52SAndrzej Pietrasiewicz DBG("%s no dma channel: %d%s\n", ep->ep.name, status, 78290fccb52SAndrzej Pietrasiewicz restart ? " (restart)" : ""); 78390fccb52SAndrzej Pietrasiewicz else 78490fccb52SAndrzej Pietrasiewicz DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name, 78590fccb52SAndrzej Pietrasiewicz is_in ? 't' : 'r', 78690fccb52SAndrzej Pietrasiewicz ep->dma_channel - 1, ep->lch, 78790fccb52SAndrzej Pietrasiewicz restart ? " (restart)" : ""); 78890fccb52SAndrzej Pietrasiewicz 78990fccb52SAndrzej Pietrasiewicz if (restart) { 79090fccb52SAndrzej Pietrasiewicz struct omap_req *req; 79190fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, struct omap_req, queue); 79290fccb52SAndrzej Pietrasiewicz if (ep->has_dma) 79390fccb52SAndrzej Pietrasiewicz (is_in ? next_in_dma : next_out_dma)(ep, req); 79490fccb52SAndrzej Pietrasiewicz else { 79590fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 79690fccb52SAndrzej Pietrasiewicz (is_in ? write_fifo : read_fifo)(ep, req); 79790fccb52SAndrzej Pietrasiewicz deselect_ep(); 79890fccb52SAndrzej Pietrasiewicz if (!is_in) { 79990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 80090fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 80190fccb52SAndrzej Pietrasiewicz } 80290fccb52SAndrzej Pietrasiewicz /* IN: 6 wait states before it'll tx */ 80390fccb52SAndrzej Pietrasiewicz } 80490fccb52SAndrzej Pietrasiewicz } 80590fccb52SAndrzej Pietrasiewicz } 80690fccb52SAndrzej Pietrasiewicz 80790fccb52SAndrzej Pietrasiewicz static void dma_channel_release(struct omap_ep *ep) 80890fccb52SAndrzej Pietrasiewicz { 80990fccb52SAndrzej Pietrasiewicz int shift = 4 * (ep->dma_channel - 1); 81090fccb52SAndrzej Pietrasiewicz u16 mask = 0x0f << shift; 81190fccb52SAndrzej Pietrasiewicz struct omap_req *req; 81290fccb52SAndrzej Pietrasiewicz int active; 81390fccb52SAndrzej Pietrasiewicz 81490fccb52SAndrzej Pietrasiewicz /* abort any active usb transfer request */ 81590fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) 81690fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, struct omap_req, queue); 81790fccb52SAndrzej Pietrasiewicz else 81890fccb52SAndrzej Pietrasiewicz req = NULL; 81990fccb52SAndrzej Pietrasiewicz 82090fccb52SAndrzej Pietrasiewicz active = omap_get_dma_active_status(ep->lch); 82190fccb52SAndrzej Pietrasiewicz 82290fccb52SAndrzej Pietrasiewicz DBG("%s release %s %cxdma%d %p\n", ep->ep.name, 82390fccb52SAndrzej Pietrasiewicz active ? "active" : "idle", 82490fccb52SAndrzej Pietrasiewicz (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', 82590fccb52SAndrzej Pietrasiewicz ep->dma_channel - 1, req); 82690fccb52SAndrzej Pietrasiewicz 82790fccb52SAndrzej Pietrasiewicz /* NOTE: re-setting RX_REQ/TX_REQ because of a chip bug (before 82890fccb52SAndrzej Pietrasiewicz * OMAP 1710 ES2.0) where reading the DMA_CFG can clear them. 82990fccb52SAndrzej Pietrasiewicz */ 83090fccb52SAndrzej Pietrasiewicz 83190fccb52SAndrzej Pietrasiewicz /* wait till current packet DMA finishes, and fifo empties */ 83290fccb52SAndrzej Pietrasiewicz if (ep->bEndpointAddress & USB_DIR_IN) { 83390fccb52SAndrzej Pietrasiewicz omap_writew((omap_readw(UDC_TXDMA_CFG) & ~mask) | UDC_DMA_REQ, 83490fccb52SAndrzej Pietrasiewicz UDC_TXDMA_CFG); 83590fccb52SAndrzej Pietrasiewicz 83690fccb52SAndrzej Pietrasiewicz if (req) { 83790fccb52SAndrzej Pietrasiewicz finish_in_dma(ep, req, -ECONNRESET); 83890fccb52SAndrzej Pietrasiewicz 83990fccb52SAndrzej Pietrasiewicz /* clear FIFO; hosts probably won't empty it */ 84090fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 84190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 84290fccb52SAndrzej Pietrasiewicz deselect_ep(); 84390fccb52SAndrzej Pietrasiewicz } 84490fccb52SAndrzej Pietrasiewicz while (omap_readw(UDC_TXDMA_CFG) & mask) 84590fccb52SAndrzej Pietrasiewicz udelay(10); 84690fccb52SAndrzej Pietrasiewicz } else { 84790fccb52SAndrzej Pietrasiewicz omap_writew((omap_readw(UDC_RXDMA_CFG) & ~mask) | UDC_DMA_REQ, 84890fccb52SAndrzej Pietrasiewicz UDC_RXDMA_CFG); 84990fccb52SAndrzej Pietrasiewicz 85090fccb52SAndrzej Pietrasiewicz /* dma empties the fifo */ 85190fccb52SAndrzej Pietrasiewicz while (omap_readw(UDC_RXDMA_CFG) & mask) 85290fccb52SAndrzej Pietrasiewicz udelay(10); 85390fccb52SAndrzej Pietrasiewicz if (req) 85490fccb52SAndrzej Pietrasiewicz finish_out_dma(ep, req, -ECONNRESET, 0); 85590fccb52SAndrzej Pietrasiewicz } 85690fccb52SAndrzej Pietrasiewicz omap_free_dma(ep->lch); 85790fccb52SAndrzej Pietrasiewicz ep->dma_channel = 0; 85890fccb52SAndrzej Pietrasiewicz ep->lch = -1; 85990fccb52SAndrzej Pietrasiewicz /* has_dma still set, till endpoint is fully quiesced */ 86090fccb52SAndrzej Pietrasiewicz } 86190fccb52SAndrzej Pietrasiewicz 86290fccb52SAndrzej Pietrasiewicz 86390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 86490fccb52SAndrzej Pietrasiewicz 86590fccb52SAndrzej Pietrasiewicz static int 86690fccb52SAndrzej Pietrasiewicz omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) 86790fccb52SAndrzej Pietrasiewicz { 86890fccb52SAndrzej Pietrasiewicz struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); 86990fccb52SAndrzej Pietrasiewicz struct omap_req *req = container_of(_req, struct omap_req, req); 87090fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 87190fccb52SAndrzej Pietrasiewicz unsigned long flags; 87290fccb52SAndrzej Pietrasiewicz int is_iso = 0; 87390fccb52SAndrzej Pietrasiewicz 87490fccb52SAndrzej Pietrasiewicz /* catch various bogus parameters */ 87590fccb52SAndrzej Pietrasiewicz if (!_req || !req->req.complete || !req->req.buf 87690fccb52SAndrzej Pietrasiewicz || !list_empty(&req->queue)) { 87790fccb52SAndrzej Pietrasiewicz DBG("%s, bad params\n", __func__); 87890fccb52SAndrzej Pietrasiewicz return -EINVAL; 87990fccb52SAndrzej Pietrasiewicz } 88090fccb52SAndrzej Pietrasiewicz if (!_ep || (!ep->ep.desc && ep->bEndpointAddress)) { 88190fccb52SAndrzej Pietrasiewicz DBG("%s, bad ep\n", __func__); 88290fccb52SAndrzej Pietrasiewicz return -EINVAL; 88390fccb52SAndrzej Pietrasiewicz } 88490fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { 88590fccb52SAndrzej Pietrasiewicz if (req->req.length > ep->ep.maxpacket) 88690fccb52SAndrzej Pietrasiewicz return -EMSGSIZE; 88790fccb52SAndrzej Pietrasiewicz is_iso = 1; 88890fccb52SAndrzej Pietrasiewicz } 88990fccb52SAndrzej Pietrasiewicz 89090fccb52SAndrzej Pietrasiewicz /* this isn't bogus, but OMAP DMA isn't the only hardware to 89190fccb52SAndrzej Pietrasiewicz * have a hard time with partial packet reads... reject it. 89290fccb52SAndrzej Pietrasiewicz */ 89390fccb52SAndrzej Pietrasiewicz if (use_dma 89490fccb52SAndrzej Pietrasiewicz && ep->has_dma 89590fccb52SAndrzej Pietrasiewicz && ep->bEndpointAddress != 0 89690fccb52SAndrzej Pietrasiewicz && (ep->bEndpointAddress & USB_DIR_IN) == 0 89790fccb52SAndrzej Pietrasiewicz && (req->req.length % ep->ep.maxpacket) != 0) { 89890fccb52SAndrzej Pietrasiewicz DBG("%s, no partial packet OUT reads\n", __func__); 89990fccb52SAndrzej Pietrasiewicz return -EMSGSIZE; 90090fccb52SAndrzej Pietrasiewicz } 90190fccb52SAndrzej Pietrasiewicz 90290fccb52SAndrzej Pietrasiewicz udc = ep->udc; 90390fccb52SAndrzej Pietrasiewicz if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) 90490fccb52SAndrzej Pietrasiewicz return -ESHUTDOWN; 90590fccb52SAndrzej Pietrasiewicz 90690fccb52SAndrzej Pietrasiewicz if (use_dma && ep->has_dma) 90790fccb52SAndrzej Pietrasiewicz usb_gadget_map_request(&udc->gadget, &req->req, 90890fccb52SAndrzej Pietrasiewicz (ep->bEndpointAddress & USB_DIR_IN)); 90990fccb52SAndrzej Pietrasiewicz 91090fccb52SAndrzej Pietrasiewicz VDBG("%s queue req %p, len %d buf %p\n", 91190fccb52SAndrzej Pietrasiewicz ep->ep.name, _req, _req->length, _req->buf); 91290fccb52SAndrzej Pietrasiewicz 91390fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 91490fccb52SAndrzej Pietrasiewicz 91590fccb52SAndrzej Pietrasiewicz req->req.status = -EINPROGRESS; 91690fccb52SAndrzej Pietrasiewicz req->req.actual = 0; 91790fccb52SAndrzej Pietrasiewicz 91890fccb52SAndrzej Pietrasiewicz /* maybe kickstart non-iso i/o queues */ 91990fccb52SAndrzej Pietrasiewicz if (is_iso) { 92090fccb52SAndrzej Pietrasiewicz u16 w; 92190fccb52SAndrzej Pietrasiewicz 92290fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_IRQ_EN); 92390fccb52SAndrzej Pietrasiewicz w |= UDC_SOF_IE; 92490fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_IRQ_EN); 92590fccb52SAndrzej Pietrasiewicz } else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { 92690fccb52SAndrzej Pietrasiewicz int is_in; 92790fccb52SAndrzej Pietrasiewicz 92890fccb52SAndrzej Pietrasiewicz if (ep->bEndpointAddress == 0) { 92990fccb52SAndrzej Pietrasiewicz if (!udc->ep0_pending || !list_empty(&ep->queue)) { 93090fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 93190fccb52SAndrzej Pietrasiewicz return -EL2HLT; 93290fccb52SAndrzej Pietrasiewicz } 93390fccb52SAndrzej Pietrasiewicz 93490fccb52SAndrzej Pietrasiewicz /* empty DATA stage? */ 93590fccb52SAndrzej Pietrasiewicz is_in = udc->ep0_in; 93690fccb52SAndrzej Pietrasiewicz if (!req->req.length) { 93790fccb52SAndrzej Pietrasiewicz 93890fccb52SAndrzej Pietrasiewicz /* chip became CONFIGURED or ADDRESSED 93990fccb52SAndrzej Pietrasiewicz * earlier; drivers may already have queued 94090fccb52SAndrzej Pietrasiewicz * requests to non-control endpoints 94190fccb52SAndrzej Pietrasiewicz */ 94290fccb52SAndrzej Pietrasiewicz if (udc->ep0_set_config) { 94390fccb52SAndrzej Pietrasiewicz u16 irq_en = omap_readw(UDC_IRQ_EN); 94490fccb52SAndrzej Pietrasiewicz 94590fccb52SAndrzej Pietrasiewicz irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE; 94690fccb52SAndrzej Pietrasiewicz if (!udc->ep0_reset_config) 94790fccb52SAndrzej Pietrasiewicz irq_en |= UDC_EPN_RX_IE 94890fccb52SAndrzej Pietrasiewicz | UDC_EPN_TX_IE; 94990fccb52SAndrzej Pietrasiewicz omap_writew(irq_en, UDC_IRQ_EN); 95090fccb52SAndrzej Pietrasiewicz } 95190fccb52SAndrzej Pietrasiewicz 95290fccb52SAndrzej Pietrasiewicz /* STATUS for zero length DATA stages is 95390fccb52SAndrzej Pietrasiewicz * always an IN ... even for IN transfers, 95490fccb52SAndrzej Pietrasiewicz * a weird case which seem to stall OMAP. 95590fccb52SAndrzej Pietrasiewicz */ 95690fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL | UDC_EP_DIR, 95790fccb52SAndrzej Pietrasiewicz UDC_EP_NUM); 95890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 95990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 96090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 96190fccb52SAndrzej Pietrasiewicz 96290fccb52SAndrzej Pietrasiewicz /* cleanup */ 96390fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 96490fccb52SAndrzej Pietrasiewicz done(ep, req, 0); 96590fccb52SAndrzej Pietrasiewicz req = NULL; 96690fccb52SAndrzej Pietrasiewicz 96790fccb52SAndrzej Pietrasiewicz /* non-empty DATA stage */ 96890fccb52SAndrzej Pietrasiewicz } else if (is_in) { 96990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL | UDC_EP_DIR, 97090fccb52SAndrzej Pietrasiewicz UDC_EP_NUM); 97190fccb52SAndrzej Pietrasiewicz } else { 97290fccb52SAndrzej Pietrasiewicz if (udc->ep0_setup) 97390fccb52SAndrzej Pietrasiewicz goto irq_wait; 97490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL, UDC_EP_NUM); 97590fccb52SAndrzej Pietrasiewicz } 97690fccb52SAndrzej Pietrasiewicz } else { 97790fccb52SAndrzej Pietrasiewicz is_in = ep->bEndpointAddress & USB_DIR_IN; 97890fccb52SAndrzej Pietrasiewicz if (!ep->has_dma) 97990fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 98090fccb52SAndrzej Pietrasiewicz /* if ISO: SOF IRQs must be enabled/disabled! */ 98190fccb52SAndrzej Pietrasiewicz } 98290fccb52SAndrzej Pietrasiewicz 98390fccb52SAndrzej Pietrasiewicz if (ep->has_dma) 98490fccb52SAndrzej Pietrasiewicz (is_in ? next_in_dma : next_out_dma)(ep, req); 98590fccb52SAndrzej Pietrasiewicz else if (req) { 98690fccb52SAndrzej Pietrasiewicz if ((is_in ? write_fifo : read_fifo)(ep, req) == 1) 98790fccb52SAndrzej Pietrasiewicz req = NULL; 98890fccb52SAndrzej Pietrasiewicz deselect_ep(); 98990fccb52SAndrzej Pietrasiewicz if (!is_in) { 99090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 99190fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 99290fccb52SAndrzej Pietrasiewicz } 99390fccb52SAndrzej Pietrasiewicz /* IN: 6 wait states before it'll tx */ 99490fccb52SAndrzej Pietrasiewicz } 99590fccb52SAndrzej Pietrasiewicz } 99690fccb52SAndrzej Pietrasiewicz 99790fccb52SAndrzej Pietrasiewicz irq_wait: 99890fccb52SAndrzej Pietrasiewicz /* irq handler advances the queue */ 99990fccb52SAndrzej Pietrasiewicz if (req != NULL) 100090fccb52SAndrzej Pietrasiewicz list_add_tail(&req->queue, &ep->queue); 100190fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 100290fccb52SAndrzej Pietrasiewicz 100390fccb52SAndrzej Pietrasiewicz return 0; 100490fccb52SAndrzej Pietrasiewicz } 100590fccb52SAndrzej Pietrasiewicz 100690fccb52SAndrzej Pietrasiewicz static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) 100790fccb52SAndrzej Pietrasiewicz { 100890fccb52SAndrzej Pietrasiewicz struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); 1009d5d0b280SJakob Koschel struct omap_req *req = NULL, *iter; 101090fccb52SAndrzej Pietrasiewicz unsigned long flags; 101190fccb52SAndrzej Pietrasiewicz 101290fccb52SAndrzej Pietrasiewicz if (!_ep || !_req) 101390fccb52SAndrzej Pietrasiewicz return -EINVAL; 101490fccb52SAndrzej Pietrasiewicz 101590fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->udc->lock, flags); 101690fccb52SAndrzej Pietrasiewicz 101790fccb52SAndrzej Pietrasiewicz /* make sure it's actually queued on this endpoint */ 1018d5d0b280SJakob Koschel list_for_each_entry(iter, &ep->queue, queue) { 1019d5d0b280SJakob Koschel if (&iter->req != _req) 1020d5d0b280SJakob Koschel continue; 1021d5d0b280SJakob Koschel req = iter; 102290fccb52SAndrzej Pietrasiewicz break; 102390fccb52SAndrzej Pietrasiewicz } 1024d5d0b280SJakob Koschel if (!req) { 102590fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 102690fccb52SAndrzej Pietrasiewicz return -EINVAL; 102790fccb52SAndrzej Pietrasiewicz } 102890fccb52SAndrzej Pietrasiewicz 102990fccb52SAndrzej Pietrasiewicz if (use_dma && ep->dma_channel && ep->queue.next == &req->queue) { 103090fccb52SAndrzej Pietrasiewicz int channel = ep->dma_channel; 103190fccb52SAndrzej Pietrasiewicz 103290fccb52SAndrzej Pietrasiewicz /* releasing the channel cancels the request, 103390fccb52SAndrzej Pietrasiewicz * reclaiming the channel restarts the queue 103490fccb52SAndrzej Pietrasiewicz */ 103590fccb52SAndrzej Pietrasiewicz dma_channel_release(ep); 103690fccb52SAndrzej Pietrasiewicz dma_channel_claim(ep, channel); 103790fccb52SAndrzej Pietrasiewicz } else 103890fccb52SAndrzej Pietrasiewicz done(ep, req, -ECONNRESET); 103990fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 104090fccb52SAndrzej Pietrasiewicz return 0; 104190fccb52SAndrzej Pietrasiewicz } 104290fccb52SAndrzej Pietrasiewicz 104390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 104490fccb52SAndrzej Pietrasiewicz 104590fccb52SAndrzej Pietrasiewicz static int omap_ep_set_halt(struct usb_ep *_ep, int value) 104690fccb52SAndrzej Pietrasiewicz { 104790fccb52SAndrzej Pietrasiewicz struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); 104890fccb52SAndrzej Pietrasiewicz unsigned long flags; 104990fccb52SAndrzej Pietrasiewicz int status = -EOPNOTSUPP; 105090fccb52SAndrzej Pietrasiewicz 105190fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->udc->lock, flags); 105290fccb52SAndrzej Pietrasiewicz 105390fccb52SAndrzej Pietrasiewicz /* just use protocol stalls for ep0; real halts are annoying */ 105490fccb52SAndrzej Pietrasiewicz if (ep->bEndpointAddress == 0) { 105590fccb52SAndrzej Pietrasiewicz if (!ep->udc->ep0_pending) 105690fccb52SAndrzej Pietrasiewicz status = -EINVAL; 105790fccb52SAndrzej Pietrasiewicz else if (value) { 105890fccb52SAndrzej Pietrasiewicz if (ep->udc->ep0_set_config) { 105990fccb52SAndrzej Pietrasiewicz WARNING("error changing config?\n"); 106090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_CFG, UDC_SYSCON2); 106190fccb52SAndrzej Pietrasiewicz } 106290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_STALL_CMD, UDC_SYSCON2); 106390fccb52SAndrzej Pietrasiewicz ep->udc->ep0_pending = 0; 106490fccb52SAndrzej Pietrasiewicz status = 0; 106590fccb52SAndrzej Pietrasiewicz } else /* NOP */ 106690fccb52SAndrzej Pietrasiewicz status = 0; 106790fccb52SAndrzej Pietrasiewicz 106890fccb52SAndrzej Pietrasiewicz /* otherwise, all active non-ISO endpoints can halt */ 106990fccb52SAndrzej Pietrasiewicz } else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->ep.desc) { 107090fccb52SAndrzej Pietrasiewicz 107190fccb52SAndrzej Pietrasiewicz /* IN endpoints must already be idle */ 107290fccb52SAndrzej Pietrasiewicz if ((ep->bEndpointAddress & USB_DIR_IN) 107390fccb52SAndrzej Pietrasiewicz && !list_empty(&ep->queue)) { 107490fccb52SAndrzej Pietrasiewicz status = -EAGAIN; 107590fccb52SAndrzej Pietrasiewicz goto done; 107690fccb52SAndrzej Pietrasiewicz } 107790fccb52SAndrzej Pietrasiewicz 107890fccb52SAndrzej Pietrasiewicz if (value) { 107990fccb52SAndrzej Pietrasiewicz int channel; 108090fccb52SAndrzej Pietrasiewicz 108190fccb52SAndrzej Pietrasiewicz if (use_dma && ep->dma_channel 108290fccb52SAndrzej Pietrasiewicz && !list_empty(&ep->queue)) { 108390fccb52SAndrzej Pietrasiewicz channel = ep->dma_channel; 108490fccb52SAndrzej Pietrasiewicz dma_channel_release(ep); 108590fccb52SAndrzej Pietrasiewicz } else 108690fccb52SAndrzej Pietrasiewicz channel = 0; 108790fccb52SAndrzej Pietrasiewicz 108890fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 108990fccb52SAndrzej Pietrasiewicz if (omap_readw(UDC_STAT_FLG) & UDC_NON_ISO_FIFO_EMPTY) { 109090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_HALT, UDC_CTRL); 109190fccb52SAndrzej Pietrasiewicz status = 0; 109290fccb52SAndrzej Pietrasiewicz } else 109390fccb52SAndrzej Pietrasiewicz status = -EAGAIN; 109490fccb52SAndrzej Pietrasiewicz deselect_ep(); 109590fccb52SAndrzej Pietrasiewicz 109690fccb52SAndrzej Pietrasiewicz if (channel) 109790fccb52SAndrzej Pietrasiewicz dma_channel_claim(ep, channel); 109890fccb52SAndrzej Pietrasiewicz } else { 109990fccb52SAndrzej Pietrasiewicz use_ep(ep, 0); 110090fccb52SAndrzej Pietrasiewicz omap_writew(ep->udc->clr_halt, UDC_CTRL); 110190fccb52SAndrzej Pietrasiewicz ep->ackwait = 0; 110290fccb52SAndrzej Pietrasiewicz if (!(ep->bEndpointAddress & USB_DIR_IN)) { 110390fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 110490fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 110590fccb52SAndrzej Pietrasiewicz } 110690fccb52SAndrzej Pietrasiewicz } 110790fccb52SAndrzej Pietrasiewicz } 110890fccb52SAndrzej Pietrasiewicz done: 110990fccb52SAndrzej Pietrasiewicz VDBG("%s %s halt stat %d\n", ep->ep.name, 111090fccb52SAndrzej Pietrasiewicz value ? "set" : "clear", status); 111190fccb52SAndrzej Pietrasiewicz 111290fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 111390fccb52SAndrzej Pietrasiewicz return status; 111490fccb52SAndrzej Pietrasiewicz } 111590fccb52SAndrzej Pietrasiewicz 1116977ac789SBhumika Goyal static const struct usb_ep_ops omap_ep_ops = { 111790fccb52SAndrzej Pietrasiewicz .enable = omap_ep_enable, 111890fccb52SAndrzej Pietrasiewicz .disable = omap_ep_disable, 111990fccb52SAndrzej Pietrasiewicz 112090fccb52SAndrzej Pietrasiewicz .alloc_request = omap_alloc_request, 112190fccb52SAndrzej Pietrasiewicz .free_request = omap_free_request, 112290fccb52SAndrzej Pietrasiewicz 112390fccb52SAndrzej Pietrasiewicz .queue = omap_ep_queue, 112490fccb52SAndrzej Pietrasiewicz .dequeue = omap_ep_dequeue, 112590fccb52SAndrzej Pietrasiewicz 112690fccb52SAndrzej Pietrasiewicz .set_halt = omap_ep_set_halt, 112790fccb52SAndrzej Pietrasiewicz /* fifo_status ... report bytes in fifo */ 112890fccb52SAndrzej Pietrasiewicz /* fifo_flush ... flush fifo */ 112990fccb52SAndrzej Pietrasiewicz }; 113090fccb52SAndrzej Pietrasiewicz 113190fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 113290fccb52SAndrzej Pietrasiewicz 113390fccb52SAndrzej Pietrasiewicz static int omap_get_frame(struct usb_gadget *gadget) 113490fccb52SAndrzej Pietrasiewicz { 113590fccb52SAndrzej Pietrasiewicz u16 sof = omap_readw(UDC_SOF); 113690fccb52SAndrzej Pietrasiewicz return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC; 113790fccb52SAndrzej Pietrasiewicz } 113890fccb52SAndrzej Pietrasiewicz 113990fccb52SAndrzej Pietrasiewicz static int omap_wakeup(struct usb_gadget *gadget) 114090fccb52SAndrzej Pietrasiewicz { 114190fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 114290fccb52SAndrzej Pietrasiewicz unsigned long flags; 114390fccb52SAndrzej Pietrasiewicz int retval = -EHOSTUNREACH; 114490fccb52SAndrzej Pietrasiewicz 114590fccb52SAndrzej Pietrasiewicz udc = container_of(gadget, struct omap_udc, gadget); 114690fccb52SAndrzej Pietrasiewicz 114790fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 114890fccb52SAndrzej Pietrasiewicz if (udc->devstat & UDC_SUS) { 114990fccb52SAndrzej Pietrasiewicz /* NOTE: OTG spec erratum says that OTG devices may 115090fccb52SAndrzej Pietrasiewicz * issue wakeups without host enable. 115190fccb52SAndrzej Pietrasiewicz */ 115290fccb52SAndrzej Pietrasiewicz if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) { 115390fccb52SAndrzej Pietrasiewicz DBG("remote wakeup...\n"); 115490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_RMT_WKP, UDC_SYSCON2); 115590fccb52SAndrzej Pietrasiewicz retval = 0; 115690fccb52SAndrzej Pietrasiewicz } 115790fccb52SAndrzej Pietrasiewicz 115890fccb52SAndrzej Pietrasiewicz /* NOTE: non-OTG systems may use SRP TOO... */ 115990fccb52SAndrzej Pietrasiewicz } else if (!(udc->devstat & UDC_ATT)) { 116090fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(udc->transceiver)) 116190fccb52SAndrzej Pietrasiewicz retval = otg_start_srp(udc->transceiver->otg); 116290fccb52SAndrzej Pietrasiewicz } 116390fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 116490fccb52SAndrzej Pietrasiewicz 116590fccb52SAndrzej Pietrasiewicz return retval; 116690fccb52SAndrzej Pietrasiewicz } 116790fccb52SAndrzej Pietrasiewicz 116890fccb52SAndrzej Pietrasiewicz static int 116990fccb52SAndrzej Pietrasiewicz omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) 117090fccb52SAndrzej Pietrasiewicz { 117190fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 117290fccb52SAndrzej Pietrasiewicz unsigned long flags; 117390fccb52SAndrzej Pietrasiewicz u16 syscon1; 117490fccb52SAndrzej Pietrasiewicz 117558ae8e0bSPeter Chen gadget->is_selfpowered = (is_selfpowered != 0); 117690fccb52SAndrzej Pietrasiewicz udc = container_of(gadget, struct omap_udc, gadget); 117790fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 117890fccb52SAndrzej Pietrasiewicz syscon1 = omap_readw(UDC_SYSCON1); 117990fccb52SAndrzej Pietrasiewicz if (is_selfpowered) 118090fccb52SAndrzej Pietrasiewicz syscon1 |= UDC_SELF_PWR; 118190fccb52SAndrzej Pietrasiewicz else 118290fccb52SAndrzej Pietrasiewicz syscon1 &= ~UDC_SELF_PWR; 118390fccb52SAndrzej Pietrasiewicz omap_writew(syscon1, UDC_SYSCON1); 118490fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 118590fccb52SAndrzej Pietrasiewicz 118690fccb52SAndrzej Pietrasiewicz return 0; 118790fccb52SAndrzej Pietrasiewicz } 118890fccb52SAndrzej Pietrasiewicz 118990fccb52SAndrzej Pietrasiewicz static int can_pullup(struct omap_udc *udc) 119090fccb52SAndrzej Pietrasiewicz { 119190fccb52SAndrzej Pietrasiewicz return udc->driver && udc->softconnect && udc->vbus_active; 119290fccb52SAndrzej Pietrasiewicz } 119390fccb52SAndrzej Pietrasiewicz 119490fccb52SAndrzej Pietrasiewicz static void pullup_enable(struct omap_udc *udc) 119590fccb52SAndrzej Pietrasiewicz { 119690fccb52SAndrzej Pietrasiewicz u16 w; 119790fccb52SAndrzej Pietrasiewicz 119890fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_SYSCON1); 119990fccb52SAndrzej Pietrasiewicz w |= UDC_PULLUP_EN; 120090fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_SYSCON1); 120190fccb52SAndrzej Pietrasiewicz if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) { 120290fccb52SAndrzej Pietrasiewicz u32 l; 120390fccb52SAndrzej Pietrasiewicz 120490fccb52SAndrzej Pietrasiewicz l = omap_readl(OTG_CTRL); 120590fccb52SAndrzej Pietrasiewicz l |= OTG_BSESSVLD; 120690fccb52SAndrzej Pietrasiewicz omap_writel(l, OTG_CTRL); 120790fccb52SAndrzej Pietrasiewicz } 120890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN); 120990fccb52SAndrzej Pietrasiewicz } 121090fccb52SAndrzej Pietrasiewicz 121190fccb52SAndrzej Pietrasiewicz static void pullup_disable(struct omap_udc *udc) 121290fccb52SAndrzej Pietrasiewicz { 121390fccb52SAndrzej Pietrasiewicz u16 w; 121490fccb52SAndrzej Pietrasiewicz 121590fccb52SAndrzej Pietrasiewicz if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) { 121690fccb52SAndrzej Pietrasiewicz u32 l; 121790fccb52SAndrzej Pietrasiewicz 121890fccb52SAndrzej Pietrasiewicz l = omap_readl(OTG_CTRL); 121990fccb52SAndrzej Pietrasiewicz l &= ~OTG_BSESSVLD; 122090fccb52SAndrzej Pietrasiewicz omap_writel(l, OTG_CTRL); 122190fccb52SAndrzej Pietrasiewicz } 122290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN); 122390fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_SYSCON1); 122490fccb52SAndrzej Pietrasiewicz w &= ~UDC_PULLUP_EN; 122590fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_SYSCON1); 122690fccb52SAndrzej Pietrasiewicz } 122790fccb52SAndrzej Pietrasiewicz 122890fccb52SAndrzej Pietrasiewicz static struct omap_udc *udc; 122990fccb52SAndrzej Pietrasiewicz 123090fccb52SAndrzej Pietrasiewicz static void omap_udc_enable_clock(int enable) 123190fccb52SAndrzej Pietrasiewicz { 123290fccb52SAndrzej Pietrasiewicz if (udc == NULL || udc->dc_clk == NULL || udc->hhc_clk == NULL) 123390fccb52SAndrzej Pietrasiewicz return; 123490fccb52SAndrzej Pietrasiewicz 123590fccb52SAndrzej Pietrasiewicz if (enable) { 123690fccb52SAndrzej Pietrasiewicz clk_enable(udc->dc_clk); 123790fccb52SAndrzej Pietrasiewicz clk_enable(udc->hhc_clk); 123890fccb52SAndrzej Pietrasiewicz udelay(100); 123990fccb52SAndrzej Pietrasiewicz } else { 124090fccb52SAndrzej Pietrasiewicz clk_disable(udc->hhc_clk); 124190fccb52SAndrzej Pietrasiewicz clk_disable(udc->dc_clk); 124290fccb52SAndrzej Pietrasiewicz } 124390fccb52SAndrzej Pietrasiewicz } 124490fccb52SAndrzej Pietrasiewicz 124590fccb52SAndrzej Pietrasiewicz /* 124690fccb52SAndrzej Pietrasiewicz * Called by whatever detects VBUS sessions: external transceiver 124790fccb52SAndrzej Pietrasiewicz * driver, or maybe GPIO0 VBUS IRQ. May request 48 MHz clock. 124890fccb52SAndrzej Pietrasiewicz */ 124990fccb52SAndrzej Pietrasiewicz static int omap_vbus_session(struct usb_gadget *gadget, int is_active) 125090fccb52SAndrzej Pietrasiewicz { 125190fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 125290fccb52SAndrzej Pietrasiewicz unsigned long flags; 125390fccb52SAndrzej Pietrasiewicz u32 l; 125490fccb52SAndrzej Pietrasiewicz 125590fccb52SAndrzej Pietrasiewicz udc = container_of(gadget, struct omap_udc, gadget); 125690fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 125790fccb52SAndrzej Pietrasiewicz VDBG("VBUS %s\n", is_active ? "on" : "off"); 125890fccb52SAndrzej Pietrasiewicz udc->vbus_active = (is_active != 0); 125990fccb52SAndrzej Pietrasiewicz if (cpu_is_omap15xx()) { 126090fccb52SAndrzej Pietrasiewicz /* "software" detect, ignored if !VBUS_MODE_1510 */ 126190fccb52SAndrzej Pietrasiewicz l = omap_readl(FUNC_MUX_CTRL_0); 126290fccb52SAndrzej Pietrasiewicz if (is_active) 126390fccb52SAndrzej Pietrasiewicz l |= VBUS_CTRL_1510; 126490fccb52SAndrzej Pietrasiewicz else 126590fccb52SAndrzej Pietrasiewicz l &= ~VBUS_CTRL_1510; 126690fccb52SAndrzej Pietrasiewicz omap_writel(l, FUNC_MUX_CTRL_0); 126790fccb52SAndrzej Pietrasiewicz } 126890fccb52SAndrzej Pietrasiewicz if (udc->dc_clk != NULL && is_active) { 126990fccb52SAndrzej Pietrasiewicz if (!udc->clk_requested) { 127090fccb52SAndrzej Pietrasiewicz omap_udc_enable_clock(1); 127190fccb52SAndrzej Pietrasiewicz udc->clk_requested = 1; 127290fccb52SAndrzej Pietrasiewicz } 127390fccb52SAndrzej Pietrasiewicz } 127490fccb52SAndrzej Pietrasiewicz if (can_pullup(udc)) 127590fccb52SAndrzej Pietrasiewicz pullup_enable(udc); 127690fccb52SAndrzej Pietrasiewicz else 127790fccb52SAndrzej Pietrasiewicz pullup_disable(udc); 127890fccb52SAndrzej Pietrasiewicz if (udc->dc_clk != NULL && !is_active) { 127990fccb52SAndrzej Pietrasiewicz if (udc->clk_requested) { 128090fccb52SAndrzej Pietrasiewicz omap_udc_enable_clock(0); 128190fccb52SAndrzej Pietrasiewicz udc->clk_requested = 0; 128290fccb52SAndrzej Pietrasiewicz } 128390fccb52SAndrzej Pietrasiewicz } 128490fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 128590fccb52SAndrzej Pietrasiewicz return 0; 128690fccb52SAndrzej Pietrasiewicz } 128790fccb52SAndrzej Pietrasiewicz 128890fccb52SAndrzej Pietrasiewicz static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA) 128990fccb52SAndrzej Pietrasiewicz { 129090fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 129190fccb52SAndrzej Pietrasiewicz 129290fccb52SAndrzej Pietrasiewicz udc = container_of(gadget, struct omap_udc, gadget); 129390fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(udc->transceiver)) 129490fccb52SAndrzej Pietrasiewicz return usb_phy_set_power(udc->transceiver, mA); 129590fccb52SAndrzej Pietrasiewicz return -EOPNOTSUPP; 129690fccb52SAndrzej Pietrasiewicz } 129790fccb52SAndrzej Pietrasiewicz 129890fccb52SAndrzej Pietrasiewicz static int omap_pullup(struct usb_gadget *gadget, int is_on) 129990fccb52SAndrzej Pietrasiewicz { 130090fccb52SAndrzej Pietrasiewicz struct omap_udc *udc; 130190fccb52SAndrzej Pietrasiewicz unsigned long flags; 130290fccb52SAndrzej Pietrasiewicz 130390fccb52SAndrzej Pietrasiewicz udc = container_of(gadget, struct omap_udc, gadget); 130490fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 130590fccb52SAndrzej Pietrasiewicz udc->softconnect = (is_on != 0); 130690fccb52SAndrzej Pietrasiewicz if (can_pullup(udc)) 130790fccb52SAndrzej Pietrasiewicz pullup_enable(udc); 130890fccb52SAndrzej Pietrasiewicz else 130990fccb52SAndrzej Pietrasiewicz pullup_disable(udc); 131090fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 131190fccb52SAndrzej Pietrasiewicz return 0; 131290fccb52SAndrzej Pietrasiewicz } 131390fccb52SAndrzej Pietrasiewicz 131490fccb52SAndrzej Pietrasiewicz static int omap_udc_start(struct usb_gadget *g, 131590fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver); 131622835b80SFelipe Balbi static int omap_udc_stop(struct usb_gadget *g); 131790fccb52SAndrzej Pietrasiewicz 131890fccb52SAndrzej Pietrasiewicz static const struct usb_gadget_ops omap_gadget_ops = { 131990fccb52SAndrzej Pietrasiewicz .get_frame = omap_get_frame, 132090fccb52SAndrzej Pietrasiewicz .wakeup = omap_wakeup, 132190fccb52SAndrzej Pietrasiewicz .set_selfpowered = omap_set_selfpowered, 132290fccb52SAndrzej Pietrasiewicz .vbus_session = omap_vbus_session, 132390fccb52SAndrzej Pietrasiewicz .vbus_draw = omap_vbus_draw, 132490fccb52SAndrzej Pietrasiewicz .pullup = omap_pullup, 132590fccb52SAndrzej Pietrasiewicz .udc_start = omap_udc_start, 132690fccb52SAndrzej Pietrasiewicz .udc_stop = omap_udc_stop, 132790fccb52SAndrzej Pietrasiewicz }; 132890fccb52SAndrzej Pietrasiewicz 132990fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 133090fccb52SAndrzej Pietrasiewicz 133190fccb52SAndrzej Pietrasiewicz /* dequeue ALL requests; caller holds udc->lock */ 133290fccb52SAndrzej Pietrasiewicz static void nuke(struct omap_ep *ep, int status) 133390fccb52SAndrzej Pietrasiewicz { 133490fccb52SAndrzej Pietrasiewicz struct omap_req *req; 133590fccb52SAndrzej Pietrasiewicz 133690fccb52SAndrzej Pietrasiewicz ep->stopped = 1; 133790fccb52SAndrzej Pietrasiewicz 133890fccb52SAndrzej Pietrasiewicz if (use_dma && ep->dma_channel) 133990fccb52SAndrzej Pietrasiewicz dma_channel_release(ep); 134090fccb52SAndrzej Pietrasiewicz 134190fccb52SAndrzej Pietrasiewicz use_ep(ep, 0); 134290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 134390fccb52SAndrzej Pietrasiewicz if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) 134490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_HALT, UDC_CTRL); 134590fccb52SAndrzej Pietrasiewicz 134690fccb52SAndrzej Pietrasiewicz while (!list_empty(&ep->queue)) { 134790fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct omap_req, queue); 134890fccb52SAndrzej Pietrasiewicz done(ep, req, status); 134990fccb52SAndrzej Pietrasiewicz } 135090fccb52SAndrzej Pietrasiewicz } 135190fccb52SAndrzej Pietrasiewicz 135290fccb52SAndrzej Pietrasiewicz /* caller holds udc->lock */ 135390fccb52SAndrzej Pietrasiewicz static void udc_quiesce(struct omap_udc *udc) 135490fccb52SAndrzej Pietrasiewicz { 135590fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 135690fccb52SAndrzej Pietrasiewicz 135790fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_UNKNOWN; 135890fccb52SAndrzej Pietrasiewicz nuke(&udc->ep[0], -ESHUTDOWN); 135990fccb52SAndrzej Pietrasiewicz list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) 136090fccb52SAndrzej Pietrasiewicz nuke(ep, -ESHUTDOWN); 136190fccb52SAndrzej Pietrasiewicz } 136290fccb52SAndrzej Pietrasiewicz 136390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 136490fccb52SAndrzej Pietrasiewicz 136590fccb52SAndrzej Pietrasiewicz static void update_otg(struct omap_udc *udc) 136690fccb52SAndrzej Pietrasiewicz { 136790fccb52SAndrzej Pietrasiewicz u16 devstat; 136890fccb52SAndrzej Pietrasiewicz 136990fccb52SAndrzej Pietrasiewicz if (!gadget_is_otg(&udc->gadget)) 137090fccb52SAndrzej Pietrasiewicz return; 137190fccb52SAndrzej Pietrasiewicz 137290fccb52SAndrzej Pietrasiewicz if (omap_readl(OTG_CTRL) & OTG_ID) 137390fccb52SAndrzej Pietrasiewicz devstat = omap_readw(UDC_DEVSTAT); 137490fccb52SAndrzej Pietrasiewicz else 137590fccb52SAndrzej Pietrasiewicz devstat = 0; 137690fccb52SAndrzej Pietrasiewicz 137790fccb52SAndrzej Pietrasiewicz udc->gadget.b_hnp_enable = !!(devstat & UDC_B_HNP_ENABLE); 137890fccb52SAndrzej Pietrasiewicz udc->gadget.a_hnp_support = !!(devstat & UDC_A_HNP_SUPPORT); 137990fccb52SAndrzej Pietrasiewicz udc->gadget.a_alt_hnp_support = !!(devstat & UDC_A_ALT_HNP_SUPPORT); 138090fccb52SAndrzej Pietrasiewicz 138190fccb52SAndrzej Pietrasiewicz /* Enable HNP early, avoiding races on suspend irq path. 138290fccb52SAndrzej Pietrasiewicz * ASSUMES OTG state machine B_BUS_REQ input is true. 138390fccb52SAndrzej Pietrasiewicz */ 138490fccb52SAndrzej Pietrasiewicz if (udc->gadget.b_hnp_enable) { 138590fccb52SAndrzej Pietrasiewicz u32 l; 138690fccb52SAndrzej Pietrasiewicz 138790fccb52SAndrzej Pietrasiewicz l = omap_readl(OTG_CTRL); 138890fccb52SAndrzej Pietrasiewicz l |= OTG_B_HNPEN | OTG_B_BUSREQ; 138990fccb52SAndrzej Pietrasiewicz l &= ~OTG_PULLUP; 139090fccb52SAndrzej Pietrasiewicz omap_writel(l, OTG_CTRL); 139190fccb52SAndrzej Pietrasiewicz } 139290fccb52SAndrzej Pietrasiewicz } 139390fccb52SAndrzej Pietrasiewicz 139490fccb52SAndrzej Pietrasiewicz static void ep0_irq(struct omap_udc *udc, u16 irq_src) 139590fccb52SAndrzej Pietrasiewicz { 139690fccb52SAndrzej Pietrasiewicz struct omap_ep *ep0 = &udc->ep[0]; 139790fccb52SAndrzej Pietrasiewicz struct omap_req *req = NULL; 139890fccb52SAndrzej Pietrasiewicz 139990fccb52SAndrzej Pietrasiewicz ep0->irqs++; 140090fccb52SAndrzej Pietrasiewicz 140190fccb52SAndrzej Pietrasiewicz /* Clear any pending requests and then scrub any rx/tx state 140290fccb52SAndrzej Pietrasiewicz * before starting to handle the SETUP request. 140390fccb52SAndrzej Pietrasiewicz */ 140490fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_SETUP) { 140590fccb52SAndrzej Pietrasiewicz u16 ack = irq_src & (UDC_EP0_TX|UDC_EP0_RX); 140690fccb52SAndrzej Pietrasiewicz 140790fccb52SAndrzej Pietrasiewicz nuke(ep0, 0); 140890fccb52SAndrzej Pietrasiewicz if (ack) { 140990fccb52SAndrzej Pietrasiewicz omap_writew(ack, UDC_IRQ_SRC); 141090fccb52SAndrzej Pietrasiewicz irq_src = UDC_SETUP; 141190fccb52SAndrzej Pietrasiewicz } 141290fccb52SAndrzej Pietrasiewicz } 141390fccb52SAndrzej Pietrasiewicz 141490fccb52SAndrzej Pietrasiewicz /* IN/OUT packets mean we're in the DATA or STATUS stage. 141590fccb52SAndrzej Pietrasiewicz * This driver uses only uses protocol stalls (ep0 never halts), 141690fccb52SAndrzej Pietrasiewicz * and if we got this far the gadget driver already had a 141790fccb52SAndrzej Pietrasiewicz * chance to stall. Tries to be forgiving of host oddities. 141890fccb52SAndrzej Pietrasiewicz * 141990fccb52SAndrzej Pietrasiewicz * NOTE: the last chance gadget drivers have to stall control 142090fccb52SAndrzej Pietrasiewicz * requests is during their request completion callback. 142190fccb52SAndrzej Pietrasiewicz */ 142290fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep0->queue)) 142390fccb52SAndrzej Pietrasiewicz req = container_of(ep0->queue.next, struct omap_req, queue); 142490fccb52SAndrzej Pietrasiewicz 142590fccb52SAndrzej Pietrasiewicz /* IN == TX to host */ 142690fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_EP0_TX) { 142790fccb52SAndrzej Pietrasiewicz int stat; 142890fccb52SAndrzej Pietrasiewicz 142990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP0_TX, UDC_IRQ_SRC); 143090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); 143190fccb52SAndrzej Pietrasiewicz stat = omap_readw(UDC_STAT_FLG); 143290fccb52SAndrzej Pietrasiewicz if (stat & UDC_ACK) { 143390fccb52SAndrzej Pietrasiewicz if (udc->ep0_in) { 143490fccb52SAndrzej Pietrasiewicz /* write next IN packet from response, 143590fccb52SAndrzej Pietrasiewicz * or set up the status stage. 143690fccb52SAndrzej Pietrasiewicz */ 143790fccb52SAndrzej Pietrasiewicz if (req) 143890fccb52SAndrzej Pietrasiewicz stat = write_fifo(ep0, req); 143990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 144090fccb52SAndrzej Pietrasiewicz if (!req && udc->ep0_pending) { 144190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL, UDC_EP_NUM); 144290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 144390fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 144490fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 144590fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 144690fccb52SAndrzej Pietrasiewicz } /* else: 6 wait states before it'll tx */ 144790fccb52SAndrzej Pietrasiewicz } else { 144890fccb52SAndrzej Pietrasiewicz /* ack status stage of OUT transfer */ 144990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 145090fccb52SAndrzej Pietrasiewicz if (req) 145190fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 145290fccb52SAndrzej Pietrasiewicz } 145390fccb52SAndrzej Pietrasiewicz req = NULL; 145490fccb52SAndrzej Pietrasiewicz } else if (stat & UDC_STALL) { 145590fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_HALT, UDC_CTRL); 145690fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 145790fccb52SAndrzej Pietrasiewicz } else { 145890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 145990fccb52SAndrzej Pietrasiewicz } 146090fccb52SAndrzej Pietrasiewicz } 146190fccb52SAndrzej Pietrasiewicz 146290fccb52SAndrzej Pietrasiewicz /* OUT == RX from host */ 146390fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_EP0_RX) { 146490fccb52SAndrzej Pietrasiewicz int stat; 146590fccb52SAndrzej Pietrasiewicz 146690fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP0_RX, UDC_IRQ_SRC); 146790fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL, UDC_EP_NUM); 146890fccb52SAndrzej Pietrasiewicz stat = omap_readw(UDC_STAT_FLG); 146990fccb52SAndrzej Pietrasiewicz if (stat & UDC_ACK) { 147090fccb52SAndrzej Pietrasiewicz if (!udc->ep0_in) { 147190fccb52SAndrzej Pietrasiewicz stat = 0; 147290fccb52SAndrzej Pietrasiewicz /* read next OUT packet of request, maybe 14732a0a71d9STom Rix * reactivating the fifo; stall on errors. 147490fccb52SAndrzej Pietrasiewicz */ 147590fccb52SAndrzej Pietrasiewicz stat = read_fifo(ep0, req); 147690fccb52SAndrzej Pietrasiewicz if (!req || stat < 0) { 147790fccb52SAndrzej Pietrasiewicz omap_writew(UDC_STALL_CMD, UDC_SYSCON2); 147890fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 147990fccb52SAndrzej Pietrasiewicz stat = 0; 148090fccb52SAndrzej Pietrasiewicz } else if (stat == 0) 148190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 148290fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 148390fccb52SAndrzej Pietrasiewicz 148490fccb52SAndrzej Pietrasiewicz /* activate status stage */ 148590fccb52SAndrzej Pietrasiewicz if (stat == 1) { 148690fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 148790fccb52SAndrzej Pietrasiewicz /* that may have STALLed ep0... */ 148890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL | UDC_EP_DIR, 148990fccb52SAndrzej Pietrasiewicz UDC_EP_NUM); 149090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 149190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 149290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 149390fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 149490fccb52SAndrzej Pietrasiewicz } 149590fccb52SAndrzej Pietrasiewicz } else { 149690fccb52SAndrzej Pietrasiewicz /* ack status stage of IN transfer */ 149790fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 149890fccb52SAndrzej Pietrasiewicz if (req) 149990fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 150090fccb52SAndrzej Pietrasiewicz } 150190fccb52SAndrzej Pietrasiewicz } else if (stat & UDC_STALL) { 150290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_HALT, UDC_CTRL); 150390fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 150490fccb52SAndrzej Pietrasiewicz } else { 150590fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 150690fccb52SAndrzej Pietrasiewicz } 150790fccb52SAndrzej Pietrasiewicz } 150890fccb52SAndrzej Pietrasiewicz 150990fccb52SAndrzej Pietrasiewicz /* SETUP starts all control transfers */ 151090fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_SETUP) { 151190fccb52SAndrzej Pietrasiewicz union u { 151290fccb52SAndrzej Pietrasiewicz u16 word[4]; 151390fccb52SAndrzej Pietrasiewicz struct usb_ctrlrequest r; 151490fccb52SAndrzej Pietrasiewicz } u; 151590fccb52SAndrzej Pietrasiewicz int status = -EINVAL; 151690fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 151790fccb52SAndrzej Pietrasiewicz 151890fccb52SAndrzej Pietrasiewicz /* read the (latest) SETUP message */ 151990fccb52SAndrzej Pietrasiewicz do { 152090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SETUP_SEL, UDC_EP_NUM); 152190fccb52SAndrzej Pietrasiewicz /* two bytes at a time */ 152290fccb52SAndrzej Pietrasiewicz u.word[0] = omap_readw(UDC_DATA); 152390fccb52SAndrzej Pietrasiewicz u.word[1] = omap_readw(UDC_DATA); 152490fccb52SAndrzej Pietrasiewicz u.word[2] = omap_readw(UDC_DATA); 152590fccb52SAndrzej Pietrasiewicz u.word[3] = omap_readw(UDC_DATA); 152690fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 152790fccb52SAndrzej Pietrasiewicz } while (omap_readw(UDC_IRQ_SRC) & UDC_SETUP); 152890fccb52SAndrzej Pietrasiewicz 152990fccb52SAndrzej Pietrasiewicz #define w_value le16_to_cpu(u.r.wValue) 153090fccb52SAndrzej Pietrasiewicz #define w_index le16_to_cpu(u.r.wIndex) 153190fccb52SAndrzej Pietrasiewicz #define w_length le16_to_cpu(u.r.wLength) 153290fccb52SAndrzej Pietrasiewicz 153390fccb52SAndrzej Pietrasiewicz /* Delegate almost all control requests to the gadget driver, 153490fccb52SAndrzej Pietrasiewicz * except for a handful of ch9 status/feature requests that 153590fccb52SAndrzej Pietrasiewicz * hardware doesn't autodecode _and_ the gadget API hides. 153690fccb52SAndrzej Pietrasiewicz */ 153790fccb52SAndrzej Pietrasiewicz udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0; 153890fccb52SAndrzej Pietrasiewicz udc->ep0_set_config = 0; 153990fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 1; 154090fccb52SAndrzej Pietrasiewicz ep0->stopped = 0; 154190fccb52SAndrzej Pietrasiewicz ep0->ackwait = 0; 154290fccb52SAndrzej Pietrasiewicz switch (u.r.bRequest) { 154390fccb52SAndrzej Pietrasiewicz case USB_REQ_SET_CONFIGURATION: 154490fccb52SAndrzej Pietrasiewicz /* udc needs to know when ep != 0 is valid */ 154590fccb52SAndrzej Pietrasiewicz if (u.r.bRequestType != USB_RECIP_DEVICE) 154690fccb52SAndrzej Pietrasiewicz goto delegate; 154790fccb52SAndrzej Pietrasiewicz if (w_length != 0) 154890fccb52SAndrzej Pietrasiewicz goto do_stall; 154990fccb52SAndrzej Pietrasiewicz udc->ep0_set_config = 1; 155090fccb52SAndrzej Pietrasiewicz udc->ep0_reset_config = (w_value == 0); 155190fccb52SAndrzej Pietrasiewicz VDBG("set config %d\n", w_value); 155290fccb52SAndrzej Pietrasiewicz 155390fccb52SAndrzej Pietrasiewicz /* update udc NOW since gadget driver may start 155490fccb52SAndrzej Pietrasiewicz * queueing requests immediately; clear config 155590fccb52SAndrzej Pietrasiewicz * later if it fails the request. 155690fccb52SAndrzej Pietrasiewicz */ 155790fccb52SAndrzej Pietrasiewicz if (udc->ep0_reset_config) 155890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_CFG, UDC_SYSCON2); 155990fccb52SAndrzej Pietrasiewicz else 156090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_DEV_CFG, UDC_SYSCON2); 156190fccb52SAndrzej Pietrasiewicz update_otg(udc); 156290fccb52SAndrzej Pietrasiewicz goto delegate; 156390fccb52SAndrzej Pietrasiewicz case USB_REQ_CLEAR_FEATURE: 156490fccb52SAndrzej Pietrasiewicz /* clear endpoint halt */ 156590fccb52SAndrzej Pietrasiewicz if (u.r.bRequestType != USB_RECIP_ENDPOINT) 156690fccb52SAndrzej Pietrasiewicz goto delegate; 156790fccb52SAndrzej Pietrasiewicz if (w_value != USB_ENDPOINT_HALT 156890fccb52SAndrzej Pietrasiewicz || w_length != 0) 156990fccb52SAndrzej Pietrasiewicz goto do_stall; 157090fccb52SAndrzej Pietrasiewicz ep = &udc->ep[w_index & 0xf]; 157190fccb52SAndrzej Pietrasiewicz if (ep != ep0) { 157290fccb52SAndrzej Pietrasiewicz if (w_index & USB_DIR_IN) 157390fccb52SAndrzej Pietrasiewicz ep += 16; 157490fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC 157590fccb52SAndrzej Pietrasiewicz || !ep->ep.desc) 157690fccb52SAndrzej Pietrasiewicz goto do_stall; 157790fccb52SAndrzej Pietrasiewicz use_ep(ep, 0); 157890fccb52SAndrzej Pietrasiewicz omap_writew(udc->clr_halt, UDC_CTRL); 157990fccb52SAndrzej Pietrasiewicz ep->ackwait = 0; 158090fccb52SAndrzej Pietrasiewicz if (!(ep->bEndpointAddress & USB_DIR_IN)) { 158190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 158290fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 158390fccb52SAndrzej Pietrasiewicz } 158490fccb52SAndrzej Pietrasiewicz /* NOTE: assumes the host behaves sanely, 158590fccb52SAndrzej Pietrasiewicz * only clearing real halts. Else we may 158690fccb52SAndrzej Pietrasiewicz * need to kill pending transfers and then 158790fccb52SAndrzej Pietrasiewicz * restart the queue... very messy for DMA! 158890fccb52SAndrzej Pietrasiewicz */ 158990fccb52SAndrzej Pietrasiewicz } 159090fccb52SAndrzej Pietrasiewicz VDBG("%s halt cleared by host\n", ep->name); 159190fccb52SAndrzej Pietrasiewicz goto ep0out_status_stage; 159290fccb52SAndrzej Pietrasiewicz case USB_REQ_SET_FEATURE: 159390fccb52SAndrzej Pietrasiewicz /* set endpoint halt */ 159490fccb52SAndrzej Pietrasiewicz if (u.r.bRequestType != USB_RECIP_ENDPOINT) 159590fccb52SAndrzej Pietrasiewicz goto delegate; 159690fccb52SAndrzej Pietrasiewicz if (w_value != USB_ENDPOINT_HALT 159790fccb52SAndrzej Pietrasiewicz || w_length != 0) 159890fccb52SAndrzej Pietrasiewicz goto do_stall; 159990fccb52SAndrzej Pietrasiewicz ep = &udc->ep[w_index & 0xf]; 160090fccb52SAndrzej Pietrasiewicz if (w_index & USB_DIR_IN) 160190fccb52SAndrzej Pietrasiewicz ep += 16; 160290fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC 160390fccb52SAndrzej Pietrasiewicz || ep == ep0 || !ep->ep.desc) 160490fccb52SAndrzej Pietrasiewicz goto do_stall; 160590fccb52SAndrzej Pietrasiewicz if (use_dma && ep->has_dma) { 160690fccb52SAndrzej Pietrasiewicz /* this has rude side-effects (aborts) and 160790fccb52SAndrzej Pietrasiewicz * can't really work if DMA-IN is active 160890fccb52SAndrzej Pietrasiewicz */ 160990fccb52SAndrzej Pietrasiewicz DBG("%s host set_halt, NYET\n", ep->name); 161090fccb52SAndrzej Pietrasiewicz goto do_stall; 161190fccb52SAndrzej Pietrasiewicz } 161290fccb52SAndrzej Pietrasiewicz use_ep(ep, 0); 161390fccb52SAndrzej Pietrasiewicz /* can't halt if fifo isn't empty... */ 161490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 161590fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_HALT, UDC_CTRL); 161690fccb52SAndrzej Pietrasiewicz VDBG("%s halted by host\n", ep->name); 161790fccb52SAndrzej Pietrasiewicz ep0out_status_stage: 161890fccb52SAndrzej Pietrasiewicz status = 0; 161990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); 162090fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_EP, UDC_CTRL); 162190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 162290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 162390fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 162490fccb52SAndrzej Pietrasiewicz break; 162590fccb52SAndrzej Pietrasiewicz case USB_REQ_GET_STATUS: 162690fccb52SAndrzej Pietrasiewicz /* USB_ENDPOINT_HALT status? */ 162790fccb52SAndrzej Pietrasiewicz if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) 162890fccb52SAndrzej Pietrasiewicz goto intf_status; 162990fccb52SAndrzej Pietrasiewicz 163090fccb52SAndrzej Pietrasiewicz /* ep0 never stalls */ 163190fccb52SAndrzej Pietrasiewicz if (!(w_index & 0xf)) 163290fccb52SAndrzej Pietrasiewicz goto zero_status; 163390fccb52SAndrzej Pietrasiewicz 163490fccb52SAndrzej Pietrasiewicz /* only active endpoints count */ 163590fccb52SAndrzej Pietrasiewicz ep = &udc->ep[w_index & 0xf]; 163690fccb52SAndrzej Pietrasiewicz if (w_index & USB_DIR_IN) 163790fccb52SAndrzej Pietrasiewicz ep += 16; 163890fccb52SAndrzej Pietrasiewicz if (!ep->ep.desc) 163990fccb52SAndrzej Pietrasiewicz goto do_stall; 164090fccb52SAndrzej Pietrasiewicz 164190fccb52SAndrzej Pietrasiewicz /* iso never stalls */ 164290fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) 164390fccb52SAndrzej Pietrasiewicz goto zero_status; 164490fccb52SAndrzej Pietrasiewicz 164590fccb52SAndrzej Pietrasiewicz /* FIXME don't assume non-halted endpoints!! */ 164690fccb52SAndrzej Pietrasiewicz ERR("%s status, can't report\n", ep->ep.name); 164790fccb52SAndrzej Pietrasiewicz goto do_stall; 164890fccb52SAndrzej Pietrasiewicz 164990fccb52SAndrzej Pietrasiewicz intf_status: 165090fccb52SAndrzej Pietrasiewicz /* return interface status. if we were pedantic, 165190fccb52SAndrzej Pietrasiewicz * we'd detect non-existent interfaces, and stall. 165290fccb52SAndrzej Pietrasiewicz */ 165390fccb52SAndrzej Pietrasiewicz if (u.r.bRequestType 165490fccb52SAndrzej Pietrasiewicz != (USB_DIR_IN|USB_RECIP_INTERFACE)) 165590fccb52SAndrzej Pietrasiewicz goto delegate; 165690fccb52SAndrzej Pietrasiewicz 165790fccb52SAndrzej Pietrasiewicz zero_status: 165890fccb52SAndrzej Pietrasiewicz /* return two zero bytes */ 165990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); 166090fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_DATA); 166190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 166290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EP_DIR, UDC_EP_NUM); 166390fccb52SAndrzej Pietrasiewicz status = 0; 166490fccb52SAndrzej Pietrasiewicz VDBG("GET_STATUS, interface %d\n", w_index); 166590fccb52SAndrzej Pietrasiewicz /* next, status stage */ 166690fccb52SAndrzej Pietrasiewicz break; 166790fccb52SAndrzej Pietrasiewicz default: 166890fccb52SAndrzej Pietrasiewicz delegate: 166990fccb52SAndrzej Pietrasiewicz /* activate the ep0out fifo right away */ 167090fccb52SAndrzej Pietrasiewicz if (!udc->ep0_in && w_length) { 167190fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_NUM); 167290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 167390fccb52SAndrzej Pietrasiewicz } 167490fccb52SAndrzej Pietrasiewicz 167590fccb52SAndrzej Pietrasiewicz /* gadget drivers see class/vendor specific requests, 167690fccb52SAndrzej Pietrasiewicz * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, 167790fccb52SAndrzej Pietrasiewicz * and more 167890fccb52SAndrzej Pietrasiewicz */ 167990fccb52SAndrzej Pietrasiewicz VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n", 168090fccb52SAndrzej Pietrasiewicz u.r.bRequestType, u.r.bRequest, 168190fccb52SAndrzej Pietrasiewicz w_value, w_index, w_length); 168290fccb52SAndrzej Pietrasiewicz 168390fccb52SAndrzej Pietrasiewicz #undef w_value 168490fccb52SAndrzej Pietrasiewicz #undef w_index 168590fccb52SAndrzej Pietrasiewicz #undef w_length 168690fccb52SAndrzej Pietrasiewicz 168790fccb52SAndrzej Pietrasiewicz /* The gadget driver may return an error here, 168890fccb52SAndrzej Pietrasiewicz * causing an immediate protocol stall. 168990fccb52SAndrzej Pietrasiewicz * 169090fccb52SAndrzej Pietrasiewicz * Else it must issue a response, either queueing a 169190fccb52SAndrzej Pietrasiewicz * response buffer for the DATA stage, or halting ep0 169290fccb52SAndrzej Pietrasiewicz * (causing a protocol stall, not a real halt). A 169390fccb52SAndrzej Pietrasiewicz * zero length buffer means no DATA stage. 169490fccb52SAndrzej Pietrasiewicz * 169590fccb52SAndrzej Pietrasiewicz * It's fine to issue that response after the setup() 169690fccb52SAndrzej Pietrasiewicz * call returns, and this IRQ was handled. 169790fccb52SAndrzej Pietrasiewicz */ 169890fccb52SAndrzej Pietrasiewicz udc->ep0_setup = 1; 169990fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 170090fccb52SAndrzej Pietrasiewicz status = udc->driver->setup(&udc->gadget, &u.r); 170190fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 170290fccb52SAndrzej Pietrasiewicz udc->ep0_setup = 0; 170390fccb52SAndrzej Pietrasiewicz } 170490fccb52SAndrzej Pietrasiewicz 170590fccb52SAndrzej Pietrasiewicz if (status < 0) { 170690fccb52SAndrzej Pietrasiewicz do_stall: 170790fccb52SAndrzej Pietrasiewicz VDBG("req %02x.%02x protocol STALL; stat %d\n", 170890fccb52SAndrzej Pietrasiewicz u.r.bRequestType, u.r.bRequest, status); 170990fccb52SAndrzej Pietrasiewicz if (udc->ep0_set_config) { 171090fccb52SAndrzej Pietrasiewicz if (udc->ep0_reset_config) 171190fccb52SAndrzej Pietrasiewicz WARNING("error resetting config?\n"); 171290fccb52SAndrzej Pietrasiewicz else 171390fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CLR_CFG, UDC_SYSCON2); 171490fccb52SAndrzej Pietrasiewicz } 171590fccb52SAndrzej Pietrasiewicz omap_writew(UDC_STALL_CMD, UDC_SYSCON2); 171690fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 171790fccb52SAndrzej Pietrasiewicz } 171890fccb52SAndrzej Pietrasiewicz } 171990fccb52SAndrzej Pietrasiewicz } 172090fccb52SAndrzej Pietrasiewicz 172190fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 172290fccb52SAndrzej Pietrasiewicz 172390fccb52SAndrzej Pietrasiewicz #define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT) 172490fccb52SAndrzej Pietrasiewicz 172590fccb52SAndrzej Pietrasiewicz static void devstate_irq(struct omap_udc *udc, u16 irq_src) 172690fccb52SAndrzej Pietrasiewicz { 172790fccb52SAndrzej Pietrasiewicz u16 devstat, change; 172890fccb52SAndrzej Pietrasiewicz 172990fccb52SAndrzej Pietrasiewicz devstat = omap_readw(UDC_DEVSTAT); 173090fccb52SAndrzej Pietrasiewicz change = devstat ^ udc->devstat; 173190fccb52SAndrzej Pietrasiewicz udc->devstat = devstat; 173290fccb52SAndrzej Pietrasiewicz 173390fccb52SAndrzej Pietrasiewicz if (change & (UDC_USB_RESET|UDC_ATT)) { 173490fccb52SAndrzej Pietrasiewicz udc_quiesce(udc); 173590fccb52SAndrzej Pietrasiewicz 173690fccb52SAndrzej Pietrasiewicz if (change & UDC_ATT) { 173790fccb52SAndrzej Pietrasiewicz /* driver for any external transceiver will 173890fccb52SAndrzej Pietrasiewicz * have called omap_vbus_session() already 173990fccb52SAndrzej Pietrasiewicz */ 174090fccb52SAndrzej Pietrasiewicz if (devstat & UDC_ATT) { 174190fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_FULL; 174290fccb52SAndrzej Pietrasiewicz VDBG("connect\n"); 174390fccb52SAndrzej Pietrasiewicz if (IS_ERR_OR_NULL(udc->transceiver)) 174490fccb52SAndrzej Pietrasiewicz pullup_enable(udc); 174590fccb52SAndrzej Pietrasiewicz /* if (driver->connect) call it */ 174690fccb52SAndrzej Pietrasiewicz } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { 174790fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_UNKNOWN; 174890fccb52SAndrzej Pietrasiewicz if (IS_ERR_OR_NULL(udc->transceiver)) 174990fccb52SAndrzej Pietrasiewicz pullup_disable(udc); 175090fccb52SAndrzej Pietrasiewicz DBG("disconnect, gadget %s\n", 175190fccb52SAndrzej Pietrasiewicz udc->driver->driver.name); 175290fccb52SAndrzej Pietrasiewicz if (udc->driver->disconnect) { 175390fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 175490fccb52SAndrzej Pietrasiewicz udc->driver->disconnect(&udc->gadget); 175590fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 175690fccb52SAndrzej Pietrasiewicz } 175790fccb52SAndrzej Pietrasiewicz } 175890fccb52SAndrzej Pietrasiewicz change &= ~UDC_ATT; 175990fccb52SAndrzej Pietrasiewicz } 176090fccb52SAndrzej Pietrasiewicz 176190fccb52SAndrzej Pietrasiewicz if (change & UDC_USB_RESET) { 176290fccb52SAndrzej Pietrasiewicz if (devstat & UDC_USB_RESET) { 176390fccb52SAndrzej Pietrasiewicz VDBG("RESET=1\n"); 176490fccb52SAndrzej Pietrasiewicz } else { 176590fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_FULL; 176690fccb52SAndrzej Pietrasiewicz INFO("USB reset done, gadget %s\n", 176790fccb52SAndrzej Pietrasiewicz udc->driver->driver.name); 176890fccb52SAndrzej Pietrasiewicz /* ep0 traffic is legal from now on */ 176990fccb52SAndrzej Pietrasiewicz omap_writew(UDC_DS_CHG_IE | UDC_EP0_IE, 177090fccb52SAndrzej Pietrasiewicz UDC_IRQ_EN); 177190fccb52SAndrzej Pietrasiewicz } 177290fccb52SAndrzej Pietrasiewicz change &= ~UDC_USB_RESET; 177390fccb52SAndrzej Pietrasiewicz } 177490fccb52SAndrzej Pietrasiewicz } 177590fccb52SAndrzej Pietrasiewicz if (change & UDC_SUS) { 177690fccb52SAndrzej Pietrasiewicz if (udc->gadget.speed != USB_SPEED_UNKNOWN) { 177790fccb52SAndrzej Pietrasiewicz /* FIXME tell isp1301 to suspend/resume (?) */ 177890fccb52SAndrzej Pietrasiewicz if (devstat & UDC_SUS) { 177990fccb52SAndrzej Pietrasiewicz VDBG("suspend\n"); 178090fccb52SAndrzej Pietrasiewicz update_otg(udc); 178190fccb52SAndrzej Pietrasiewicz /* HNP could be under way already */ 178290fccb52SAndrzej Pietrasiewicz if (udc->gadget.speed == USB_SPEED_FULL 178390fccb52SAndrzej Pietrasiewicz && udc->driver->suspend) { 178490fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 178590fccb52SAndrzej Pietrasiewicz udc->driver->suspend(&udc->gadget); 178690fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 178790fccb52SAndrzej Pietrasiewicz } 178890fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(udc->transceiver)) 178990fccb52SAndrzej Pietrasiewicz usb_phy_set_suspend( 179090fccb52SAndrzej Pietrasiewicz udc->transceiver, 1); 179190fccb52SAndrzej Pietrasiewicz } else { 179290fccb52SAndrzej Pietrasiewicz VDBG("resume\n"); 179390fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(udc->transceiver)) 179490fccb52SAndrzej Pietrasiewicz usb_phy_set_suspend( 179590fccb52SAndrzej Pietrasiewicz udc->transceiver, 0); 179690fccb52SAndrzej Pietrasiewicz if (udc->gadget.speed == USB_SPEED_FULL 179790fccb52SAndrzej Pietrasiewicz && udc->driver->resume) { 179890fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 179990fccb52SAndrzej Pietrasiewicz udc->driver->resume(&udc->gadget); 180090fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 180190fccb52SAndrzej Pietrasiewicz } 180290fccb52SAndrzej Pietrasiewicz } 180390fccb52SAndrzej Pietrasiewicz } 180490fccb52SAndrzej Pietrasiewicz change &= ~UDC_SUS; 180590fccb52SAndrzej Pietrasiewicz } 180690fccb52SAndrzej Pietrasiewicz if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) { 180790fccb52SAndrzej Pietrasiewicz update_otg(udc); 180890fccb52SAndrzej Pietrasiewicz change &= ~OTG_FLAGS; 180990fccb52SAndrzej Pietrasiewicz } 181090fccb52SAndrzej Pietrasiewicz 181190fccb52SAndrzej Pietrasiewicz change &= ~(UDC_CFG|UDC_DEF|UDC_ADD); 181290fccb52SAndrzej Pietrasiewicz if (change) 181390fccb52SAndrzej Pietrasiewicz VDBG("devstat %03x, ignore change %03x\n", 181490fccb52SAndrzej Pietrasiewicz devstat, change); 181590fccb52SAndrzej Pietrasiewicz 181690fccb52SAndrzej Pietrasiewicz omap_writew(UDC_DS_CHG, UDC_IRQ_SRC); 181790fccb52SAndrzej Pietrasiewicz } 181890fccb52SAndrzej Pietrasiewicz 181990fccb52SAndrzej Pietrasiewicz static irqreturn_t omap_udc_irq(int irq, void *_udc) 182090fccb52SAndrzej Pietrasiewicz { 182190fccb52SAndrzej Pietrasiewicz struct omap_udc *udc = _udc; 182290fccb52SAndrzej Pietrasiewicz u16 irq_src; 182390fccb52SAndrzej Pietrasiewicz irqreturn_t status = IRQ_NONE; 182490fccb52SAndrzej Pietrasiewicz unsigned long flags; 182590fccb52SAndrzej Pietrasiewicz 182690fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 182790fccb52SAndrzej Pietrasiewicz irq_src = omap_readw(UDC_IRQ_SRC); 182890fccb52SAndrzej Pietrasiewicz 182990fccb52SAndrzej Pietrasiewicz /* Device state change (usb ch9 stuff) */ 183090fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_DS_CHG) { 183190fccb52SAndrzej Pietrasiewicz devstate_irq(_udc, irq_src); 183290fccb52SAndrzej Pietrasiewicz status = IRQ_HANDLED; 183390fccb52SAndrzej Pietrasiewicz irq_src &= ~UDC_DS_CHG; 183490fccb52SAndrzej Pietrasiewicz } 183590fccb52SAndrzej Pietrasiewicz 183690fccb52SAndrzej Pietrasiewicz /* EP0 control transfers */ 183790fccb52SAndrzej Pietrasiewicz if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) { 183890fccb52SAndrzej Pietrasiewicz ep0_irq(_udc, irq_src); 183990fccb52SAndrzej Pietrasiewicz status = IRQ_HANDLED; 184090fccb52SAndrzej Pietrasiewicz irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX); 184190fccb52SAndrzej Pietrasiewicz } 184290fccb52SAndrzej Pietrasiewicz 184390fccb52SAndrzej Pietrasiewicz /* DMA transfer completion */ 184490fccb52SAndrzej Pietrasiewicz if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) { 184590fccb52SAndrzej Pietrasiewicz dma_irq(_udc, irq_src); 184690fccb52SAndrzej Pietrasiewicz status = IRQ_HANDLED; 184790fccb52SAndrzej Pietrasiewicz irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT); 184890fccb52SAndrzej Pietrasiewicz } 184990fccb52SAndrzej Pietrasiewicz 185090fccb52SAndrzej Pietrasiewicz irq_src &= ~(UDC_IRQ_SOF | UDC_EPN_TX|UDC_EPN_RX); 185190fccb52SAndrzej Pietrasiewicz if (irq_src) 185290fccb52SAndrzej Pietrasiewicz DBG("udc_irq, unhandled %03x\n", irq_src); 185390fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 185490fccb52SAndrzej Pietrasiewicz 185590fccb52SAndrzej Pietrasiewicz return status; 185690fccb52SAndrzej Pietrasiewicz } 185790fccb52SAndrzej Pietrasiewicz 185890fccb52SAndrzej Pietrasiewicz /* workaround for seemingly-lost IRQs for RX ACKs... */ 185990fccb52SAndrzej Pietrasiewicz #define PIO_OUT_TIMEOUT (jiffies + HZ/3) 186090fccb52SAndrzej Pietrasiewicz #define HALF_FULL(f) (!((f)&(UDC_NON_ISO_FIFO_FULL|UDC_NON_ISO_FIFO_EMPTY))) 186190fccb52SAndrzej Pietrasiewicz 1862e99e88a9SKees Cook static void pio_out_timer(struct timer_list *t) 186390fccb52SAndrzej Pietrasiewicz { 1864e99e88a9SKees Cook struct omap_ep *ep = from_timer(ep, t, timer); 186590fccb52SAndrzej Pietrasiewicz unsigned long flags; 186690fccb52SAndrzej Pietrasiewicz u16 stat_flg; 186790fccb52SAndrzej Pietrasiewicz 186890fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->udc->lock, flags); 186990fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue) && ep->ackwait) { 187090fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 187190fccb52SAndrzej Pietrasiewicz stat_flg = omap_readw(UDC_STAT_FLG); 187290fccb52SAndrzej Pietrasiewicz 187390fccb52SAndrzej Pietrasiewicz if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN) 187490fccb52SAndrzej Pietrasiewicz || (ep->double_buf && HALF_FULL(stat_flg)))) { 187590fccb52SAndrzej Pietrasiewicz struct omap_req *req; 187690fccb52SAndrzej Pietrasiewicz 187790fccb52SAndrzej Pietrasiewicz VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg); 187890fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 187990fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 188090fccb52SAndrzej Pietrasiewicz (void) read_fifo(ep, req); 188190fccb52SAndrzej Pietrasiewicz omap_writew(ep->bEndpointAddress, UDC_EP_NUM); 188290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 188390fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 188490fccb52SAndrzej Pietrasiewicz } else 188590fccb52SAndrzej Pietrasiewicz deselect_ep(); 188690fccb52SAndrzej Pietrasiewicz } 188790fccb52SAndrzej Pietrasiewicz mod_timer(&ep->timer, PIO_OUT_TIMEOUT); 188890fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 188990fccb52SAndrzej Pietrasiewicz } 189090fccb52SAndrzej Pietrasiewicz 189190fccb52SAndrzej Pietrasiewicz static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) 189290fccb52SAndrzej Pietrasiewicz { 189390fccb52SAndrzej Pietrasiewicz u16 epn_stat, irq_src; 189490fccb52SAndrzej Pietrasiewicz irqreturn_t status = IRQ_NONE; 189590fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 189690fccb52SAndrzej Pietrasiewicz int epnum; 189790fccb52SAndrzej Pietrasiewicz struct omap_udc *udc = _dev; 189890fccb52SAndrzej Pietrasiewicz struct omap_req *req; 189990fccb52SAndrzej Pietrasiewicz unsigned long flags; 190090fccb52SAndrzej Pietrasiewicz 190190fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 190290fccb52SAndrzej Pietrasiewicz epn_stat = omap_readw(UDC_EPN_STAT); 190390fccb52SAndrzej Pietrasiewicz irq_src = omap_readw(UDC_IRQ_SRC); 190490fccb52SAndrzej Pietrasiewicz 190590fccb52SAndrzej Pietrasiewicz /* handle OUT first, to avoid some wasteful NAKs */ 190690fccb52SAndrzej Pietrasiewicz if (irq_src & UDC_EPN_RX) { 190790fccb52SAndrzej Pietrasiewicz epnum = (epn_stat >> 8) & 0x0f; 190890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EPN_RX, UDC_IRQ_SRC); 190990fccb52SAndrzej Pietrasiewicz status = IRQ_HANDLED; 191090fccb52SAndrzej Pietrasiewicz ep = &udc->ep[epnum]; 191190fccb52SAndrzej Pietrasiewicz ep->irqs++; 191290fccb52SAndrzej Pietrasiewicz 191390fccb52SAndrzej Pietrasiewicz omap_writew(epnum | UDC_EP_SEL, UDC_EP_NUM); 191490fccb52SAndrzej Pietrasiewicz ep->fnf = 0; 191590fccb52SAndrzej Pietrasiewicz if (omap_readw(UDC_STAT_FLG) & UDC_ACK) { 191690fccb52SAndrzej Pietrasiewicz ep->ackwait--; 191790fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 191890fccb52SAndrzej Pietrasiewicz int stat; 191990fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 192090fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 192190fccb52SAndrzej Pietrasiewicz stat = read_fifo(ep, req); 192290fccb52SAndrzej Pietrasiewicz if (!ep->double_buf) 192390fccb52SAndrzej Pietrasiewicz ep->fnf = 1; 192490fccb52SAndrzej Pietrasiewicz } 192590fccb52SAndrzej Pietrasiewicz } 192690fccb52SAndrzej Pietrasiewicz /* min 6 clock delay before clearing EP_SEL ... */ 192790fccb52SAndrzej Pietrasiewicz epn_stat = omap_readw(UDC_EPN_STAT); 192890fccb52SAndrzej Pietrasiewicz epn_stat = omap_readw(UDC_EPN_STAT); 192990fccb52SAndrzej Pietrasiewicz omap_writew(epnum, UDC_EP_NUM); 193090fccb52SAndrzej Pietrasiewicz 193190fccb52SAndrzej Pietrasiewicz /* enabling fifo _after_ clearing ACK, contrary to docs, 193290fccb52SAndrzej Pietrasiewicz * reduces lossage; timer still needed though (sigh). 193390fccb52SAndrzej Pietrasiewicz */ 193490fccb52SAndrzej Pietrasiewicz if (ep->fnf) { 193590fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); 193690fccb52SAndrzej Pietrasiewicz ep->ackwait = 1 + ep->double_buf; 193790fccb52SAndrzej Pietrasiewicz } 193890fccb52SAndrzej Pietrasiewicz mod_timer(&ep->timer, PIO_OUT_TIMEOUT); 193990fccb52SAndrzej Pietrasiewicz } 194090fccb52SAndrzej Pietrasiewicz 194190fccb52SAndrzej Pietrasiewicz /* then IN transfers */ 194290fccb52SAndrzej Pietrasiewicz else if (irq_src & UDC_EPN_TX) { 194390fccb52SAndrzej Pietrasiewicz epnum = epn_stat & 0x0f; 194490fccb52SAndrzej Pietrasiewicz omap_writew(UDC_EPN_TX, UDC_IRQ_SRC); 194590fccb52SAndrzej Pietrasiewicz status = IRQ_HANDLED; 194690fccb52SAndrzej Pietrasiewicz ep = &udc->ep[16 + epnum]; 194790fccb52SAndrzej Pietrasiewicz ep->irqs++; 194890fccb52SAndrzej Pietrasiewicz 194990fccb52SAndrzej Pietrasiewicz omap_writew(epnum | UDC_EP_DIR | UDC_EP_SEL, UDC_EP_NUM); 195090fccb52SAndrzej Pietrasiewicz if (omap_readw(UDC_STAT_FLG) & UDC_ACK) { 195190fccb52SAndrzej Pietrasiewicz ep->ackwait = 0; 195290fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 195390fccb52SAndrzej Pietrasiewicz req = container_of(ep->queue.next, 195490fccb52SAndrzej Pietrasiewicz struct omap_req, queue); 195590fccb52SAndrzej Pietrasiewicz (void) write_fifo(ep, req); 195690fccb52SAndrzej Pietrasiewicz } 195790fccb52SAndrzej Pietrasiewicz } 195890fccb52SAndrzej Pietrasiewicz /* min 6 clock delay before clearing EP_SEL ... */ 195990fccb52SAndrzej Pietrasiewicz epn_stat = omap_readw(UDC_EPN_STAT); 196090fccb52SAndrzej Pietrasiewicz epn_stat = omap_readw(UDC_EPN_STAT); 196190fccb52SAndrzej Pietrasiewicz omap_writew(epnum | UDC_EP_DIR, UDC_EP_NUM); 196290fccb52SAndrzej Pietrasiewicz /* then 6 clocks before it'd tx */ 196390fccb52SAndrzej Pietrasiewicz } 196490fccb52SAndrzej Pietrasiewicz 196590fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 196690fccb52SAndrzej Pietrasiewicz return status; 196790fccb52SAndrzej Pietrasiewicz } 196890fccb52SAndrzej Pietrasiewicz 196990fccb52SAndrzej Pietrasiewicz #ifdef USE_ISO 197090fccb52SAndrzej Pietrasiewicz static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) 197190fccb52SAndrzej Pietrasiewicz { 197290fccb52SAndrzej Pietrasiewicz struct omap_udc *udc = _dev; 197390fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 197490fccb52SAndrzej Pietrasiewicz int pending = 0; 197590fccb52SAndrzej Pietrasiewicz unsigned long flags; 197690fccb52SAndrzej Pietrasiewicz 197790fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 197890fccb52SAndrzej Pietrasiewicz 197990fccb52SAndrzej Pietrasiewicz /* handle all non-DMA ISO transfers */ 198090fccb52SAndrzej Pietrasiewicz list_for_each_entry(ep, &udc->iso, iso) { 198190fccb52SAndrzej Pietrasiewicz u16 stat; 198290fccb52SAndrzej Pietrasiewicz struct omap_req *req; 198390fccb52SAndrzej Pietrasiewicz 198490fccb52SAndrzej Pietrasiewicz if (ep->has_dma || list_empty(&ep->queue)) 198590fccb52SAndrzej Pietrasiewicz continue; 198690fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct omap_req, queue); 198790fccb52SAndrzej Pietrasiewicz 198890fccb52SAndrzej Pietrasiewicz use_ep(ep, UDC_EP_SEL); 198990fccb52SAndrzej Pietrasiewicz stat = omap_readw(UDC_STAT_FLG); 199090fccb52SAndrzej Pietrasiewicz 199190fccb52SAndrzej Pietrasiewicz /* NOTE: like the other controller drivers, this isn't 199290fccb52SAndrzej Pietrasiewicz * currently reporting lost or damaged frames. 199390fccb52SAndrzej Pietrasiewicz */ 199490fccb52SAndrzej Pietrasiewicz if (ep->bEndpointAddress & USB_DIR_IN) { 199590fccb52SAndrzej Pietrasiewicz if (stat & UDC_MISS_IN) 199690fccb52SAndrzej Pietrasiewicz /* done(ep, req, -EPROTO) */; 199790fccb52SAndrzej Pietrasiewicz else 199890fccb52SAndrzej Pietrasiewicz write_fifo(ep, req); 199990fccb52SAndrzej Pietrasiewicz } else { 200090fccb52SAndrzej Pietrasiewicz int status = 0; 200190fccb52SAndrzej Pietrasiewicz 200290fccb52SAndrzej Pietrasiewicz if (stat & UDC_NO_RXPACKET) 200390fccb52SAndrzej Pietrasiewicz status = -EREMOTEIO; 200490fccb52SAndrzej Pietrasiewicz else if (stat & UDC_ISO_ERR) 200590fccb52SAndrzej Pietrasiewicz status = -EILSEQ; 200690fccb52SAndrzej Pietrasiewicz else if (stat & UDC_DATA_FLUSH) 200790fccb52SAndrzej Pietrasiewicz status = -ENOSR; 200890fccb52SAndrzej Pietrasiewicz 200990fccb52SAndrzej Pietrasiewicz if (status) 201090fccb52SAndrzej Pietrasiewicz /* done(ep, req, status) */; 201190fccb52SAndrzej Pietrasiewicz else 201290fccb52SAndrzej Pietrasiewicz read_fifo(ep, req); 201390fccb52SAndrzej Pietrasiewicz } 201490fccb52SAndrzej Pietrasiewicz deselect_ep(); 201590fccb52SAndrzej Pietrasiewicz /* 6 wait states before next EP */ 201690fccb52SAndrzej Pietrasiewicz 201790fccb52SAndrzej Pietrasiewicz ep->irqs++; 201890fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) 201990fccb52SAndrzej Pietrasiewicz pending = 1; 202090fccb52SAndrzej Pietrasiewicz } 202190fccb52SAndrzej Pietrasiewicz if (!pending) { 202290fccb52SAndrzej Pietrasiewicz u16 w; 202390fccb52SAndrzej Pietrasiewicz 202490fccb52SAndrzej Pietrasiewicz w = omap_readw(UDC_IRQ_EN); 202590fccb52SAndrzej Pietrasiewicz w &= ~UDC_SOF_IE; 202690fccb52SAndrzej Pietrasiewicz omap_writew(w, UDC_IRQ_EN); 202790fccb52SAndrzej Pietrasiewicz } 202890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_IRQ_SOF, UDC_IRQ_SRC); 202990fccb52SAndrzej Pietrasiewicz 203090fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 203190fccb52SAndrzej Pietrasiewicz return IRQ_HANDLED; 203290fccb52SAndrzej Pietrasiewicz } 203390fccb52SAndrzej Pietrasiewicz #endif 203490fccb52SAndrzej Pietrasiewicz 203590fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 203690fccb52SAndrzej Pietrasiewicz 203790fccb52SAndrzej Pietrasiewicz static inline int machine_without_vbus_sense(void) 203890fccb52SAndrzej Pietrasiewicz { 2039*858a74cbSAaro Koskinen return machine_is_omap_osk() || machine_is_omap_palmte() || 2040*858a74cbSAaro Koskinen machine_is_sx1(); 204190fccb52SAndrzej Pietrasiewicz } 204290fccb52SAndrzej Pietrasiewicz 204390fccb52SAndrzej Pietrasiewicz static int omap_udc_start(struct usb_gadget *g, 204490fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver) 204590fccb52SAndrzej Pietrasiewicz { 20466ca6695fSAaro Koskinen int status; 204790fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 204890fccb52SAndrzej Pietrasiewicz unsigned long flags; 204990fccb52SAndrzej Pietrasiewicz 205090fccb52SAndrzej Pietrasiewicz 205190fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 205290fccb52SAndrzej Pietrasiewicz /* reset state */ 205390fccb52SAndrzej Pietrasiewicz list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { 205490fccb52SAndrzej Pietrasiewicz ep->irqs = 0; 205590fccb52SAndrzej Pietrasiewicz if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) 205690fccb52SAndrzej Pietrasiewicz continue; 205790fccb52SAndrzej Pietrasiewicz use_ep(ep, 0); 205890fccb52SAndrzej Pietrasiewicz omap_writew(UDC_SET_HALT, UDC_CTRL); 205990fccb52SAndrzej Pietrasiewicz } 206090fccb52SAndrzej Pietrasiewicz udc->ep0_pending = 0; 206190fccb52SAndrzej Pietrasiewicz udc->ep[0].irqs = 0; 206290fccb52SAndrzej Pietrasiewicz udc->softconnect = 1; 206390fccb52SAndrzej Pietrasiewicz 206490fccb52SAndrzej Pietrasiewicz /* hook up the driver */ 206590fccb52SAndrzej Pietrasiewicz udc->driver = driver; 206690fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 206790fccb52SAndrzej Pietrasiewicz 206890fccb52SAndrzej Pietrasiewicz if (udc->dc_clk != NULL) 206990fccb52SAndrzej Pietrasiewicz omap_udc_enable_clock(1); 207090fccb52SAndrzej Pietrasiewicz 207190fccb52SAndrzej Pietrasiewicz omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); 207290fccb52SAndrzej Pietrasiewicz 207390fccb52SAndrzej Pietrasiewicz /* connect to bus through transceiver */ 207490fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(udc->transceiver)) { 207590fccb52SAndrzej Pietrasiewicz status = otg_set_peripheral(udc->transceiver->otg, 207690fccb52SAndrzej Pietrasiewicz &udc->gadget); 207790fccb52SAndrzej Pietrasiewicz if (status < 0) { 207890fccb52SAndrzej Pietrasiewicz ERR("can't bind to transceiver\n"); 207990fccb52SAndrzej Pietrasiewicz udc->driver = NULL; 208090fccb52SAndrzej Pietrasiewicz goto done; 208190fccb52SAndrzej Pietrasiewicz } 208290fccb52SAndrzej Pietrasiewicz } else { 20836ca6695fSAaro Koskinen status = 0; 208490fccb52SAndrzej Pietrasiewicz if (can_pullup(udc)) 208590fccb52SAndrzej Pietrasiewicz pullup_enable(udc); 208690fccb52SAndrzej Pietrasiewicz else 208790fccb52SAndrzej Pietrasiewicz pullup_disable(udc); 208890fccb52SAndrzej Pietrasiewicz } 208990fccb52SAndrzej Pietrasiewicz 209090fccb52SAndrzej Pietrasiewicz /* boards that don't have VBUS sensing can't autogate 48MHz; 209190fccb52SAndrzej Pietrasiewicz * can't enter deep sleep while a gadget driver is active. 209290fccb52SAndrzej Pietrasiewicz */ 209390fccb52SAndrzej Pietrasiewicz if (machine_without_vbus_sense()) 209490fccb52SAndrzej Pietrasiewicz omap_vbus_session(&udc->gadget, 1); 209590fccb52SAndrzej Pietrasiewicz 209690fccb52SAndrzej Pietrasiewicz done: 209790fccb52SAndrzej Pietrasiewicz if (udc->dc_clk != NULL) 209890fccb52SAndrzej Pietrasiewicz omap_udc_enable_clock(0); 209990fccb52SAndrzej Pietrasiewicz 210090fccb52SAndrzej Pietrasiewicz return status; 210190fccb52SAndrzej Pietrasiewicz } 210290fccb52SAndrzej Pietrasiewicz 210322835b80SFelipe Balbi static int omap_udc_stop(struct usb_gadget *g) 210490fccb52SAndrzej Pietrasiewicz { 210590fccb52SAndrzej Pietrasiewicz unsigned long flags; 210690fccb52SAndrzej Pietrasiewicz 210790fccb52SAndrzej Pietrasiewicz if (udc->dc_clk != NULL) 210890fccb52SAndrzej Pietrasiewicz omap_udc_enable_clock(1); 210990fccb52SAndrzej Pietrasiewicz 211090fccb52SAndrzej Pietrasiewicz if (machine_without_vbus_sense()) 211190fccb52SAndrzej Pietrasiewicz omap_vbus_session(&udc->gadget, 0); 211290fccb52SAndrzej Pietrasiewicz 211390fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(udc->transceiver)) 211490fccb52SAndrzej Pietrasiewicz (void) otg_set_peripheral(udc->transceiver->otg, NULL); 211590fccb52SAndrzej Pietrasiewicz else 211690fccb52SAndrzej Pietrasiewicz pullup_disable(udc); 211790fccb52SAndrzej Pietrasiewicz 211890fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 211990fccb52SAndrzej Pietrasiewicz udc_quiesce(udc); 212090fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 212190fccb52SAndrzej Pietrasiewicz 212290fccb52SAndrzej Pietrasiewicz udc->driver = NULL; 212390fccb52SAndrzej Pietrasiewicz 212490fccb52SAndrzej Pietrasiewicz if (udc->dc_clk != NULL) 212590fccb52SAndrzej Pietrasiewicz omap_udc_enable_clock(0); 212690fccb52SAndrzej Pietrasiewicz 212748f5e749SHariprasad Kelam return 0; 212890fccb52SAndrzej Pietrasiewicz } 212990fccb52SAndrzej Pietrasiewicz 213090fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 213190fccb52SAndrzej Pietrasiewicz 213290fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 213390fccb52SAndrzej Pietrasiewicz 213490fccb52SAndrzej Pietrasiewicz #include <linux/seq_file.h> 213590fccb52SAndrzej Pietrasiewicz 213690fccb52SAndrzej Pietrasiewicz static const char proc_filename[] = "driver/udc"; 213790fccb52SAndrzej Pietrasiewicz 213890fccb52SAndrzej Pietrasiewicz #define FOURBITS "%s%s%s%s" 213990fccb52SAndrzej Pietrasiewicz #define EIGHTBITS "%s%s%s%s%s%s%s%s" 214090fccb52SAndrzej Pietrasiewicz 214190fccb52SAndrzej Pietrasiewicz static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) 214290fccb52SAndrzej Pietrasiewicz { 214390fccb52SAndrzej Pietrasiewicz u16 stat_flg; 214490fccb52SAndrzej Pietrasiewicz struct omap_req *req; 214590fccb52SAndrzej Pietrasiewicz char buf[20]; 214690fccb52SAndrzej Pietrasiewicz 214790fccb52SAndrzej Pietrasiewicz use_ep(ep, 0); 214890fccb52SAndrzej Pietrasiewicz 214990fccb52SAndrzej Pietrasiewicz if (use_dma && ep->has_dma) 215090fccb52SAndrzej Pietrasiewicz snprintf(buf, sizeof buf, "(%cxdma%d lch%d) ", 215190fccb52SAndrzej Pietrasiewicz (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', 215290fccb52SAndrzej Pietrasiewicz ep->dma_channel - 1, ep->lch); 215390fccb52SAndrzej Pietrasiewicz else 215490fccb52SAndrzej Pietrasiewicz buf[0] = 0; 215590fccb52SAndrzej Pietrasiewicz 215690fccb52SAndrzej Pietrasiewicz stat_flg = omap_readw(UDC_STAT_FLG); 215790fccb52SAndrzej Pietrasiewicz seq_printf(s, 215890fccb52SAndrzej Pietrasiewicz "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", 215990fccb52SAndrzej Pietrasiewicz ep->name, buf, 216090fccb52SAndrzej Pietrasiewicz ep->double_buf ? "dbuf " : "", 216190fccb52SAndrzej Pietrasiewicz ({ char *s; 216290fccb52SAndrzej Pietrasiewicz switch (ep->ackwait) { 216390fccb52SAndrzej Pietrasiewicz case 0: 216490fccb52SAndrzej Pietrasiewicz s = ""; 216590fccb52SAndrzej Pietrasiewicz break; 216690fccb52SAndrzej Pietrasiewicz case 1: 216790fccb52SAndrzej Pietrasiewicz s = "(ackw) "; 216890fccb52SAndrzej Pietrasiewicz break; 216990fccb52SAndrzej Pietrasiewicz case 2: 217090fccb52SAndrzej Pietrasiewicz s = "(ackw2) "; 217190fccb52SAndrzej Pietrasiewicz break; 217290fccb52SAndrzej Pietrasiewicz default: 217390fccb52SAndrzej Pietrasiewicz s = "(?) "; 217490fccb52SAndrzej Pietrasiewicz break; 217590fccb52SAndrzej Pietrasiewicz } s; }), 217690fccb52SAndrzej Pietrasiewicz ep->irqs, stat_flg, 217790fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "", 217890fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_MISS_IN) ? "miss_in " : "", 217990fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_DATA_FLUSH) ? "data_flush " : "", 218090fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_ISO_ERR) ? "iso_err " : "", 218190fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_ISO_FIFO_EMPTY) ? "iso_fifo_empty " : "", 218290fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_ISO_FIFO_FULL) ? "iso_fifo_full " : "", 218390fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_EP_HALTED) ? "HALT " : "", 218490fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_STALL) ? "STALL " : "", 218590fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_NAK) ? "NAK " : "", 218690fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_ACK) ? "ACK " : "", 218790fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_FIFO_EN) ? "fifo_en " : "", 218890fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "", 218990fccb52SAndrzej Pietrasiewicz (stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : ""); 219090fccb52SAndrzej Pietrasiewicz 219190fccb52SAndrzej Pietrasiewicz if (list_empty(&ep->queue)) 219290fccb52SAndrzej Pietrasiewicz seq_printf(s, "\t(queue empty)\n"); 219390fccb52SAndrzej Pietrasiewicz else 219490fccb52SAndrzej Pietrasiewicz list_for_each_entry(req, &ep->queue, queue) { 219590fccb52SAndrzej Pietrasiewicz unsigned length = req->req.actual; 219690fccb52SAndrzej Pietrasiewicz 219790fccb52SAndrzej Pietrasiewicz if (use_dma && buf[0]) { 219890fccb52SAndrzej Pietrasiewicz length += ((ep->bEndpointAddress & USB_DIR_IN) 219990fccb52SAndrzej Pietrasiewicz ? dma_src_len : dma_dest_len) 220090fccb52SAndrzej Pietrasiewicz (ep, req->req.dma + length); 220190fccb52SAndrzej Pietrasiewicz buf[0] = 0; 220290fccb52SAndrzej Pietrasiewicz } 220390fccb52SAndrzej Pietrasiewicz seq_printf(s, "\treq %p len %d/%d buf %p\n", 220490fccb52SAndrzej Pietrasiewicz &req->req, length, 220590fccb52SAndrzej Pietrasiewicz req->req.length, req->req.buf); 220690fccb52SAndrzej Pietrasiewicz } 220790fccb52SAndrzej Pietrasiewicz } 220890fccb52SAndrzej Pietrasiewicz 220990fccb52SAndrzej Pietrasiewicz static char *trx_mode(unsigned m, int enabled) 221090fccb52SAndrzej Pietrasiewicz { 221190fccb52SAndrzej Pietrasiewicz switch (m) { 221290fccb52SAndrzej Pietrasiewicz case 0: 221390fccb52SAndrzej Pietrasiewicz return enabled ? "*6wire" : "unused"; 221490fccb52SAndrzej Pietrasiewicz case 1: 221590fccb52SAndrzej Pietrasiewicz return "4wire"; 221690fccb52SAndrzej Pietrasiewicz case 2: 221790fccb52SAndrzej Pietrasiewicz return "3wire"; 221890fccb52SAndrzej Pietrasiewicz case 3: 221990fccb52SAndrzej Pietrasiewicz return "6wire"; 222090fccb52SAndrzej Pietrasiewicz default: 222190fccb52SAndrzej Pietrasiewicz return "unknown"; 222290fccb52SAndrzej Pietrasiewicz } 222390fccb52SAndrzej Pietrasiewicz } 222490fccb52SAndrzej Pietrasiewicz 222590fccb52SAndrzej Pietrasiewicz static int proc_otg_show(struct seq_file *s) 222690fccb52SAndrzej Pietrasiewicz { 222790fccb52SAndrzej Pietrasiewicz u32 tmp; 222890fccb52SAndrzej Pietrasiewicz u32 trans = 0; 222990fccb52SAndrzej Pietrasiewicz char *ctrl_name = "(UNKNOWN)"; 223090fccb52SAndrzej Pietrasiewicz 223190fccb52SAndrzej Pietrasiewicz tmp = omap_readl(OTG_REV); 22324b833fb3SColin Ian King ctrl_name = "transceiver_ctrl"; 223390fccb52SAndrzej Pietrasiewicz trans = omap_readw(USB_TRANSCEIVER_CTRL); 223490fccb52SAndrzej Pietrasiewicz seq_printf(s, "\nOTG rev %d.%d, %s %05x\n", 223590fccb52SAndrzej Pietrasiewicz tmp >> 4, tmp & 0xf, ctrl_name, trans); 223690fccb52SAndrzej Pietrasiewicz tmp = omap_readw(OTG_SYSCON_1); 223790fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," 223890fccb52SAndrzej Pietrasiewicz FOURBITS "\n", tmp, 223990fccb52SAndrzej Pietrasiewicz trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R), 224090fccb52SAndrzej Pietrasiewicz trx_mode(USB1_TRX_MODE(tmp), trans & CONF_USB1_UNI_R), 224190fccb52SAndrzej Pietrasiewicz (USB0_TRX_MODE(tmp) == 0 && !cpu_is_omap1710()) 224290fccb52SAndrzej Pietrasiewicz ? "internal" 224390fccb52SAndrzej Pietrasiewicz : trx_mode(USB0_TRX_MODE(tmp), 1), 224490fccb52SAndrzej Pietrasiewicz (tmp & OTG_IDLE_EN) ? " !otg" : "", 224590fccb52SAndrzej Pietrasiewicz (tmp & HST_IDLE_EN) ? " !host" : "", 224690fccb52SAndrzej Pietrasiewicz (tmp & DEV_IDLE_EN) ? " !dev" : "", 224790fccb52SAndrzej Pietrasiewicz (tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active"); 224890fccb52SAndrzej Pietrasiewicz tmp = omap_readl(OTG_SYSCON_2); 224990fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS 225090fccb52SAndrzej Pietrasiewicz " b_ase_brst=%d hmc=%d\n", tmp, 225190fccb52SAndrzej Pietrasiewicz (tmp & OTG_EN) ? " otg_en" : "", 225290fccb52SAndrzej Pietrasiewicz (tmp & USBX_SYNCHRO) ? " synchro" : "", 225390fccb52SAndrzej Pietrasiewicz /* much more SRP stuff */ 225490fccb52SAndrzej Pietrasiewicz (tmp & SRP_DATA) ? " srp_data" : "", 225590fccb52SAndrzej Pietrasiewicz (tmp & SRP_VBUS) ? " srp_vbus" : "", 225690fccb52SAndrzej Pietrasiewicz (tmp & OTG_PADEN) ? " otg_paden" : "", 225790fccb52SAndrzej Pietrasiewicz (tmp & HMC_PADEN) ? " hmc_paden" : "", 225890fccb52SAndrzej Pietrasiewicz (tmp & UHOST_EN) ? " uhost_en" : "", 225990fccb52SAndrzej Pietrasiewicz (tmp & HMC_TLLSPEED) ? " tllspeed" : "", 226090fccb52SAndrzej Pietrasiewicz (tmp & HMC_TLLATTACH) ? " tllattach" : "", 226190fccb52SAndrzej Pietrasiewicz B_ASE_BRST(tmp), 226290fccb52SAndrzej Pietrasiewicz OTG_HMC(tmp)); 226390fccb52SAndrzej Pietrasiewicz tmp = omap_readl(OTG_CTRL); 226490fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_ctrl %06x" EIGHTBITS EIGHTBITS "%s\n", tmp, 226590fccb52SAndrzej Pietrasiewicz (tmp & OTG_ASESSVLD) ? " asess" : "", 226690fccb52SAndrzej Pietrasiewicz (tmp & OTG_BSESSEND) ? " bsess_end" : "", 226790fccb52SAndrzej Pietrasiewicz (tmp & OTG_BSESSVLD) ? " bsess" : "", 226890fccb52SAndrzej Pietrasiewicz (tmp & OTG_VBUSVLD) ? " vbus" : "", 226990fccb52SAndrzej Pietrasiewicz (tmp & OTG_ID) ? " id" : "", 227090fccb52SAndrzej Pietrasiewicz (tmp & OTG_DRIVER_SEL) ? " DEVICE" : " HOST", 227190fccb52SAndrzej Pietrasiewicz (tmp & OTG_A_SETB_HNPEN) ? " a_setb_hnpen" : "", 227290fccb52SAndrzej Pietrasiewicz (tmp & OTG_A_BUSREQ) ? " a_bus" : "", 227390fccb52SAndrzej Pietrasiewicz (tmp & OTG_B_HNPEN) ? " b_hnpen" : "", 227490fccb52SAndrzej Pietrasiewicz (tmp & OTG_B_BUSREQ) ? " b_bus" : "", 227590fccb52SAndrzej Pietrasiewicz (tmp & OTG_BUSDROP) ? " busdrop" : "", 227690fccb52SAndrzej Pietrasiewicz (tmp & OTG_PULLDOWN) ? " down" : "", 227790fccb52SAndrzej Pietrasiewicz (tmp & OTG_PULLUP) ? " up" : "", 227890fccb52SAndrzej Pietrasiewicz (tmp & OTG_DRV_VBUS) ? " drv" : "", 227990fccb52SAndrzej Pietrasiewicz (tmp & OTG_PD_VBUS) ? " pd_vb" : "", 228090fccb52SAndrzej Pietrasiewicz (tmp & OTG_PU_VBUS) ? " pu_vb" : "", 228190fccb52SAndrzej Pietrasiewicz (tmp & OTG_PU_ID) ? " pu_id" : "" 228290fccb52SAndrzej Pietrasiewicz ); 228390fccb52SAndrzej Pietrasiewicz tmp = omap_readw(OTG_IRQ_EN); 228490fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_irq_en %04x" "\n", tmp); 228590fccb52SAndrzej Pietrasiewicz tmp = omap_readw(OTG_IRQ_SRC); 228690fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_irq_src %04x" "\n", tmp); 228790fccb52SAndrzej Pietrasiewicz tmp = omap_readw(OTG_OUTCTRL); 228890fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_outctrl %04x" "\n", tmp); 228990fccb52SAndrzej Pietrasiewicz tmp = omap_readw(OTG_TEST); 229090fccb52SAndrzej Pietrasiewicz seq_printf(s, "otg_test %04x" "\n", tmp); 229190fccb52SAndrzej Pietrasiewicz return 0; 229290fccb52SAndrzej Pietrasiewicz } 229390fccb52SAndrzej Pietrasiewicz 229490fccb52SAndrzej Pietrasiewicz static int proc_udc_show(struct seq_file *s, void *_) 229590fccb52SAndrzej Pietrasiewicz { 229690fccb52SAndrzej Pietrasiewicz u32 tmp; 229790fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 229890fccb52SAndrzej Pietrasiewicz unsigned long flags; 229990fccb52SAndrzej Pietrasiewicz 230090fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 230190fccb52SAndrzej Pietrasiewicz 230290fccb52SAndrzej Pietrasiewicz seq_printf(s, "%s, version: " DRIVER_VERSION 230390fccb52SAndrzej Pietrasiewicz #ifdef USE_ISO 230490fccb52SAndrzej Pietrasiewicz " (iso)" 230590fccb52SAndrzej Pietrasiewicz #endif 230690fccb52SAndrzej Pietrasiewicz "%s\n", 230790fccb52SAndrzej Pietrasiewicz driver_desc, 230890fccb52SAndrzej Pietrasiewicz use_dma ? " (dma)" : ""); 230990fccb52SAndrzej Pietrasiewicz 231090fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_REV) & 0xff; 231190fccb52SAndrzej Pietrasiewicz seq_printf(s, 231290fccb52SAndrzej Pietrasiewicz "UDC rev %d.%d, fifo mode %d, gadget %s\n" 231390fccb52SAndrzej Pietrasiewicz "hmc %d, transceiver %s\n", 231490fccb52SAndrzej Pietrasiewicz tmp >> 4, tmp & 0xf, 231590fccb52SAndrzej Pietrasiewicz fifo_mode, 231690fccb52SAndrzej Pietrasiewicz udc->driver ? udc->driver->driver.name : "(none)", 231790fccb52SAndrzej Pietrasiewicz HMC, 231890fccb52SAndrzej Pietrasiewicz udc->transceiver 231990fccb52SAndrzej Pietrasiewicz ? udc->transceiver->label 232090fccb52SAndrzej Pietrasiewicz : (cpu_is_omap1710() 232190fccb52SAndrzej Pietrasiewicz ? "external" : "(none)")); 232290fccb52SAndrzej Pietrasiewicz seq_printf(s, "ULPD control %04x req %04x status %04x\n", 232390fccb52SAndrzej Pietrasiewicz omap_readw(ULPD_CLOCK_CTRL), 232490fccb52SAndrzej Pietrasiewicz omap_readw(ULPD_SOFT_REQ), 232590fccb52SAndrzej Pietrasiewicz omap_readw(ULPD_STATUS_REQ)); 232690fccb52SAndrzej Pietrasiewicz 232790fccb52SAndrzej Pietrasiewicz /* OTG controller registers */ 232890fccb52SAndrzej Pietrasiewicz if (!cpu_is_omap15xx()) 232990fccb52SAndrzej Pietrasiewicz proc_otg_show(s); 233090fccb52SAndrzej Pietrasiewicz 233190fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_SYSCON1); 233290fccb52SAndrzej Pietrasiewicz seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, 233390fccb52SAndrzej Pietrasiewicz (tmp & UDC_CFG_LOCK) ? " cfg_lock" : "", 233490fccb52SAndrzej Pietrasiewicz (tmp & UDC_DATA_ENDIAN) ? " data_endian" : "", 233590fccb52SAndrzej Pietrasiewicz (tmp & UDC_DMA_ENDIAN) ? " dma_endian" : "", 233690fccb52SAndrzej Pietrasiewicz (tmp & UDC_NAK_EN) ? " nak" : "", 233790fccb52SAndrzej Pietrasiewicz (tmp & UDC_AUTODECODE_DIS) ? " autodecode_dis" : "", 233890fccb52SAndrzej Pietrasiewicz (tmp & UDC_SELF_PWR) ? " self_pwr" : "", 233990fccb52SAndrzej Pietrasiewicz (tmp & UDC_SOFF_DIS) ? " soff_dis" : "", 234090fccb52SAndrzej Pietrasiewicz (tmp & UDC_PULLUP_EN) ? " PULLUP" : ""); 234190fccb52SAndrzej Pietrasiewicz /* syscon2 is write-only */ 234290fccb52SAndrzej Pietrasiewicz 234390fccb52SAndrzej Pietrasiewicz /* UDC controller registers */ 234490fccb52SAndrzej Pietrasiewicz if (!(tmp & UDC_PULLUP_EN)) { 234590fccb52SAndrzej Pietrasiewicz seq_printf(s, "(suspended)\n"); 234690fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 234790fccb52SAndrzej Pietrasiewicz return 0; 234890fccb52SAndrzej Pietrasiewicz } 234990fccb52SAndrzej Pietrasiewicz 235090fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_DEVSTAT); 235190fccb52SAndrzej Pietrasiewicz seq_printf(s, "devstat %04x" EIGHTBITS "%s%s\n", tmp, 235290fccb52SAndrzej Pietrasiewicz (tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "", 235390fccb52SAndrzej Pietrasiewicz (tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "", 235490fccb52SAndrzej Pietrasiewicz (tmp & UDC_A_ALT_HNP_SUPPORT) ? " a_alt_hnp" : "", 235590fccb52SAndrzej Pietrasiewicz (tmp & UDC_R_WK_OK) ? " r_wk_ok" : "", 235690fccb52SAndrzej Pietrasiewicz (tmp & UDC_USB_RESET) ? " usb_reset" : "", 235790fccb52SAndrzej Pietrasiewicz (tmp & UDC_SUS) ? " SUS" : "", 235890fccb52SAndrzej Pietrasiewicz (tmp & UDC_CFG) ? " CFG" : "", 235990fccb52SAndrzej Pietrasiewicz (tmp & UDC_ADD) ? " ADD" : "", 236090fccb52SAndrzej Pietrasiewicz (tmp & UDC_DEF) ? " DEF" : "", 236190fccb52SAndrzej Pietrasiewicz (tmp & UDC_ATT) ? " ATT" : ""); 236290fccb52SAndrzej Pietrasiewicz seq_printf(s, "sof %04x\n", omap_readw(UDC_SOF)); 236390fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_IRQ_EN); 236490fccb52SAndrzej Pietrasiewicz seq_printf(s, "irq_en %04x" FOURBITS "%s\n", tmp, 236590fccb52SAndrzej Pietrasiewicz (tmp & UDC_SOF_IE) ? " sof" : "", 236690fccb52SAndrzej Pietrasiewicz (tmp & UDC_EPN_RX_IE) ? " epn_rx" : "", 236790fccb52SAndrzej Pietrasiewicz (tmp & UDC_EPN_TX_IE) ? " epn_tx" : "", 236890fccb52SAndrzej Pietrasiewicz (tmp & UDC_DS_CHG_IE) ? " ds_chg" : "", 236990fccb52SAndrzej Pietrasiewicz (tmp & UDC_EP0_IE) ? " ep0" : ""); 237090fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_IRQ_SRC); 237190fccb52SAndrzej Pietrasiewicz seq_printf(s, "irq_src %04x" EIGHTBITS "%s%s\n", tmp, 237290fccb52SAndrzej Pietrasiewicz (tmp & UDC_TXN_DONE) ? " txn_done" : "", 237390fccb52SAndrzej Pietrasiewicz (tmp & UDC_RXN_CNT) ? " rxn_cnt" : "", 237490fccb52SAndrzej Pietrasiewicz (tmp & UDC_RXN_EOT) ? " rxn_eot" : "", 237590fccb52SAndrzej Pietrasiewicz (tmp & UDC_IRQ_SOF) ? " sof" : "", 237690fccb52SAndrzej Pietrasiewicz (tmp & UDC_EPN_RX) ? " epn_rx" : "", 237790fccb52SAndrzej Pietrasiewicz (tmp & UDC_EPN_TX) ? " epn_tx" : "", 237890fccb52SAndrzej Pietrasiewicz (tmp & UDC_DS_CHG) ? " ds_chg" : "", 237990fccb52SAndrzej Pietrasiewicz (tmp & UDC_SETUP) ? " setup" : "", 238090fccb52SAndrzej Pietrasiewicz (tmp & UDC_EP0_RX) ? " ep0out" : "", 238190fccb52SAndrzej Pietrasiewicz (tmp & UDC_EP0_TX) ? " ep0in" : ""); 238290fccb52SAndrzej Pietrasiewicz if (use_dma) { 238390fccb52SAndrzej Pietrasiewicz unsigned i; 238490fccb52SAndrzej Pietrasiewicz 238590fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_DMA_IRQ_EN); 238690fccb52SAndrzej Pietrasiewicz seq_printf(s, "dma_irq_en %04x%s" EIGHTBITS "\n", tmp, 238790fccb52SAndrzej Pietrasiewicz (tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "", 238890fccb52SAndrzej Pietrasiewicz (tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "", 238990fccb52SAndrzej Pietrasiewicz (tmp & UDC_RX_EOT_IE(3)) ? " rx2_eot" : "", 239090fccb52SAndrzej Pietrasiewicz 239190fccb52SAndrzej Pietrasiewicz (tmp & UDC_TX_DONE_IE(2)) ? " tx1_done" : "", 239290fccb52SAndrzej Pietrasiewicz (tmp & UDC_RX_CNT_IE(2)) ? " rx1_cnt" : "", 239390fccb52SAndrzej Pietrasiewicz (tmp & UDC_RX_EOT_IE(2)) ? " rx1_eot" : "", 239490fccb52SAndrzej Pietrasiewicz 239590fccb52SAndrzej Pietrasiewicz (tmp & UDC_TX_DONE_IE(1)) ? " tx0_done" : "", 239690fccb52SAndrzej Pietrasiewicz (tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "", 239790fccb52SAndrzej Pietrasiewicz (tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : ""); 239890fccb52SAndrzej Pietrasiewicz 239990fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_RXDMA_CFG); 240090fccb52SAndrzej Pietrasiewicz seq_printf(s, "rxdma_cfg %04x\n", tmp); 240190fccb52SAndrzej Pietrasiewicz if (tmp) { 240290fccb52SAndrzej Pietrasiewicz for (i = 0; i < 3; i++) { 240390fccb52SAndrzej Pietrasiewicz if ((tmp & (0x0f << (i * 4))) == 0) 240490fccb52SAndrzej Pietrasiewicz continue; 240590fccb52SAndrzej Pietrasiewicz seq_printf(s, "rxdma[%d] %04x\n", i, 240690fccb52SAndrzej Pietrasiewicz omap_readw(UDC_RXDMA(i + 1))); 240790fccb52SAndrzej Pietrasiewicz } 240890fccb52SAndrzej Pietrasiewicz } 240990fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_TXDMA_CFG); 241090fccb52SAndrzej Pietrasiewicz seq_printf(s, "txdma_cfg %04x\n", tmp); 241190fccb52SAndrzej Pietrasiewicz if (tmp) { 241290fccb52SAndrzej Pietrasiewicz for (i = 0; i < 3; i++) { 241390fccb52SAndrzej Pietrasiewicz if (!(tmp & (0x0f << (i * 4)))) 241490fccb52SAndrzej Pietrasiewicz continue; 241590fccb52SAndrzej Pietrasiewicz seq_printf(s, "txdma[%d] %04x\n", i, 241690fccb52SAndrzej Pietrasiewicz omap_readw(UDC_TXDMA(i + 1))); 241790fccb52SAndrzej Pietrasiewicz } 241890fccb52SAndrzej Pietrasiewicz } 241990fccb52SAndrzej Pietrasiewicz } 242090fccb52SAndrzej Pietrasiewicz 242190fccb52SAndrzej Pietrasiewicz tmp = omap_readw(UDC_DEVSTAT); 242290fccb52SAndrzej Pietrasiewicz if (tmp & UDC_ATT) { 242390fccb52SAndrzej Pietrasiewicz proc_ep_show(s, &udc->ep[0]); 242490fccb52SAndrzej Pietrasiewicz if (tmp & UDC_ADD) { 242590fccb52SAndrzej Pietrasiewicz list_for_each_entry(ep, &udc->gadget.ep_list, 242690fccb52SAndrzej Pietrasiewicz ep.ep_list) { 242790fccb52SAndrzej Pietrasiewicz if (ep->ep.desc) 242890fccb52SAndrzej Pietrasiewicz proc_ep_show(s, ep); 242990fccb52SAndrzej Pietrasiewicz } 243090fccb52SAndrzej Pietrasiewicz } 243190fccb52SAndrzej Pietrasiewicz } 243290fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 243390fccb52SAndrzej Pietrasiewicz return 0; 243490fccb52SAndrzej Pietrasiewicz } 243590fccb52SAndrzej Pietrasiewicz 243690fccb52SAndrzej Pietrasiewicz static void create_proc_file(void) 243790fccb52SAndrzej Pietrasiewicz { 24383f3942acSChristoph Hellwig proc_create_single(proc_filename, 0, NULL, proc_udc_show); 243990fccb52SAndrzej Pietrasiewicz } 244090fccb52SAndrzej Pietrasiewicz 244190fccb52SAndrzej Pietrasiewicz static void remove_proc_file(void) 244290fccb52SAndrzej Pietrasiewicz { 244390fccb52SAndrzej Pietrasiewicz remove_proc_entry(proc_filename, NULL); 244490fccb52SAndrzej Pietrasiewicz } 244590fccb52SAndrzej Pietrasiewicz 244690fccb52SAndrzej Pietrasiewicz #else 244790fccb52SAndrzej Pietrasiewicz 244890fccb52SAndrzej Pietrasiewicz static inline void create_proc_file(void) {} 244990fccb52SAndrzej Pietrasiewicz static inline void remove_proc_file(void) {} 245090fccb52SAndrzej Pietrasiewicz 245190fccb52SAndrzej Pietrasiewicz #endif 245290fccb52SAndrzej Pietrasiewicz 245390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 245490fccb52SAndrzej Pietrasiewicz 245590fccb52SAndrzej Pietrasiewicz /* Before this controller can enumerate, we need to pick an endpoint 245690fccb52SAndrzej Pietrasiewicz * configuration, or "fifo_mode" That involves allocating 2KB of packet 245790fccb52SAndrzej Pietrasiewicz * buffer space among the endpoints we'll be operating. 245890fccb52SAndrzej Pietrasiewicz * 245990fccb52SAndrzej Pietrasiewicz * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when 246090fccb52SAndrzej Pietrasiewicz * UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that 246190fccb52SAndrzej Pietrasiewicz * capability yet though. 246290fccb52SAndrzej Pietrasiewicz */ 246390fccb52SAndrzej Pietrasiewicz static unsigned 246490fccb52SAndrzej Pietrasiewicz omap_ep_setup(char *name, u8 addr, u8 type, 246590fccb52SAndrzej Pietrasiewicz unsigned buf, unsigned maxp, int dbuf) 246690fccb52SAndrzej Pietrasiewicz { 246790fccb52SAndrzej Pietrasiewicz struct omap_ep *ep; 246890fccb52SAndrzej Pietrasiewicz u16 epn_rxtx = 0; 246990fccb52SAndrzej Pietrasiewicz 247090fccb52SAndrzej Pietrasiewicz /* OUT endpoints first, then IN */ 247190fccb52SAndrzej Pietrasiewicz ep = &udc->ep[addr & 0xf]; 247290fccb52SAndrzej Pietrasiewicz if (addr & USB_DIR_IN) 247390fccb52SAndrzej Pietrasiewicz ep += 16; 247490fccb52SAndrzej Pietrasiewicz 247590fccb52SAndrzej Pietrasiewicz /* in case of ep init table bugs */ 247690fccb52SAndrzej Pietrasiewicz BUG_ON(ep->name[0]); 247790fccb52SAndrzej Pietrasiewicz 247890fccb52SAndrzej Pietrasiewicz /* chip setup ... bit values are same for IN, OUT */ 247990fccb52SAndrzej Pietrasiewicz if (type == USB_ENDPOINT_XFER_ISOC) { 248090fccb52SAndrzej Pietrasiewicz switch (maxp) { 248190fccb52SAndrzej Pietrasiewicz case 8: 248290fccb52SAndrzej Pietrasiewicz epn_rxtx = 0 << 12; 248390fccb52SAndrzej Pietrasiewicz break; 248490fccb52SAndrzej Pietrasiewicz case 16: 248590fccb52SAndrzej Pietrasiewicz epn_rxtx = 1 << 12; 248690fccb52SAndrzej Pietrasiewicz break; 248790fccb52SAndrzej Pietrasiewicz case 32: 248890fccb52SAndrzej Pietrasiewicz epn_rxtx = 2 << 12; 248990fccb52SAndrzej Pietrasiewicz break; 249090fccb52SAndrzej Pietrasiewicz case 64: 249190fccb52SAndrzej Pietrasiewicz epn_rxtx = 3 << 12; 249290fccb52SAndrzej Pietrasiewicz break; 249390fccb52SAndrzej Pietrasiewicz case 128: 249490fccb52SAndrzej Pietrasiewicz epn_rxtx = 4 << 12; 249590fccb52SAndrzej Pietrasiewicz break; 249690fccb52SAndrzej Pietrasiewicz case 256: 249790fccb52SAndrzej Pietrasiewicz epn_rxtx = 5 << 12; 249890fccb52SAndrzej Pietrasiewicz break; 249990fccb52SAndrzej Pietrasiewicz case 512: 250090fccb52SAndrzej Pietrasiewicz epn_rxtx = 6 << 12; 250190fccb52SAndrzej Pietrasiewicz break; 250290fccb52SAndrzej Pietrasiewicz default: 250390fccb52SAndrzej Pietrasiewicz BUG(); 250490fccb52SAndrzej Pietrasiewicz } 250590fccb52SAndrzej Pietrasiewicz epn_rxtx |= UDC_EPN_RX_ISO; 250690fccb52SAndrzej Pietrasiewicz dbuf = 1; 250790fccb52SAndrzej Pietrasiewicz } else { 250890fccb52SAndrzej Pietrasiewicz /* double-buffering "not supported" on 15xx, 250990fccb52SAndrzej Pietrasiewicz * and ignored for PIO-IN on newer chips 251090fccb52SAndrzej Pietrasiewicz * (for more reliable behavior) 251190fccb52SAndrzej Pietrasiewicz */ 251290fccb52SAndrzej Pietrasiewicz if (!use_dma || cpu_is_omap15xx()) 251390fccb52SAndrzej Pietrasiewicz dbuf = 0; 251490fccb52SAndrzej Pietrasiewicz 251590fccb52SAndrzej Pietrasiewicz switch (maxp) { 251690fccb52SAndrzej Pietrasiewicz case 8: 251790fccb52SAndrzej Pietrasiewicz epn_rxtx = 0 << 12; 251890fccb52SAndrzej Pietrasiewicz break; 251990fccb52SAndrzej Pietrasiewicz case 16: 252090fccb52SAndrzej Pietrasiewicz epn_rxtx = 1 << 12; 252190fccb52SAndrzej Pietrasiewicz break; 252290fccb52SAndrzej Pietrasiewicz case 32: 252390fccb52SAndrzej Pietrasiewicz epn_rxtx = 2 << 12; 252490fccb52SAndrzej Pietrasiewicz break; 252590fccb52SAndrzej Pietrasiewicz case 64: 252690fccb52SAndrzej Pietrasiewicz epn_rxtx = 3 << 12; 252790fccb52SAndrzej Pietrasiewicz break; 252890fccb52SAndrzej Pietrasiewicz default: 252990fccb52SAndrzej Pietrasiewicz BUG(); 253090fccb52SAndrzej Pietrasiewicz } 253190fccb52SAndrzej Pietrasiewicz if (dbuf && addr) 253290fccb52SAndrzej Pietrasiewicz epn_rxtx |= UDC_EPN_RX_DB; 2533e99e88a9SKees Cook timer_setup(&ep->timer, pio_out_timer, 0); 253490fccb52SAndrzej Pietrasiewicz } 253590fccb52SAndrzej Pietrasiewicz if (addr) 253690fccb52SAndrzej Pietrasiewicz epn_rxtx |= UDC_EPN_RX_VALID; 253790fccb52SAndrzej Pietrasiewicz BUG_ON(buf & 0x07); 253890fccb52SAndrzej Pietrasiewicz epn_rxtx |= buf >> 3; 253990fccb52SAndrzej Pietrasiewicz 254090fccb52SAndrzej Pietrasiewicz DBG("%s addr %02x rxtx %04x maxp %d%s buf %d\n", 254190fccb52SAndrzej Pietrasiewicz name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf); 254290fccb52SAndrzej Pietrasiewicz 254390fccb52SAndrzej Pietrasiewicz if (addr & USB_DIR_IN) 254490fccb52SAndrzej Pietrasiewicz omap_writew(epn_rxtx, UDC_EP_TX(addr & 0xf)); 254590fccb52SAndrzej Pietrasiewicz else 254690fccb52SAndrzej Pietrasiewicz omap_writew(epn_rxtx, UDC_EP_RX(addr)); 254790fccb52SAndrzej Pietrasiewicz 254890fccb52SAndrzej Pietrasiewicz /* next endpoint's buffer starts after this one's */ 254990fccb52SAndrzej Pietrasiewicz buf += maxp; 255090fccb52SAndrzej Pietrasiewicz if (dbuf) 255190fccb52SAndrzej Pietrasiewicz buf += maxp; 255290fccb52SAndrzej Pietrasiewicz BUG_ON(buf > 2048); 255390fccb52SAndrzej Pietrasiewicz 255490fccb52SAndrzej Pietrasiewicz /* set up driver data structures */ 255590fccb52SAndrzej Pietrasiewicz BUG_ON(strlen(name) >= sizeof ep->name); 2556b7db5733SWolfram Sang strscpy(ep->name, name, sizeof(ep->name)); 255790fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->queue); 255890fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->iso); 255990fccb52SAndrzej Pietrasiewicz ep->bEndpointAddress = addr; 256090fccb52SAndrzej Pietrasiewicz ep->bmAttributes = type; 256190fccb52SAndrzej Pietrasiewicz ep->double_buf = dbuf; 256290fccb52SAndrzej Pietrasiewicz ep->udc = udc; 256390fccb52SAndrzej Pietrasiewicz 25647d4ba80dSRobert Baldyga switch (type) { 25657d4ba80dSRobert Baldyga case USB_ENDPOINT_XFER_CONTROL: 25667d4ba80dSRobert Baldyga ep->ep.caps.type_control = true; 25677d4ba80dSRobert Baldyga ep->ep.caps.dir_in = true; 25687d4ba80dSRobert Baldyga ep->ep.caps.dir_out = true; 25697d4ba80dSRobert Baldyga break; 25707d4ba80dSRobert Baldyga case USB_ENDPOINT_XFER_ISOC: 25717d4ba80dSRobert Baldyga ep->ep.caps.type_iso = true; 25727d4ba80dSRobert Baldyga break; 25737d4ba80dSRobert Baldyga case USB_ENDPOINT_XFER_BULK: 25747d4ba80dSRobert Baldyga ep->ep.caps.type_bulk = true; 25757d4ba80dSRobert Baldyga break; 25767d4ba80dSRobert Baldyga case USB_ENDPOINT_XFER_INT: 25777d4ba80dSRobert Baldyga ep->ep.caps.type_int = true; 25787d4ba80dSRobert Baldyga break; 257955ee1bf9SJason Yan } 25807d4ba80dSRobert Baldyga 25817d4ba80dSRobert Baldyga if (addr & USB_DIR_IN) 25827d4ba80dSRobert Baldyga ep->ep.caps.dir_in = true; 25837d4ba80dSRobert Baldyga else 25847d4ba80dSRobert Baldyga ep->ep.caps.dir_out = true; 25857d4ba80dSRobert Baldyga 258690fccb52SAndrzej Pietrasiewicz ep->ep.name = ep->name; 258790fccb52SAndrzej Pietrasiewicz ep->ep.ops = &omap_ep_ops; 258890fccb52SAndrzej Pietrasiewicz ep->maxpacket = maxp; 258990fccb52SAndrzej Pietrasiewicz usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); 259090fccb52SAndrzej Pietrasiewicz list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); 259190fccb52SAndrzej Pietrasiewicz 259290fccb52SAndrzej Pietrasiewicz return buf; 259390fccb52SAndrzej Pietrasiewicz } 259490fccb52SAndrzej Pietrasiewicz 259590fccb52SAndrzej Pietrasiewicz static void omap_udc_release(struct device *dev) 259690fccb52SAndrzej Pietrasiewicz { 259799f70036SAaro Koskinen pullup_disable(udc); 259899f70036SAaro Koskinen if (!IS_ERR_OR_NULL(udc->transceiver)) { 259999f70036SAaro Koskinen usb_put_phy(udc->transceiver); 260099f70036SAaro Koskinen udc->transceiver = NULL; 260199f70036SAaro Koskinen } 260299f70036SAaro Koskinen omap_writew(0, UDC_SYSCON1); 260399f70036SAaro Koskinen remove_proc_file(); 260499f70036SAaro Koskinen if (udc->dc_clk) { 260599f70036SAaro Koskinen if (udc->clk_requested) 260699f70036SAaro Koskinen omap_udc_enable_clock(0); 260724a5d34dSJanusz Krzysztofik clk_unprepare(udc->hhc_clk); 260824a5d34dSJanusz Krzysztofik clk_unprepare(udc->dc_clk); 260999f70036SAaro Koskinen clk_put(udc->hhc_clk); 261099f70036SAaro Koskinen clk_put(udc->dc_clk); 261199f70036SAaro Koskinen } 261299f70036SAaro Koskinen if (udc->done) 261390fccb52SAndrzej Pietrasiewicz complete(udc->done); 261490fccb52SAndrzej Pietrasiewicz kfree(udc); 261590fccb52SAndrzej Pietrasiewicz } 261690fccb52SAndrzej Pietrasiewicz 261790fccb52SAndrzej Pietrasiewicz static int 261890fccb52SAndrzej Pietrasiewicz omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) 261990fccb52SAndrzej Pietrasiewicz { 262090fccb52SAndrzej Pietrasiewicz unsigned tmp, buf; 262190fccb52SAndrzej Pietrasiewicz 262290fccb52SAndrzej Pietrasiewicz /* abolish any previous hardware state */ 262390fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_SYSCON1); 262490fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_IRQ_EN); 262590fccb52SAndrzej Pietrasiewicz omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); 262690fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_DMA_IRQ_EN); 262790fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_RXDMA_CFG); 262890fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_TXDMA_CFG); 262990fccb52SAndrzej Pietrasiewicz 263090fccb52SAndrzej Pietrasiewicz /* UDC_PULLUP_EN gates the chip clock */ 263190fccb52SAndrzej Pietrasiewicz /* OTG_SYSCON_1 |= DEV_IDLE_EN; */ 263290fccb52SAndrzej Pietrasiewicz 263390fccb52SAndrzej Pietrasiewicz udc = kzalloc(sizeof(*udc), GFP_KERNEL); 263490fccb52SAndrzej Pietrasiewicz if (!udc) 263590fccb52SAndrzej Pietrasiewicz return -ENOMEM; 263690fccb52SAndrzej Pietrasiewicz 263790fccb52SAndrzej Pietrasiewicz spin_lock_init(&udc->lock); 263890fccb52SAndrzej Pietrasiewicz 263990fccb52SAndrzej Pietrasiewicz udc->gadget.ops = &omap_gadget_ops; 264090fccb52SAndrzej Pietrasiewicz udc->gadget.ep0 = &udc->ep[0].ep; 264190fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&udc->gadget.ep_list); 264290fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&udc->iso); 264390fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_UNKNOWN; 264490fccb52SAndrzej Pietrasiewicz udc->gadget.max_speed = USB_SPEED_FULL; 264590fccb52SAndrzej Pietrasiewicz udc->gadget.name = driver_name; 2646069caf59SAaro Koskinen udc->gadget.quirk_ep_out_aligned_size = 1; 264790fccb52SAndrzej Pietrasiewicz udc->transceiver = xceiv; 264890fccb52SAndrzej Pietrasiewicz 264990fccb52SAndrzej Pietrasiewicz /* ep0 is special; put it right after the SETUP buffer */ 265090fccb52SAndrzej Pietrasiewicz buf = omap_ep_setup("ep0", 0, USB_ENDPOINT_XFER_CONTROL, 265190fccb52SAndrzej Pietrasiewicz 8 /* after SETUP */, 64 /* maxpacket */, 0); 265290fccb52SAndrzej Pietrasiewicz list_del_init(&udc->ep[0].ep.ep_list); 265390fccb52SAndrzej Pietrasiewicz 265490fccb52SAndrzej Pietrasiewicz /* initially disable all non-ep0 endpoints */ 265590fccb52SAndrzej Pietrasiewicz for (tmp = 1; tmp < 15; tmp++) { 265690fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_RX(tmp)); 265790fccb52SAndrzej Pietrasiewicz omap_writew(0, UDC_EP_TX(tmp)); 265890fccb52SAndrzej Pietrasiewicz } 265990fccb52SAndrzej Pietrasiewicz 266090fccb52SAndrzej Pietrasiewicz #define OMAP_BULK_EP(name, addr) \ 266190fccb52SAndrzej Pietrasiewicz buf = omap_ep_setup(name "-bulk", addr, \ 266290fccb52SAndrzej Pietrasiewicz USB_ENDPOINT_XFER_BULK, buf, 64, 1); 266390fccb52SAndrzej Pietrasiewicz #define OMAP_INT_EP(name, addr, maxp) \ 266490fccb52SAndrzej Pietrasiewicz buf = omap_ep_setup(name "-int", addr, \ 266590fccb52SAndrzej Pietrasiewicz USB_ENDPOINT_XFER_INT, buf, maxp, 0); 266690fccb52SAndrzej Pietrasiewicz #define OMAP_ISO_EP(name, addr, maxp) \ 266790fccb52SAndrzej Pietrasiewicz buf = omap_ep_setup(name "-iso", addr, \ 266890fccb52SAndrzej Pietrasiewicz USB_ENDPOINT_XFER_ISOC, buf, maxp, 1); 266990fccb52SAndrzej Pietrasiewicz 267090fccb52SAndrzej Pietrasiewicz switch (fifo_mode) { 267190fccb52SAndrzej Pietrasiewicz case 0: 267290fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); 267390fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); 267490fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep3in", USB_DIR_IN | 3, 16); 267590fccb52SAndrzej Pietrasiewicz break; 267690fccb52SAndrzej Pietrasiewicz case 1: 267790fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); 267890fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); 267990fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); 268090fccb52SAndrzej Pietrasiewicz 268190fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep3in", USB_DIR_IN | 3); 268290fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep4out", USB_DIR_OUT | 4); 268390fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep10in", USB_DIR_IN | 10, 16); 268490fccb52SAndrzej Pietrasiewicz 268590fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep5in", USB_DIR_IN | 5); 268690fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); 268790fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep11in", USB_DIR_IN | 11, 16); 268890fccb52SAndrzej Pietrasiewicz 268990fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); 269090fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep6out", USB_DIR_OUT | 6); 269190fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep12in", USB_DIR_IN | 12, 16); 269290fccb52SAndrzej Pietrasiewicz 269390fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep7in", USB_DIR_IN | 7); 269490fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); 269590fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep13in", USB_DIR_IN | 13, 16); 269690fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep13out", USB_DIR_OUT | 13, 16); 269790fccb52SAndrzej Pietrasiewicz 269890fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep8in", USB_DIR_IN | 8); 269990fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep8out", USB_DIR_OUT | 8); 270090fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep14in", USB_DIR_IN | 14, 16); 270190fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep14out", USB_DIR_OUT | 14, 16); 270290fccb52SAndrzej Pietrasiewicz 270390fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep15in", USB_DIR_IN | 15); 270490fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep15out", USB_DIR_OUT | 15); 270590fccb52SAndrzej Pietrasiewicz 270690fccb52SAndrzej Pietrasiewicz break; 270790fccb52SAndrzej Pietrasiewicz 270890fccb52SAndrzej Pietrasiewicz #ifdef USE_ISO 270990fccb52SAndrzej Pietrasiewicz case 2: /* mixed iso/bulk */ 271090fccb52SAndrzej Pietrasiewicz OMAP_ISO_EP("ep1in", USB_DIR_IN | 1, 256); 271190fccb52SAndrzej Pietrasiewicz OMAP_ISO_EP("ep2out", USB_DIR_OUT | 2, 256); 271290fccb52SAndrzej Pietrasiewicz OMAP_ISO_EP("ep3in", USB_DIR_IN | 3, 128); 271390fccb52SAndrzej Pietrasiewicz OMAP_ISO_EP("ep4out", USB_DIR_OUT | 4, 128); 271490fccb52SAndrzej Pietrasiewicz 271590fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep5in", USB_DIR_IN | 5, 16); 271690fccb52SAndrzej Pietrasiewicz 271790fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); 271890fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); 271990fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep8in", USB_DIR_IN | 8, 16); 272090fccb52SAndrzej Pietrasiewicz break; 272190fccb52SAndrzej Pietrasiewicz case 3: /* mixed bulk/iso */ 272290fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); 272390fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); 272490fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep3in", USB_DIR_IN | 3, 16); 272590fccb52SAndrzej Pietrasiewicz 272690fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep4in", USB_DIR_IN | 4); 272790fccb52SAndrzej Pietrasiewicz OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); 272890fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep6in", USB_DIR_IN | 6, 16); 272990fccb52SAndrzej Pietrasiewicz 273090fccb52SAndrzej Pietrasiewicz OMAP_ISO_EP("ep7in", USB_DIR_IN | 7, 256); 273190fccb52SAndrzej Pietrasiewicz OMAP_ISO_EP("ep8out", USB_DIR_OUT | 8, 256); 273290fccb52SAndrzej Pietrasiewicz OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); 273390fccb52SAndrzej Pietrasiewicz break; 273490fccb52SAndrzej Pietrasiewicz #endif 273590fccb52SAndrzej Pietrasiewicz 273690fccb52SAndrzej Pietrasiewicz /* add more modes as needed */ 273790fccb52SAndrzej Pietrasiewicz 273890fccb52SAndrzej Pietrasiewicz default: 273990fccb52SAndrzej Pietrasiewicz ERR("unsupported fifo_mode #%d\n", fifo_mode); 274090fccb52SAndrzej Pietrasiewicz return -ENODEV; 274190fccb52SAndrzej Pietrasiewicz } 274290fccb52SAndrzej Pietrasiewicz omap_writew(UDC_CFG_LOCK|UDC_SELF_PWR, UDC_SYSCON1); 274390fccb52SAndrzej Pietrasiewicz INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf); 274490fccb52SAndrzej Pietrasiewicz return 0; 274590fccb52SAndrzej Pietrasiewicz } 274690fccb52SAndrzej Pietrasiewicz 274790fccb52SAndrzej Pietrasiewicz static int omap_udc_probe(struct platform_device *pdev) 274890fccb52SAndrzej Pietrasiewicz { 274990fccb52SAndrzej Pietrasiewicz int status = -ENODEV; 275090fccb52SAndrzej Pietrasiewicz int hmc; 275190fccb52SAndrzej Pietrasiewicz struct usb_phy *xceiv = NULL; 275290fccb52SAndrzej Pietrasiewicz const char *type = NULL; 275390fccb52SAndrzej Pietrasiewicz struct omap_usb_config *config = dev_get_platdata(&pdev->dev); 275490fccb52SAndrzej Pietrasiewicz struct clk *dc_clk = NULL; 275590fccb52SAndrzej Pietrasiewicz struct clk *hhc_clk = NULL; 275690fccb52SAndrzej Pietrasiewicz 275790fccb52SAndrzej Pietrasiewicz /* NOTE: "knows" the order of the resources! */ 275890fccb52SAndrzej Pietrasiewicz if (!request_mem_region(pdev->resource[0].start, 27597b7ad03fSJulia Lawall resource_size(&pdev->resource[0]), 276090fccb52SAndrzej Pietrasiewicz driver_name)) { 276190fccb52SAndrzej Pietrasiewicz DBG("request_mem_region failed\n"); 276290fccb52SAndrzej Pietrasiewicz return -EBUSY; 276390fccb52SAndrzej Pietrasiewicz } 276490fccb52SAndrzej Pietrasiewicz 276590fccb52SAndrzej Pietrasiewicz if (cpu_is_omap16xx()) { 276690fccb52SAndrzej Pietrasiewicz dc_clk = clk_get(&pdev->dev, "usb_dc_ck"); 276790fccb52SAndrzej Pietrasiewicz hhc_clk = clk_get(&pdev->dev, "usb_hhc_ck"); 276890fccb52SAndrzej Pietrasiewicz BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); 276990fccb52SAndrzej Pietrasiewicz /* can't use omap_udc_enable_clock yet */ 277024a5d34dSJanusz Krzysztofik clk_prepare_enable(dc_clk); 277124a5d34dSJanusz Krzysztofik clk_prepare_enable(hhc_clk); 277290fccb52SAndrzej Pietrasiewicz udelay(100); 277390fccb52SAndrzej Pietrasiewicz } 277490fccb52SAndrzej Pietrasiewicz 277590fccb52SAndrzej Pietrasiewicz INFO("OMAP UDC rev %d.%d%s\n", 277690fccb52SAndrzej Pietrasiewicz omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf, 277790fccb52SAndrzej Pietrasiewicz config->otg ? ", Mini-AB" : ""); 277890fccb52SAndrzej Pietrasiewicz 277990fccb52SAndrzej Pietrasiewicz /* use the mode given to us by board init code */ 278090fccb52SAndrzej Pietrasiewicz if (cpu_is_omap15xx()) { 278190fccb52SAndrzej Pietrasiewicz hmc = HMC_1510; 278290fccb52SAndrzej Pietrasiewicz type = "(unknown)"; 278390fccb52SAndrzej Pietrasiewicz 278490fccb52SAndrzej Pietrasiewicz if (machine_without_vbus_sense()) { 278590fccb52SAndrzej Pietrasiewicz /* just set up software VBUS detect, and then 278690fccb52SAndrzej Pietrasiewicz * later rig it so we always report VBUS. 278790fccb52SAndrzej Pietrasiewicz * FIXME without really sensing VBUS, we can't 278890fccb52SAndrzej Pietrasiewicz * know when to turn PULLUP_EN on/off; and that 278990fccb52SAndrzej Pietrasiewicz * means we always "need" the 48MHz clock. 279090fccb52SAndrzej Pietrasiewicz */ 279190fccb52SAndrzej Pietrasiewicz u32 tmp = omap_readl(FUNC_MUX_CTRL_0); 279290fccb52SAndrzej Pietrasiewicz tmp &= ~VBUS_CTRL_1510; 279390fccb52SAndrzej Pietrasiewicz omap_writel(tmp, FUNC_MUX_CTRL_0); 279490fccb52SAndrzej Pietrasiewicz tmp |= VBUS_MODE_1510; 279590fccb52SAndrzej Pietrasiewicz tmp &= ~VBUS_CTRL_1510; 279690fccb52SAndrzej Pietrasiewicz omap_writel(tmp, FUNC_MUX_CTRL_0); 279790fccb52SAndrzej Pietrasiewicz } 279890fccb52SAndrzej Pietrasiewicz } else { 279990fccb52SAndrzej Pietrasiewicz /* The transceiver may package some GPIO logic or handle 280090fccb52SAndrzej Pietrasiewicz * loopback and/or transceiverless setup; if we find one, 280190fccb52SAndrzej Pietrasiewicz * use it. Except for OTG, we don't _need_ to talk to one; 280290fccb52SAndrzej Pietrasiewicz * but not having one probably means no VBUS detection. 280390fccb52SAndrzej Pietrasiewicz */ 280490fccb52SAndrzej Pietrasiewicz xceiv = usb_get_phy(USB_PHY_TYPE_USB2); 280590fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(xceiv)) 280690fccb52SAndrzej Pietrasiewicz type = xceiv->label; 280790fccb52SAndrzej Pietrasiewicz else if (config->otg) { 280890fccb52SAndrzej Pietrasiewicz DBG("OTG requires external transceiver!\n"); 280990fccb52SAndrzej Pietrasiewicz goto cleanup0; 281090fccb52SAndrzej Pietrasiewicz } 281190fccb52SAndrzej Pietrasiewicz 281290fccb52SAndrzej Pietrasiewicz hmc = HMC_1610; 281390fccb52SAndrzej Pietrasiewicz 281490fccb52SAndrzej Pietrasiewicz switch (hmc) { 281590fccb52SAndrzej Pietrasiewicz case 0: /* POWERUP DEFAULT == 0 */ 281690fccb52SAndrzej Pietrasiewicz case 4: 281790fccb52SAndrzej Pietrasiewicz case 12: 281890fccb52SAndrzej Pietrasiewicz case 20: 281990fccb52SAndrzej Pietrasiewicz if (!cpu_is_omap1710()) { 282090fccb52SAndrzej Pietrasiewicz type = "integrated"; 282190fccb52SAndrzej Pietrasiewicz break; 282290fccb52SAndrzej Pietrasiewicz } 2823a74005abSGustavo A. R. Silva fallthrough; 282490fccb52SAndrzej Pietrasiewicz case 3: 282590fccb52SAndrzej Pietrasiewicz case 11: 282690fccb52SAndrzej Pietrasiewicz case 16: 282790fccb52SAndrzej Pietrasiewicz case 19: 282890fccb52SAndrzej Pietrasiewicz case 25: 282990fccb52SAndrzej Pietrasiewicz if (IS_ERR_OR_NULL(xceiv)) { 283090fccb52SAndrzej Pietrasiewicz DBG("external transceiver not registered!\n"); 283190fccb52SAndrzej Pietrasiewicz type = "unknown"; 283290fccb52SAndrzej Pietrasiewicz } 283390fccb52SAndrzej Pietrasiewicz break; 283490fccb52SAndrzej Pietrasiewicz case 21: /* internal loopback */ 283590fccb52SAndrzej Pietrasiewicz type = "loopback"; 283690fccb52SAndrzej Pietrasiewicz break; 283790fccb52SAndrzej Pietrasiewicz case 14: /* transceiverless */ 283890fccb52SAndrzej Pietrasiewicz if (cpu_is_omap1710()) 283990fccb52SAndrzej Pietrasiewicz goto bad_on_1710; 2840a74005abSGustavo A. R. Silva fallthrough; 284190fccb52SAndrzej Pietrasiewicz case 13: 284290fccb52SAndrzej Pietrasiewicz case 15: 284390fccb52SAndrzej Pietrasiewicz type = "no"; 284490fccb52SAndrzej Pietrasiewicz break; 284590fccb52SAndrzej Pietrasiewicz 284690fccb52SAndrzej Pietrasiewicz default: 284790fccb52SAndrzej Pietrasiewicz bad_on_1710: 284890fccb52SAndrzej Pietrasiewicz ERR("unrecognized UDC HMC mode %d\n", hmc); 284990fccb52SAndrzej Pietrasiewicz goto cleanup0; 285090fccb52SAndrzej Pietrasiewicz } 285190fccb52SAndrzej Pietrasiewicz } 285290fccb52SAndrzej Pietrasiewicz 285390fccb52SAndrzej Pietrasiewicz INFO("hmc mode %d, %s transceiver\n", hmc, type); 285490fccb52SAndrzej Pietrasiewicz 285590fccb52SAndrzej Pietrasiewicz /* a "gadget" abstracts/virtualizes the controller */ 285690fccb52SAndrzej Pietrasiewicz status = omap_udc_setup(pdev, xceiv); 285790fccb52SAndrzej Pietrasiewicz if (status) 285890fccb52SAndrzej Pietrasiewicz goto cleanup0; 285990fccb52SAndrzej Pietrasiewicz 286090fccb52SAndrzej Pietrasiewicz xceiv = NULL; 286190fccb52SAndrzej Pietrasiewicz /* "udc" is now valid */ 286290fccb52SAndrzej Pietrasiewicz pullup_disable(udc); 28633112fddeSJavier Martinez Canillas #if IS_ENABLED(CONFIG_USB_OHCI_HCD) 286490fccb52SAndrzej Pietrasiewicz udc->gadget.is_otg = (config->otg != 0); 286590fccb52SAndrzej Pietrasiewicz #endif 286690fccb52SAndrzej Pietrasiewicz 286790fccb52SAndrzej Pietrasiewicz /* starting with omap1710 es2.0, clear toggle is a separate bit */ 286890fccb52SAndrzej Pietrasiewicz if (omap_readw(UDC_REV) >= 0x61) 286990fccb52SAndrzej Pietrasiewicz udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE; 287090fccb52SAndrzej Pietrasiewicz else 287190fccb52SAndrzej Pietrasiewicz udc->clr_halt = UDC_RESET_EP; 287290fccb52SAndrzej Pietrasiewicz 287390fccb52SAndrzej Pietrasiewicz /* USB general purpose IRQ: ep0, state changes, dma, etc */ 2874286afddeSAaro Koskinen status = devm_request_irq(&pdev->dev, pdev->resource[1].start, 2875286afddeSAaro Koskinen omap_udc_irq, 0, driver_name, udc); 287690fccb52SAndrzej Pietrasiewicz if (status != 0) { 287790fccb52SAndrzej Pietrasiewicz ERR("can't get irq %d, err %d\n", 287890fccb52SAndrzej Pietrasiewicz (int) pdev->resource[1].start, status); 287990fccb52SAndrzej Pietrasiewicz goto cleanup1; 288090fccb52SAndrzej Pietrasiewicz } 288190fccb52SAndrzej Pietrasiewicz 288290fccb52SAndrzej Pietrasiewicz /* USB "non-iso" IRQ (PIO for all but ep0) */ 2883286afddeSAaro Koskinen status = devm_request_irq(&pdev->dev, pdev->resource[2].start, 2884286afddeSAaro Koskinen omap_udc_pio_irq, 0, "omap_udc pio", udc); 288590fccb52SAndrzej Pietrasiewicz if (status != 0) { 288690fccb52SAndrzej Pietrasiewicz ERR("can't get irq %d, err %d\n", 288790fccb52SAndrzej Pietrasiewicz (int) pdev->resource[2].start, status); 2888286afddeSAaro Koskinen goto cleanup1; 288990fccb52SAndrzej Pietrasiewicz } 289090fccb52SAndrzej Pietrasiewicz #ifdef USE_ISO 2891286afddeSAaro Koskinen status = devm_request_irq(&pdev->dev, pdev->resource[3].start, 2892286afddeSAaro Koskinen omap_udc_iso_irq, 0, "omap_udc iso", udc); 289390fccb52SAndrzej Pietrasiewicz if (status != 0) { 289490fccb52SAndrzej Pietrasiewicz ERR("can't get irq %d, err %d\n", 289590fccb52SAndrzej Pietrasiewicz (int) pdev->resource[3].start, status); 2896286afddeSAaro Koskinen goto cleanup1; 289790fccb52SAndrzej Pietrasiewicz } 289890fccb52SAndrzej Pietrasiewicz #endif 28998825acd7SArnd Bergmann if (cpu_is_omap16xx()) { 290090fccb52SAndrzej Pietrasiewicz udc->dc_clk = dc_clk; 290190fccb52SAndrzej Pietrasiewicz udc->hhc_clk = hhc_clk; 290290fccb52SAndrzej Pietrasiewicz clk_disable(hhc_clk); 290390fccb52SAndrzej Pietrasiewicz clk_disable(dc_clk); 290490fccb52SAndrzej Pietrasiewicz } 290590fccb52SAndrzej Pietrasiewicz 290690fccb52SAndrzej Pietrasiewicz create_proc_file(); 290799f70036SAaro Koskinen return usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, 290890fccb52SAndrzej Pietrasiewicz omap_udc_release); 290990fccb52SAndrzej Pietrasiewicz 291090fccb52SAndrzej Pietrasiewicz cleanup1: 291190fccb52SAndrzej Pietrasiewicz kfree(udc); 291290fccb52SAndrzej Pietrasiewicz udc = NULL; 291390fccb52SAndrzej Pietrasiewicz 291490fccb52SAndrzej Pietrasiewicz cleanup0: 291590fccb52SAndrzej Pietrasiewicz if (!IS_ERR_OR_NULL(xceiv)) 291690fccb52SAndrzej Pietrasiewicz usb_put_phy(xceiv); 291790fccb52SAndrzej Pietrasiewicz 29188825acd7SArnd Bergmann if (cpu_is_omap16xx()) { 291924a5d34dSJanusz Krzysztofik clk_disable_unprepare(hhc_clk); 292024a5d34dSJanusz Krzysztofik clk_disable_unprepare(dc_clk); 292190fccb52SAndrzej Pietrasiewicz clk_put(hhc_clk); 292290fccb52SAndrzej Pietrasiewicz clk_put(dc_clk); 292390fccb52SAndrzej Pietrasiewicz } 292490fccb52SAndrzej Pietrasiewicz 292590fccb52SAndrzej Pietrasiewicz release_mem_region(pdev->resource[0].start, 29267b7ad03fSJulia Lawall resource_size(&pdev->resource[0])); 292790fccb52SAndrzej Pietrasiewicz 292890fccb52SAndrzej Pietrasiewicz return status; 292990fccb52SAndrzej Pietrasiewicz } 293090fccb52SAndrzej Pietrasiewicz 29319225afafSUwe Kleine-König static void omap_udc_remove(struct platform_device *pdev) 293290fccb52SAndrzej Pietrasiewicz { 293390fccb52SAndrzej Pietrasiewicz DECLARE_COMPLETION_ONSTACK(done); 293490fccb52SAndrzej Pietrasiewicz 293590fccb52SAndrzej Pietrasiewicz udc->done = &done; 293690fccb52SAndrzej Pietrasiewicz 293799f70036SAaro Koskinen usb_del_gadget_udc(&udc->gadget); 293890fccb52SAndrzej Pietrasiewicz 293999f70036SAaro Koskinen wait_for_completion(&done); 294090fccb52SAndrzej Pietrasiewicz 294190fccb52SAndrzej Pietrasiewicz release_mem_region(pdev->resource[0].start, 29427b7ad03fSJulia Lawall resource_size(&pdev->resource[0])); 294390fccb52SAndrzej Pietrasiewicz } 294490fccb52SAndrzej Pietrasiewicz 294590fccb52SAndrzej Pietrasiewicz /* suspend/resume/wakeup from sysfs (echo > power/state) or when the 294690fccb52SAndrzej Pietrasiewicz * system is forced into deep sleep 294790fccb52SAndrzej Pietrasiewicz * 294890fccb52SAndrzej Pietrasiewicz * REVISIT we should probably reject suspend requests when there's a host 294990fccb52SAndrzej Pietrasiewicz * session active, rather than disconnecting, at least on boards that can 295090fccb52SAndrzej Pietrasiewicz * report VBUS irqs (UDC_DEVSTAT.UDC_ATT). And in any case, we need to 295190fccb52SAndrzej Pietrasiewicz * make host resumes and VBUS detection trigger OMAP wakeup events; that 295290fccb52SAndrzej Pietrasiewicz * may involve talking to an external transceiver (e.g. isp1301). 295390fccb52SAndrzej Pietrasiewicz */ 295490fccb52SAndrzej Pietrasiewicz 295590fccb52SAndrzej Pietrasiewicz static int omap_udc_suspend(struct platform_device *dev, pm_message_t message) 295690fccb52SAndrzej Pietrasiewicz { 295790fccb52SAndrzej Pietrasiewicz u32 devstat; 295890fccb52SAndrzej Pietrasiewicz 295990fccb52SAndrzej Pietrasiewicz devstat = omap_readw(UDC_DEVSTAT); 296090fccb52SAndrzej Pietrasiewicz 296190fccb52SAndrzej Pietrasiewicz /* we're requesting 48 MHz clock if the pullup is enabled 296290fccb52SAndrzej Pietrasiewicz * (== we're attached to the host) and we're not suspended, 296390fccb52SAndrzej Pietrasiewicz * which would prevent entry to deep sleep... 296490fccb52SAndrzej Pietrasiewicz */ 296590fccb52SAndrzej Pietrasiewicz if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { 296690fccb52SAndrzej Pietrasiewicz WARNING("session active; suspend requires disconnect\n"); 296790fccb52SAndrzej Pietrasiewicz omap_pullup(&udc->gadget, 0); 296890fccb52SAndrzej Pietrasiewicz } 296990fccb52SAndrzej Pietrasiewicz 297090fccb52SAndrzej Pietrasiewicz return 0; 297190fccb52SAndrzej Pietrasiewicz } 297290fccb52SAndrzej Pietrasiewicz 297390fccb52SAndrzej Pietrasiewicz static int omap_udc_resume(struct platform_device *dev) 297490fccb52SAndrzej Pietrasiewicz { 297590fccb52SAndrzej Pietrasiewicz DBG("resume + wakeup/SRP\n"); 297690fccb52SAndrzej Pietrasiewicz omap_pullup(&udc->gadget, 1); 297790fccb52SAndrzej Pietrasiewicz 297890fccb52SAndrzej Pietrasiewicz /* maybe the host would enumerate us if we nudged it */ 297990fccb52SAndrzej Pietrasiewicz msleep(100); 298090fccb52SAndrzej Pietrasiewicz return omap_wakeup(&udc->gadget); 298190fccb52SAndrzej Pietrasiewicz } 298290fccb52SAndrzej Pietrasiewicz 298390fccb52SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 298490fccb52SAndrzej Pietrasiewicz 298590fccb52SAndrzej Pietrasiewicz static struct platform_driver udc_driver = { 298690fccb52SAndrzej Pietrasiewicz .probe = omap_udc_probe, 29879225afafSUwe Kleine-König .remove_new = omap_udc_remove, 298890fccb52SAndrzej Pietrasiewicz .suspend = omap_udc_suspend, 298990fccb52SAndrzej Pietrasiewicz .resume = omap_udc_resume, 299090fccb52SAndrzej Pietrasiewicz .driver = { 2991676edc20SCorentin Labbe .name = driver_name, 299290fccb52SAndrzej Pietrasiewicz }, 299390fccb52SAndrzej Pietrasiewicz }; 299490fccb52SAndrzej Pietrasiewicz 299590fccb52SAndrzej Pietrasiewicz module_platform_driver(udc_driver); 299690fccb52SAndrzej Pietrasiewicz 299790fccb52SAndrzej Pietrasiewicz MODULE_DESCRIPTION(DRIVER_DESC); 299890fccb52SAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 299990fccb52SAndrzej Pietrasiewicz MODULE_ALIAS("platform:omap_udc"); 3000