diff options
Diffstat (limited to 'drivers/usb/gadget/ci13xxx_udc.c')
-rw-r--r-- | drivers/usb/gadget/ci13xxx_udc.c | 681 |
1 files changed, 318 insertions, 363 deletions
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 98b36fc88c77..a1c67ae1572a 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -22,7 +22,6 @@ * - ENDPT: endpoint operations (Gadget API) * - GADGET: gadget operations (Gadget API) * - BUS: bus glue code, bus abstraction layer - * - PCI: PCI core interface and PCI resources (interrupts, memory...) * * Compile Options * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities @@ -60,11 +59,11 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> #include "ci13xxx_udc.h" @@ -75,15 +74,23 @@ /* ctrl register bank access */ static DEFINE_SPINLOCK(udc_lock); -/* driver name */ -#define UDC_DRIVER_NAME "ci13xxx_udc" - /* control endpoint description */ static const struct usb_endpoint_descriptor -ctrl_endpt_desc = { +ctrl_endpt_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), +}; + +static const struct usb_endpoint_descriptor +ctrl_endpt_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_CONTROL, .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), }; @@ -132,6 +139,9 @@ static struct { size_t size; /* bank size */ } hw_bank; +/* MSM specific */ +#define ABS_AHBBURST (0x0090UL) +#define ABS_AHBMODE (0x0098UL) /* UDC register map */ #define ABS_CAPLENGTH (0x100UL) #define ABS_HCCPARAMS (0x108UL) @@ -248,13 +258,7 @@ static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data) return (reg & mask) >> ffs_nr(mask); } -/** - * hw_device_reset: resets chip (execute without interruption) - * @base: register base address - * - * This function returns an error code - */ -static int hw_device_reset(void __iomem *base) +static int hw_device_init(void __iomem *base) { u32 reg; @@ -271,6 +275,28 @@ static int hw_device_reset(void __iomem *base) hw_bank.size += CAP_LAST; hw_bank.size /= sizeof(u32); + reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); + hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ + + if (hw_ep_max == 0 || hw_ep_max > ENDPT_MAX) + return -ENODEV; + + /* setup lock mode ? */ + + /* ENDPTSETUPSTAT is '0' by default */ + + /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ + + return 0; +} +/** + * hw_device_reset: resets chip (execute without interruption) + * @base: register base address + * + * This function returns an error code + */ +static int hw_device_reset(struct ci13xxx *udc) +{ /* should flush & stop before reset */ hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); @@ -279,6 +305,14 @@ static int hw_device_reset(void __iomem *base) while (hw_cread(CAP_USBCMD, USBCMD_RST)) udelay(10); /* not RTOS friendly */ + + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_RESET_EVENT); + + if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING) + hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS); + /* USBMODE should be configured step by step */ hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); @@ -290,18 +324,6 @@ static int hw_device_reset(void __iomem *base) return -ENODEV; } - reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); - if (reg == 0 || reg > ENDPT_MAX) - return -ENODEV; - - hw_ep_max = reg; /* cache hw ENDPT_MAX */ - - /* setup lock mode ? */ - - /* ENDPTSETUPSTAT is '0' by default */ - - /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ - return 0; } @@ -1186,16 +1208,17 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, } spin_lock_irqsave(udc->lock, flags); - for (i = 0; i < hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < hw_ep_max/2; i++) { + struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; + struct ci13xxx_ep *mEpTx = &udc->ci13xxx_ep[i + hw_ep_max/2]; n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: RX=%08X TX=%08X\n", - i, (u32)mEp->qh[RX].dma, (u32)mEp->qh[TX].dma); + i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { n += scnprintf(buf + n, PAGE_SIZE - n, " %04X: %08X %08X\n", j, - *((u32 *)mEp->qh[RX].ptr + j), - *((u32 *)mEp->qh[TX].ptr + j)); + *((u32 *)mEpRx->qh.ptr + j), + *((u32 *)mEpTx->qh.ptr + j)); } } spin_unlock_irqrestore(udc->lock, flags); @@ -1282,7 +1305,7 @@ static ssize_t show_requests(struct device *dev, struct device_attribute *attr, unsigned long flags; struct list_head *ptr = NULL; struct ci13xxx_req *req = NULL; - unsigned i, j, k, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); + unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); dbg_trace("[%s] %p\n", __func__, buf); if (attr == NULL || buf == NULL) { @@ -1292,22 +1315,20 @@ static ssize_t show_requests(struct device *dev, struct device_attribute *attr, spin_lock_irqsave(udc->lock, flags); for (i = 0; i < hw_ep_max; i++) - for (k = RX; k <= TX; k++) - list_for_each(ptr, &udc->ci13xxx_ep[i].qh[k].queue) - { - req = list_entry(ptr, - struct ci13xxx_req, queue); + list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) + { + req = list_entry(ptr, struct ci13xxx_req, queue); + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: TD=%08X %s\n", + i % hw_ep_max/2, (u32)req->dma, + ((i < hw_ep_max/2) ? "RX" : "TX")); + + for (j = 0; j < qSize; j++) n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: TD=%08X %s\n", - i, (u32)req->dma, - ((k == RX) ? "RX" : "TX")); - - for (j = 0; j < qSize; j++) - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X\n", j, - *((u32 *)req->ptr + j)); - } + " %04X: %08X\n", j, + *((u32 *)req->ptr + j)); + } spin_unlock_irqrestore(udc->lock, flags); return n; @@ -1449,19 +1470,19 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->ptr->page[0] = mReq->req.dma; for (i = 1; i < 5; i++) mReq->ptr->page[i] = - (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK; + (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; /* * QH configuration * At this point it's guaranteed exclusive access to qhead * (endpt is not primed) so it's no need to use tripwire */ - mEp->qh[mEp->dir].ptr->td.next = mReq->dma; /* TERMINATE = 0 */ - mEp->qh[mEp->dir].ptr->td.token &= ~TD_STATUS; /* clear status */ + mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */ + mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */ if (mReq->req.zero == 0) - mEp->qh[mEp->dir].ptr->cap |= QH_ZLT; + mEp->qh.ptr->cap |= QH_ZLT; else - mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; + mEp->qh.ptr->cap &= ~QH_ZLT; wmb(); /* synchronize before ep prime */ @@ -1531,16 +1552,16 @@ __acquires(mEp->lock) hw_ep_flush(mEp->num, mEp->dir); - while (!list_empty(&mEp->qh[mEp->dir].queue)) { + while (!list_empty(&mEp->qh.queue)) { /* pop oldest request */ struct ci13xxx_req *mReq = \ - list_entry(mEp->qh[mEp->dir].queue.next, + list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue); list_del_init(&mReq->queue); mReq->req.status = -ESHUTDOWN; - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); @@ -1557,26 +1578,21 @@ __acquires(mEp->lock) * Caller must hold lock */ static int _gadget_stop_activity(struct usb_gadget *gadget) -__releases(udc->lock) -__acquires(udc->lock) { struct usb_ep *ep; struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); - struct ci13xxx_ep *mEp = container_of(gadget->ep0, - struct ci13xxx_ep, ep); trace("%p", gadget); if (gadget == NULL) return -EINVAL; - spin_unlock(udc->lock); - /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); } - usb_ep_fifo_flush(gadget->ep0); + usb_ep_fifo_flush(&udc->ep0out.ep); + usb_ep_fifo_flush(&udc->ep0in.ep); udc->driver->disconnect(gadget); @@ -1584,15 +1600,14 @@ __acquires(udc->lock) gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - usb_ep_disable(gadget->ep0); + usb_ep_disable(&udc->ep0out.ep); + usb_ep_disable(&udc->ep0in.ep); - if (mEp->status != NULL) { - usb_ep_free_request(gadget->ep0, mEp->status); - mEp->status = NULL; + if (udc->status != NULL) { + usb_ep_free_request(&udc->ep0in.ep, udc->status); + udc->status = NULL; } - spin_lock(udc->lock); - return 0; } @@ -1609,7 +1624,6 @@ static void isr_reset_handler(struct ci13xxx *udc) __releases(udc->lock) __acquires(udc->lock) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[0]; int retval; trace("%p", udc); @@ -1621,6 +1635,7 @@ __acquires(udc->lock) dbg_event(0xFF, "BUS RST", 0); + spin_unlock(udc->lock); retval = _gadget_stop_activity(&udc->gadget); if (retval) goto done; @@ -1629,12 +1644,15 @@ __acquires(udc->lock) if (retval) goto done; - spin_unlock(udc->lock); - retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc); + retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc); + if (retval) + goto done; + + retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc); if (!retval) { - mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL); - if (mEp->status == NULL) { - usb_ep_disable(&mEp->ep); + udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC); + if (udc->status == NULL) { + usb_ep_disable(&udc->ep0out.ep); retval = -ENOMEM; } } @@ -1667,16 +1685,17 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) /** * isr_get_status_response: get_status request response - * @ep: endpoint + * @udc: udc struct * @setup: setup request packet * * This function returns an error code */ -static int isr_get_status_response(struct ci13xxx_ep *mEp, +static int isr_get_status_response(struct ci13xxx *udc, struct usb_ctrlrequest *setup) __releases(mEp->lock) __acquires(mEp->lock) { + struct ci13xxx_ep *mEp = &udc->ep0in; struct usb_request *req = NULL; gfp_t gfp_flags = GFP_ATOMIC; int dir, num, retval; @@ -1731,27 +1750,23 @@ __acquires(mEp->lock) /** * isr_setup_status_phase: queues the status phase of a setup transation - * @mEp: endpoint + * @udc: udc struct * * This function returns an error code */ -static int isr_setup_status_phase(struct ci13xxx_ep *mEp) +static int isr_setup_status_phase(struct ci13xxx *udc) __releases(mEp->lock) __acquires(mEp->lock) { int retval; + struct ci13xxx_ep *mEp; - trace("%p", mEp); - - /* mEp is always valid & configured */ - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; + trace("%p", udc); - mEp->status->no_interrupt = 1; + mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in; spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, mEp->status, GFP_ATOMIC); + retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC); spin_lock(mEp->lock); return retval; @@ -1773,11 +1788,11 @@ __acquires(mEp->lock) trace("%p", mEp); - if (list_empty(&mEp->qh[mEp->dir].queue)) + if (list_empty(&mEp->qh.queue)) return -EINVAL; /* pop oldest request */ - mReq = list_entry(mEp->qh[mEp->dir].queue.next, + mReq = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue); list_del_init(&mReq->queue); @@ -1789,18 +1804,20 @@ __acquires(mEp->lock) dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (!list_empty(&mEp->qh.queue)) { + struct ci13xxx_req* mReqEnq; + + mReqEnq = list_entry(mEp->qh.queue.next, + struct ci13xxx_req, queue); + _hardware_enqueue(mEp, mReqEnq); + } + + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); } - if (!list_empty(&mEp->qh[mEp->dir].queue)) { - mReq = list_entry(mEp->qh[mEp->dir].queue.next, - struct ci13xxx_req, queue); - _hardware_enqueue(mEp, mReq); - } - done: return retval; } @@ -1829,16 +1846,14 @@ __acquires(udc->lock) int type, num, err = -EINVAL; struct usb_ctrlrequest req; - if (mEp->desc == NULL) continue; /* not configured */ - if ((mEp->dir == RX && hw_test_and_clear_complete(i)) || - (mEp->dir == TX && hw_test_and_clear_complete(i + 16))) { + if (hw_test_and_clear_complete(i)) { err = isr_tr_complete_low(mEp); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (err > 0) /* needs status phase */ - err = isr_setup_status_phase(mEp); + err = isr_setup_status_phase(udc); if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); @@ -1859,15 +1874,22 @@ __acquires(udc->lock) continue; } + /* + * Flush data and handshake transactions of previous + * setup packet. + */ + _ep_nuke(&udc->ep0out); + _ep_nuke(&udc->ep0in); + /* read_setup_packet */ do { hw_test_and_set_setup_guard(); - memcpy(&req, &mEp->qh[RX].ptr->setup, sizeof(req)); + memcpy(&req, &mEp->qh.ptr->setup, sizeof(req)); } while (!hw_test_and_clear_setup_guard()); type = req.bRequestType; - mEp->dir = (type & USB_DIR_IN) ? TX : RX; + udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX; dbg_setup(_usb_addr(mEp), &req); @@ -1888,7 +1910,7 @@ __acquires(udc->lock) if (err) break; } - err = isr_setup_status_phase(mEp); + err = isr_setup_status_phase(udc); break; case USB_REQ_GET_STATUS: if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && @@ -1898,7 +1920,7 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 2 || le16_to_cpu(req.wValue) != 0) break; - err = isr_get_status_response(mEp, &req); + err = isr_get_status_response(udc, &req); break; case USB_REQ_SET_ADDRESS: if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) @@ -1909,7 +1931,7 @@ __acquires(udc->lock) err = hw_usb_set_address((u8)le16_to_cpu(req.wValue)); if (err) break; - err = isr_setup_status_phase(mEp); + err = isr_setup_status_phase(udc); break; case USB_REQ_SET_FEATURE: if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && @@ -1925,12 +1947,12 @@ __acquires(udc->lock) spin_lock(udc->lock); if (err) break; - err = isr_setup_status_phase(mEp); + err = isr_setup_status_phase(udc); break; default: delegate: if (req.wLength == 0) /* no data phase */ - mEp->dir = TX; + udc->ep0_dir = TX; spin_unlock(udc->lock); err = udc->driver->setup(&udc->gadget, &req); @@ -1961,7 +1983,7 @@ static int ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - int direction, retval = 0; + int retval = 0; unsigned long flags; trace("%p, %p", ep, desc); @@ -1975,7 +1997,7 @@ static int ep_enable(struct usb_ep *ep, mEp->desc = desc; - if (!list_empty(&mEp->qh[mEp->dir].queue)) + if (!list_empty(&mEp->qh.queue)) warn("enabling a non-empty endpoint!"); mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; @@ -1984,29 +2006,22 @@ static int ep_enable(struct usb_ep *ep, mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); - direction = mEp->dir; - do { - dbg_event(_usb_addr(mEp), "ENABLE", 0); + dbg_event(_usb_addr(mEp), "ENABLE", 0); - mEp->qh[mEp->dir].ptr->cap = 0; + mEp->qh.ptr->cap = 0; - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->qh[mEp->dir].ptr->cap |= QH_IOS; - else if (mEp->type == USB_ENDPOINT_XFER_ISOC) - mEp->qh[mEp->dir].ptr->cap &= ~QH_MULT; - else - mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; - - mEp->qh[mEp->dir].ptr->cap |= - (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; - mEp->qh[mEp->dir].ptr->td.next |= TD_TERMINATE; /* needed? */ + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->qh.ptr->cap |= QH_IOS; + else if (mEp->type == USB_ENDPOINT_XFER_ISOC) + mEp->qh.ptr->cap &= ~QH_MULT; + else + mEp->qh.ptr->cap &= ~QH_ZLT; - retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); + mEp->qh.ptr->cap |= + (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; + mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; - - } while (mEp->dir != direction); + retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); spin_unlock_irqrestore(mEp->lock, flags); return retval; @@ -2061,7 +2076,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); struct ci13xxx_req *mReq = NULL; - unsigned long flags; trace("%p, %i", ep, gfp_flags); @@ -2070,8 +2084,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) return NULL; } - spin_lock_irqsave(mEp->lock, flags); - mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags); if (mReq != NULL) { INIT_LIST_HEAD(&mReq->queue); @@ -2086,8 +2098,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); - spin_unlock_irqrestore(mEp->lock, flags); - return (mReq == NULL) ? NULL : &mReq->req; } @@ -2144,7 +2154,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, spin_lock_irqsave(mEp->lock, flags); if (mEp->type == USB_ENDPOINT_XFER_CONTROL && - !list_empty(&mEp->qh[mEp->dir].queue)) { + !list_empty(&mEp->qh.queue)) { _ep_nuke(mEp); retval = -EOVERFLOW; warn("endpoint ctrl %X nuked", _usb_addr(mEp)); @@ -2157,8 +2167,8 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, goto done; } - if (req->length > (4 * PAGE_SIZE)) { - req->length = (4 * PAGE_SIZE); + if (req->length > (4 * CI13XXX_PAGE_SIZE)) { + req->length = (4 * CI13XXX_PAGE_SIZE); retval = -EMSGSIZE; warn("request length truncated"); } @@ -2168,10 +2178,12 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, /* push request */ mReq->req.status = -EINPROGRESS; mReq->req.actual = 0; - list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); + list_add_tail(&mReq->queue, &mEp->qh.queue); + + if (list_is_singular(&mEp->qh.queue)) + retval = _hardware_enqueue(mEp, mReq); - retval = _hardware_enqueue(mEp, mReq); - if (retval == -EALREADY || retval == -EBUSY) { + if (retval == -EALREADY) { dbg_event(_usb_addr(mEp), "QUEUE", retval); retval = 0; } @@ -2195,7 +2207,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) trace("%p, %p", ep, req); if (ep == NULL || req == NULL || mEp->desc == NULL || - list_empty(&mReq->queue) || list_empty(&mEp->qh[mEp->dir].queue)) + list_empty(&mReq->queue) || list_empty(&mEp->qh.queue)) return -EINVAL; spin_lock_irqsave(mEp->lock, flags); @@ -2209,7 +2221,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) list_del_init(&mReq->queue); req->status = -ECONNRESET; - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); @@ -2240,7 +2252,7 @@ static int ep_set_halt(struct usb_ep *ep, int value) #ifndef STALL_IN /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX && - !list_empty(&mEp->qh[mEp->dir].queue)) { + !list_empty(&mEp->qh.queue)) { spin_unlock_irqrestore(mEp->lock, flags); return -EAGAIN; } @@ -2332,12 +2344,47 @@ static const struct usb_ep_ops usb_ep_ops = { /****************************************************************************** * GADGET block *****************************************************************************/ +static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + unsigned long flags; + int gadget_ready = 0; + + if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS)) + return -EOPNOTSUPP; + + spin_lock_irqsave(udc->lock, flags); + udc->vbus_active = is_active; + if (udc->driver) + gadget_ready = 1; + spin_unlock_irqrestore(udc->lock, flags); + + if (gadget_ready) { + if (is_active) { + pm_runtime_get_sync(&_gadget->dev); + hw_device_reset(udc); + hw_device_state(udc->ep0out.qh.dma); + } else { + hw_device_state(0); + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_STOPPED_EVENT); + _gadget_stop_activity(&udc->gadget); + pm_runtime_put_sync(&_gadget->dev); + } + } + + return 0; +} + /** * Device operations part of the API to the USB controller hardware, * which don't involve endpoints (or i/o) * Check "usb_gadget.h" for details */ -static const struct usb_gadget_ops usb_gadget_ops; +static const struct usb_gadget_ops usb_gadget_ops = { + .vbus_session = ci13xxx_vbus_session, +}; /** * usb_gadget_probe_driver: register a gadget driver @@ -2351,14 +2398,14 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct ci13xxx *udc = _udc; - unsigned long i, k, flags; + unsigned long flags; + int i, j; int retval = -ENOMEM; trace("%p", driver); if (driver == NULL || bind == NULL || - driver->unbind == NULL || driver->setup == NULL || driver->disconnect == NULL || driver->suspend == NULL || @@ -2372,13 +2419,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* alloc resources */ udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev, sizeof(struct ci13xxx_qh), - 64, PAGE_SIZE); + 64, CI13XXX_PAGE_SIZE); if (udc->qh_pool == NULL) return -ENOMEM; udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev, sizeof(struct ci13xxx_td), - 64, PAGE_SIZE); + 64, CI13XXX_PAGE_SIZE); if (udc->td_pool == NULL) { dma_pool_destroy(udc->qh_pool); udc->qh_pool = NULL; @@ -2389,47 +2436,48 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, info("hw_ep_max = %d", hw_ep_max); - udc->driver = driver; - udc->gadget.ops = NULL; udc->gadget.dev.driver = NULL; retval = 0; - for (i = 0; i < hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - - scnprintf(mEp->name, sizeof(mEp->name), "ep%i", (int)i); - - mEp->lock = udc->lock; - mEp->device = &udc->gadget.dev; - mEp->td_pool = udc->td_pool; - - mEp->ep.name = mEp->name; - mEp->ep.ops = &usb_ep_ops; - mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; - - /* this allocation cannot be random */ - for (k = RX; k <= TX; k++) { - INIT_LIST_HEAD(&mEp->qh[k].queue); - mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool, - GFP_KERNEL, - &mEp->qh[k].dma); - if (mEp->qh[k].ptr == NULL) + for (i = 0; i < hw_ep_max/2; i++) { + for (j = RX; j <= TX; j++) { + int k = i + j * hw_ep_max/2; + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k]; + + scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i, + (j == TX) ? "in" : "out"); + + mEp->lock = udc->lock; + mEp->device = &udc->gadget.dev; + mEp->td_pool = udc->td_pool; + + mEp->ep.name = mEp->name; + mEp->ep.ops = &usb_ep_ops; + mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; + + INIT_LIST_HEAD(&mEp->qh.queue); + spin_unlock_irqrestore(udc->lock, flags); + mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL, + &mEp->qh.dma); + spin_lock_irqsave(udc->lock, flags); + if (mEp->qh.ptr == NULL) retval = -ENOMEM; else - memset(mEp->qh[k].ptr, 0, - sizeof(*mEp->qh[k].ptr)); - } - if (i == 0) - udc->gadget.ep0 = &mEp->ep; - else + memset(mEp->qh.ptr, 0, sizeof(*mEp->qh.ptr)); + + /* skip ep0 out and in endpoints */ + if (i == 0) + continue; + list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + } } if (retval) goto done; + udc->gadget.ep0 = &udc->ep0in.ep; /* bind gadget */ driver->driver.bus = NULL; - udc->gadget.ops = &usb_gadget_ops; udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(udc->lock, flags); @@ -2437,17 +2485,28 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, spin_lock_irqsave(udc->lock, flags); if (retval) { - udc->gadget.ops = NULL; udc->gadget.dev.driver = NULL; goto done; } - retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma); + udc->driver = driver; + pm_runtime_get_sync(&udc->gadget.dev); + if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { + if (udc->vbus_active) { + if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) + hw_device_reset(udc); + } else { + pm_runtime_put_sync(&udc->gadget.dev); + goto done; + } + } + + retval = hw_device_state(udc->ep0out.qh.dma); + if (retval) + pm_runtime_put_sync(&udc->gadget.dev); done: spin_unlock_irqrestore(udc->lock, flags); - if (retval) - usb_gadget_unregister_driver(driver); return retval; } EXPORT_SYMBOL(usb_gadget_probe_driver); @@ -2460,7 +2519,7 @@ EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { struct ci13xxx *udc = _udc; - unsigned long i, k, flags; + unsigned long i, flags; trace("%p", driver); @@ -2475,35 +2534,35 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) spin_lock_irqsave(udc->lock, flags); - hw_device_state(0); - - /* unbind gadget */ - if (udc->gadget.ops != NULL) { + if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || + udc->vbus_active) { + hw_device_state(0); + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&udc->gadget); + pm_runtime_put(&udc->gadget.dev); + } - spin_unlock_irqrestore(udc->lock, flags); - driver->unbind(&udc->gadget); /* MAY SLEEP */ - spin_lock_irqsave(udc->lock, flags); + /* unbind gadget */ + spin_unlock_irqrestore(udc->lock, flags); + driver->unbind(&udc->gadget); /* MAY SLEEP */ + spin_lock_irqsave(udc->lock, flags); - udc->gadget.ops = NULL; - udc->gadget.dev.driver = NULL; - } + udc->gadget.dev.driver = NULL; /* free resources */ for (i = 0; i < hw_ep_max; i++) { struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - if (i == 0) - udc->gadget.ep0 = NULL; - else if (!list_empty(&mEp->ep.ep_list)) + if (!list_empty(&mEp->ep.ep_list)) list_del_init(&mEp->ep.ep_list); - for (k = RX; k <= TX; k++) - if (mEp->qh[k].ptr != NULL) - dma_pool_free(udc->qh_pool, - mEp->qh[k].ptr, mEp->qh[k].dma); + if (mEp->qh.ptr != NULL) + dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma); } + udc->gadget.ep0 = NULL; udc->driver = NULL; spin_unlock_irqrestore(udc->lock, flags); @@ -2544,6 +2603,14 @@ static irqreturn_t udc_irq(void) } spin_lock(udc->lock); + + if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) { + if (hw_cread(CAP_USBMODE, USBMODE_CM) != + USBMODE_CM_DEVICE) { + spin_unlock(udc->lock); + return IRQ_NONE; + } + } intr = hw_test_and_clear_intr_active(); if (intr) { isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr; @@ -2602,14 +2669,16 @@ static void udc_release(struct device *dev) * No interrupts active, the IRQ has not been requested yet * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask */ -static int udc_probe(struct device *dev, void __iomem *regs, const char *name) +static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, + void __iomem *regs) { struct ci13xxx *udc; int retval = 0; trace("%p, %p, %p", dev, regs, name); - if (dev == NULL || regs == NULL || name == NULL) + if (dev == NULL || regs == NULL || driver == NULL || + driver->name == NULL) return -EINVAL; udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); @@ -2617,42 +2686,77 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name) return -ENOMEM; udc->lock = &udc_lock; + udc->regs = regs; + udc->udc_driver = driver; - retval = hw_device_reset(regs); - if (retval) - goto done; - - udc->gadget.ops = NULL; + udc->gadget.ops = &usb_gadget_ops; udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.is_dualspeed = 1; udc->gadget.is_otg = 0; - udc->gadget.name = name; + udc->gadget.name = driver->name; INIT_LIST_HEAD(&udc->gadget.ep_list); udc->gadget.ep0 = NULL; dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.dev.dma_mask = dev->dma_mask; + udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; udc->gadget.dev.parent = dev; udc->gadget.dev.release = udc_release; + retval = hw_device_init(regs); + if (retval < 0) + goto free_udc; + + udc->transceiver = otg_get_transceiver(); + + if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (udc->transceiver == NULL) { + retval = -ENODEV; + goto free_udc; + } + } + + if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { + retval = hw_device_reset(udc); + if (retval) + goto put_transceiver; + } + retval = device_register(&udc->gadget.dev); - if (retval) - goto done; + if (retval) { + put_device(&udc->gadget.dev); + goto put_transceiver; + } #ifdef CONFIG_USB_GADGET_DEBUG_FILES retval = dbg_create_files(&udc->gadget.dev); #endif - if (retval) { - device_unregister(&udc->gadget.dev); - goto done; + if (retval) + goto unreg_device; + + if (udc->transceiver) { + retval = otg_set_peripheral(udc->transceiver, &udc->gadget); + if (retval) + goto remove_dbg; } + pm_runtime_no_callbacks(&udc->gadget.dev); + pm_runtime_enable(&udc->gadget.dev); _udc = udc; return retval; - done: err("error = %i", retval); +remove_dbg: +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + dbg_remove_files(&udc->gadget.dev); +#endif +unreg_device: + device_unregister(&udc->gadget.dev); +put_transceiver: + if (udc->transceiver) + otg_put_transceiver(udc->transceiver); +free_udc: kfree(udc); _udc = NULL; return retval; @@ -2672,6 +2776,10 @@ static void udc_remove(void) return; } + if (udc->transceiver) { + otg_set_peripheral(udc->transceiver, &udc->gadget); + otg_put_transceiver(udc->transceiver); + } #ifdef CONFIG_USB_GADGET_DEBUG_FILES dbg_remove_files(&udc->gadget.dev); #endif @@ -2680,156 +2788,3 @@ static void udc_remove(void) kfree(udc); _udc = NULL; } - -/****************************************************************************** - * PCI block - *****************************************************************************/ -/** - * ci13xxx_pci_irq: interrut handler - * @irq: irq number - * @pdev: USB Device Controller interrupt source - * - * This function returns IRQ_HANDLED if the IRQ has been handled - * This is an ISR don't trace, use attribute interface instead - */ -static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev) -{ - if (irq == 0) { - dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!"); - return IRQ_HANDLED; - } - return udc_irq(); -} - -/** - * ci13xxx_pci_probe: PCI probe - * @pdev: USB device controller being probed - * @id: PCI hotplug ID connecting controller to UDC framework - * - * This function returns an error code - * Allocates basic PCI resources for this USB device controller, and then - * invokes the udc_probe() method to start the UDC associated with it - */ -static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - void __iomem *regs = NULL; - int retval = 0; - - if (id == NULL) - return -EINVAL; - - retval = pci_enable_device(pdev); - if (retval) - goto done; - - if (!pdev->irq) { - dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!"); - retval = -ENODEV; - goto disable_device; - } - - retval = pci_request_regions(pdev, UDC_DRIVER_NAME); - if (retval) - goto disable_device; - - /* BAR 0 holds all the registers */ - regs = pci_iomap(pdev, 0, 0); - if (!regs) { - dev_err(&pdev->dev, "Error mapping memory!"); - retval = -EFAULT; - goto release_regions; - } - pci_set_drvdata(pdev, (__force void *)regs); - - pci_set_master(pdev); - pci_try_set_mwi(pdev); - - retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME); - if (retval) - goto iounmap; - - /* our device does not have MSI capability */ - - retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED, - UDC_DRIVER_NAME, pdev); - if (retval) - goto gadget_remove; - - return 0; - - gadget_remove: - udc_remove(); - iounmap: - pci_iounmap(pdev, regs); - release_regions: - pci_release_regions(pdev); - disable_device: - pci_disable_device(pdev); - done: - return retval; -} - -/** - * ci13xxx_pci_remove: PCI remove - * @pdev: USB Device Controller being removed - * - * Reverses the effect of ci13xxx_pci_probe(), - * first invoking the udc_remove() and then releases - * all PCI resources allocated for this USB device controller - */ -static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) -{ - free_irq(pdev->irq, pdev); - udc_remove(); - pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev)); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -/** - * PCI device table - * PCI device structure - * - * Check "pci.h" for details - */ -static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { - { PCI_DEVICE(0x153F, 0x1004) }, - { PCI_DEVICE(0x153F, 0x1006) }, - { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table); - -static struct pci_driver ci13xxx_pci_driver = { - .name = UDC_DRIVER_NAME, - .id_table = ci13xxx_pci_id_table, - .probe = ci13xxx_pci_probe, - .remove = __devexit_p(ci13xxx_pci_remove), -}; - -/** - * ci13xxx_pci_init: module init - * - * Driver load - */ -static int __init ci13xxx_pci_init(void) -{ - return pci_register_driver(&ci13xxx_pci_driver); -} -module_init(ci13xxx_pci_init); - -/** - * ci13xxx_pci_exit: module exit - * - * Driver unload - */ -static void __exit ci13xxx_pci_exit(void) -{ - pci_unregister_driver(&ci13xxx_pci_driver); -} -module_exit(ci13xxx_pci_exit); - -MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>"); -MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("June 2008"); |