diff options
Diffstat (limited to 'drivers/usb/chipidea/udc.c')
| -rw-r--r-- | drivers/usb/chipidea/udc.c | 1435 |
1 files changed, 731 insertions, 704 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 2f45bba8561..b8125aa64ad 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -13,26 +13,22 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dmapool.h> -#include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> +#include <linux/irqreturn.h> #include <linux/kernel.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 <linux/usb/otg-fsm.h> #include <linux/usb/chipidea.h> #include "ci.h" #include "udc.h" #include "bits.h" #include "debug.h" +#include "otg.h" +#include "otg_fsm.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -67,7 +63,7 @@ static inline int hw_ep_bit(int num, int dir) return num + (dir ? 16 : 0); } -static inline int ep_to_bit(struct ci13xxx *ci, int n) +static inline int ep_to_bit(struct ci_hdrc *ci, int n) { int fill = 16 - ci->hw_ep_max / 2; @@ -83,15 +79,17 @@ static inline int ep_to_bit(struct ci13xxx *ci, int n) * * This function returns an error code */ -static int hw_device_state(struct ci13xxx *ci, u32 dma) +static int hw_device_state(struct ci_hdrc *ci, u32 dma) { if (dma) { hw_write(ci, OP_ENDPTLISTADDR, ~0, dma); /* interrupt, error, port change, reset, sleep/suspend */ hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { hw_write(ci, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); } return 0; } @@ -103,13 +101,13 @@ static int hw_device_state(struct ci13xxx *ci, u32 dma) * * This function returns an error code */ -static int hw_ep_flush(struct ci13xxx *ci, int num, int dir) +static int hw_ep_flush(struct ci_hdrc *ci, int num, int dir) { int n = hw_ep_bit(num, dir); do { /* flush any pending transfer */ - hw_write(ci, OP_ENDPTFLUSH, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTFLUSH, ~0, BIT(n)); while (hw_read(ci, OP_ENDPTFLUSH, BIT(n))) cpu_relax(); } while (hw_read(ci, OP_ENDPTSTAT, BIT(n))); @@ -124,7 +122,7 @@ static int hw_ep_flush(struct ci13xxx *ci, int num, int dir) * * This function returns an error code */ -static int hw_ep_disable(struct ci13xxx *ci, int num, int dir) +static int hw_ep_disable(struct ci_hdrc *ci, int num, int dir) { hw_ep_flush(ci, num, dir); hw_write(ci, OP_ENDPTCTRL + num, @@ -140,13 +138,13 @@ static int hw_ep_disable(struct ci13xxx *ci, int num, int dir) * * This function returns an error code */ -static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) +static int hw_ep_enable(struct ci_hdrc *ci, int num, int dir, int type) { u32 mask, data; if (dir) { mask = ENDPTCTRL_TXT; /* type */ - data = type << ffs_nr(mask); + data = type << __ffs(mask); mask |= ENDPTCTRL_TXS; /* unstall */ mask |= ENDPTCTRL_TXR; /* reset data toggle */ @@ -155,7 +153,7 @@ static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) data |= ENDPTCTRL_TXE; } else { mask = ENDPTCTRL_RXT; /* type */ - data = type << ffs_nr(mask); + data = type << __ffs(mask); mask |= ENDPTCTRL_RXS; /* unstall */ mask |= ENDPTCTRL_RXR; /* reset data toggle */ @@ -174,7 +172,7 @@ static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) * * This function returns 1 if endpoint halted */ -static int hw_ep_get_halt(struct ci13xxx *ci, int num, int dir) +static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir) { u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; @@ -182,19 +180,6 @@ static int hw_ep_get_halt(struct ci13xxx *ci, int num, int dir) } /** - * hw_test_and_clear_setup_status: test & clear setup status (execute without - * interruption) - * @n: endpoint number - * - * This function returns setup status - */ -static int hw_test_and_clear_setup_status(struct ci13xxx *ci, int n) -{ - n = ep_to_bit(ci, n); - return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); -} - -/** * hw_ep_prime: primes endpoint (execute without interruption) * @num: endpoint number * @dir: endpoint direction @@ -202,14 +187,14 @@ static int hw_test_and_clear_setup_status(struct ci13xxx *ci, int n) * * This function returns an error code */ -static int hw_ep_prime(struct ci13xxx *ci, int num, int dir, int is_ctrl) +static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl) { int n = hw_ep_bit(num, dir); if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; - hw_write(ci, OP_ENDPTPRIME, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTPRIME, ~0, BIT(n)); while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); @@ -229,13 +214,13 @@ static int hw_ep_prime(struct ci13xxx *ci, int num, int dir, int is_ctrl) * * This function returns an error code */ -static int hw_ep_set_halt(struct ci13xxx *ci, int num, int dir, int value) +static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value) { if (value != 0 && value != 1) return -EINVAL; do { - enum ci13xxx_regs reg = OP_ENDPTCTRL + num; + enum ci_hw_regs reg = OP_ENDPTCTRL + num; u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; @@ -252,40 +237,20 @@ static int hw_ep_set_halt(struct ci13xxx *ci, int num, int dir, int value) * * This function returns true if high speed port */ -static int hw_port_is_high_speed(struct ci13xxx *ci) +static int hw_port_is_high_speed(struct ci_hdrc *ci) { return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) : hw_read(ci, OP_PORTSC, PORTSC_HSP); } /** - * hw_read_intr_enable: returns interrupt enable register - * - * This function returns register data - */ -static u32 hw_read_intr_enable(struct ci13xxx *ci) -{ - return hw_read(ci, OP_USBINTR, ~0); -} - -/** - * hw_read_intr_status: returns interrupt status register - * - * This function returns register data - */ -static u32 hw_read_intr_status(struct ci13xxx *ci) -{ - return hw_read(ci, OP_USBSTS, ~0); -} - -/** * hw_test_and_clear_complete: test & clear complete status (execute without * interruption) * @n: endpoint number * * This function returns complete status */ -static int hw_test_and_clear_complete(struct ci13xxx *ci, int n) +static int hw_test_and_clear_complete(struct ci_hdrc *ci, int n) { n = ep_to_bit(ci, n); return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n)); @@ -297,7 +262,7 @@ static int hw_test_and_clear_complete(struct ci13xxx *ci, int n) * * This function returns active interrutps */ -static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci) +static u32 hw_test_and_clear_intr_active(struct ci_hdrc *ci) { u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci); @@ -305,25 +270,13 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci) return reg; } -static void hw_enable_vbus_intr(struct ci13xxx *ci) -{ - hw_write(ci, OP_OTGSC, OTGSC_AVVIS, OTGSC_AVVIS); - hw_write(ci, OP_OTGSC, OTGSC_AVVIE, OTGSC_AVVIE); - queue_work(ci->wq, &ci->vbus_work); -} - -static void hw_disable_vbus_intr(struct ci13xxx *ci) -{ - hw_write(ci, OP_OTGSC, OTGSC_AVVIE, 0); -} - /** * hw_test_and_clear_setup_guard: test & clear setup guard (execute without * interruption) * * This function returns guard value */ -static int hw_test_and_clear_setup_guard(struct ci13xxx *ci) +static int hw_test_and_clear_setup_guard(struct ci_hdrc *ci) { return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0); } @@ -334,7 +287,7 @@ static int hw_test_and_clear_setup_guard(struct ci13xxx *ci) * * This function returns guard value */ -static int hw_test_and_set_setup_guard(struct ci13xxx *ci) +static int hw_test_and_set_setup_guard(struct ci_hdrc *ci) { return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); } @@ -346,10 +299,10 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *ci) * This function explicitly sets the address, without the "USBADRA" (advance) * feature, which is not supported by older versions of the controller. */ -static void hw_usb_set_address(struct ci13xxx *ci, u8 value) +static void hw_usb_set_address(struct ci_hdrc *ci, u8 value) { hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR, - value << ffs_nr(DEVICEADDR_USBADR)); + value << __ffs(DEVICEADDR_USBADR)); } /** @@ -358,7 +311,7 @@ static void hw_usb_set_address(struct ci13xxx *ci, u8 value) * * This function returns an error code */ -static int hw_usb_reset(struct ci13xxx *ci) +static int hw_usb_reset(struct ci_hdrc *ci) { hw_usb_set_address(ci, 0); @@ -383,24 +336,71 @@ static int hw_usb_reset(struct ci13xxx *ci) return 0; } -static void vbus_work(struct work_struct *work) +/****************************************************************************** + * UTIL block + *****************************************************************************/ + +static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, + unsigned length) { - struct ci13xxx *ci = container_of(work, struct ci13xxx, vbus_work); + int i; + u32 temp; + struct td_node *lastnode, *node = kzalloc(sizeof(struct td_node), + GFP_ATOMIC); - if (hw_read(ci, OP_OTGSC, OTGSC_AVV)) - usb_gadget_vbus_connect(&ci->gadget); - else - usb_gadget_vbus_disconnect(&ci->gadget); + if (node == NULL) + return -ENOMEM; + + node->ptr = dma_pool_alloc(hwep->td_pool, GFP_ATOMIC, + &node->dma); + if (node->ptr == NULL) { + kfree(node); + return -ENOMEM; + } + + memset(node->ptr, 0, sizeof(struct ci_hw_td)); + node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES)); + node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES); + node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE); + if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX) { + u32 mul = hwreq->req.length / hwep->ep.maxpacket; + + if (hwreq->req.length == 0 + || hwreq->req.length % hwep->ep.maxpacket) + mul++; + node->ptr->token |= mul << __ffs(TD_MULTO); + } + + temp = (u32) (hwreq->req.dma + hwreq->req.actual); + if (length) { + node->ptr->page[0] = cpu_to_le32(temp); + for (i = 1; i < TD_PAGE_COUNT; i++) { + u32 page = temp + i * CI_HDRC_PAGE_SIZE; + page &= ~TD_RESERVED_MASK; + node->ptr->page[i] = cpu_to_le32(page); + } + } + + hwreq->req.actual += length; + + if (!list_empty(&hwreq->tds)) { + /* get the last entry */ + lastnode = list_entry(hwreq->tds.prev, + struct td_node, td); + lastnode->ptr->next = cpu_to_le32(node->dma); + } + + INIT_LIST_HEAD(&node->td); + list_add_tail(&node->td, &hwreq->tds); + + return 0; } -/****************************************************************************** - * UTIL block - *****************************************************************************/ /** * _usb_addr: calculates endpoint address from direction & number * @ep: endpoint */ -static inline u8 _usb_addr(struct ci13xxx_ep *ep) +static inline u8 _usb_addr(struct ci_hw_ep *ep) { return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num; } @@ -408,70 +408,73 @@ static inline u8 _usb_addr(struct ci13xxx_ep *ep) /** * _hardware_queue: configures a request at hardware level * @gadget: gadget - * @mEp: endpoint + * @hwep: endpoint * * This function returns an error code */ -static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) +static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { - struct ci13xxx *ci = mEp->ci; - unsigned i; + struct ci_hdrc *ci = hwep->ci; int ret = 0; - unsigned length = mReq->req.length; + unsigned rest = hwreq->req.length; + int pages = TD_PAGE_COUNT; + struct td_node *firstnode, *lastnode; /* don't queue twice */ - if (mReq->req.status == -EALREADY) + if (hwreq->req.status == -EALREADY) return -EALREADY; - mReq->req.status = -EALREADY; - - if (mReq->req.zero && length && (length % mEp->ep.maxpacket == 0)) { - mReq->zptr = dma_pool_alloc(mEp->td_pool, GFP_ATOMIC, - &mReq->zdma); - if (mReq->zptr == NULL) - return -ENOMEM; + hwreq->req.status = -EALREADY; - memset(mReq->zptr, 0, sizeof(*mReq->zptr)); - mReq->zptr->next = TD_TERMINATE; - mReq->zptr->token = TD_STATUS_ACTIVE; - if (!mReq->req.no_interrupt) - mReq->zptr->token |= TD_IOC; - } - ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir); + ret = usb_gadget_map_request(&ci->gadget, &hwreq->req, hwep->dir); if (ret) return ret; /* - * TD configuration - * TODO - handle requests which spawns into several TDs + * The first buffer could be not page aligned. + * In that case we have to span into one extra td. */ - memset(mReq->ptr, 0, sizeof(*mReq->ptr)); - mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES); - mReq->ptr->token &= TD_TOTAL_BYTES; - mReq->ptr->token |= TD_STATUS_ACTIVE; - if (mReq->zptr) { - mReq->ptr->next = mReq->zdma; - } else { - mReq->ptr->next = TD_TERMINATE; - if (!mReq->req.no_interrupt) - mReq->ptr->token |= TD_IOC; + if (hwreq->req.dma % PAGE_SIZE) + pages--; + + if (rest == 0) + add_td_to_list(hwep, hwreq, 0); + + while (rest > 0) { + unsigned count = min(hwreq->req.length - hwreq->req.actual, + (unsigned)(pages * CI_HDRC_PAGE_SIZE)); + add_td_to_list(hwep, hwreq, count); + rest -= count; } - mReq->ptr->page[0] = mReq->req.dma; - for (i = 1; i < 5; i++) - mReq->ptr->page[i] = - (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; - - if (!list_empty(&mEp->qh.queue)) { - struct ci13xxx_req *mReqPrev; - int n = hw_ep_bit(mEp->num, mEp->dir); + + if (hwreq->req.zero && hwreq->req.length + && (hwreq->req.length % hwep->ep.maxpacket == 0)) + add_td_to_list(hwep, hwreq, 0); + + firstnode = list_first_entry(&hwreq->tds, struct td_node, td); + + lastnode = list_entry(hwreq->tds.prev, + struct td_node, td); + + lastnode->ptr->next = cpu_to_le32(TD_TERMINATE); + if (!hwreq->req.no_interrupt) + lastnode->ptr->token |= cpu_to_le32(TD_IOC); + wmb(); + + hwreq->req.actual = 0; + if (!list_empty(&hwep->qh.queue)) { + struct ci_hw_req *hwreqprev; + int n = hw_ep_bit(hwep->num, hwep->dir); int tmp_stat; + struct td_node *prevlastnode; + u32 next = firstnode->dma & TD_ADDR_MASK; + + hwreqprev = list_entry(hwep->qh.queue.prev, + struct ci_hw_req, queue); + prevlastnode = list_entry(hwreqprev->tds.prev, + struct td_node, td); - mReqPrev = list_entry(mEp->qh.queue.prev, - struct ci13xxx_req, queue); - if (mReqPrev->zptr) - mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK; - else - mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK; + prevlastnode->ptr->next = cpu_to_le32(next); wmb(); if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) goto done; @@ -485,91 +488,153 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) } /* QH configuration */ - mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */ - mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */ - mEp->qh.ptr->cap |= QH_ZLT; + hwep->qh.ptr->td.next = cpu_to_le32(firstnode->dma); + hwep->qh.ptr->td.token &= + cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE)); + + if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == RX) { + u32 mul = hwreq->req.length / hwep->ep.maxpacket; + + if (hwreq->req.length == 0 + || hwreq->req.length % hwep->ep.maxpacket) + mul++; + hwep->qh.ptr->cap |= mul << __ffs(QH_MULT); + } wmb(); /* synchronize before ep prime */ - ret = hw_ep_prime(ci, mEp->num, mEp->dir, - mEp->type == USB_ENDPOINT_XFER_CONTROL); + ret = hw_ep_prime(ci, hwep->num, hwep->dir, + hwep->type == USB_ENDPOINT_XFER_CONTROL); done: return ret; } +/* + * free_pending_td: remove a pending request for the endpoint + * @hwep: endpoint + */ +static void free_pending_td(struct ci_hw_ep *hwep) +{ + struct td_node *pending = hwep->pending_td; + + dma_pool_free(hwep->td_pool, pending->ptr, pending->dma); + hwep->pending_td = NULL; + kfree(pending); +} + /** * _hardware_dequeue: handles a request at hardware level * @gadget: gadget - * @mEp: endpoint + * @hwep: endpoint * * This function returns an error code */ -static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) +static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { - if (mReq->req.status != -EALREADY) + u32 tmptoken; + struct td_node *node, *tmpnode; + unsigned remaining_length; + unsigned actual = hwreq->req.length; + + if (hwreq->req.status != -EALREADY) return -EINVAL; - if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0) - return -EBUSY; + hwreq->req.status = 0; - if (mReq->zptr) { - if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0) + list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { + tmptoken = le32_to_cpu(node->ptr->token); + if ((TD_STATUS_ACTIVE & tmptoken) != 0) { + hwreq->req.status = -EALREADY; return -EBUSY; - dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma); - mReq->zptr = NULL; - } + } - mReq->req.status = 0; + remaining_length = (tmptoken & TD_TOTAL_BYTES); + remaining_length >>= __ffs(TD_TOTAL_BYTES); + actual -= remaining_length; - usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); + hwreq->req.status = tmptoken & TD_STATUS; + if ((TD_STATUS_HALTED & hwreq->req.status)) { + hwreq->req.status = -EPIPE; + break; + } else if ((TD_STATUS_DT_ERR & hwreq->req.status)) { + hwreq->req.status = -EPROTO; + break; + } else if ((TD_STATUS_TR_ERR & hwreq->req.status)) { + hwreq->req.status = -EILSEQ; + break; + } - mReq->req.status = mReq->ptr->token & TD_STATUS; - if ((TD_STATUS_HALTED & mReq->req.status) != 0) - mReq->req.status = -1; - else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) - mReq->req.status = -1; - else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) - mReq->req.status = -1; + if (remaining_length) { + if (hwep->dir) { + hwreq->req.status = -EPROTO; + break; + } + } + /* + * As the hardware could still address the freed td + * which will run the udc unusable, the cleanup of the + * td has to be delayed by one. + */ + if (hwep->pending_td) + free_pending_td(hwep); + + hwep->pending_td = node; + list_del_init(&node->td); + } - mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; - mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); - mReq->req.actual = mReq->req.length - mReq->req.actual; - mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; + usb_gadget_unmap_request(&hwep->ci->gadget, &hwreq->req, hwep->dir); - return mReq->req.actual; + hwreq->req.actual += actual; + + if (hwreq->req.status) + return hwreq->req.status; + + return hwreq->req.actual; } /** * _ep_nuke: dequeues all endpoint requests - * @mEp: endpoint + * @hwep: endpoint * * This function returns an error code * Caller must hold lock */ -static int _ep_nuke(struct ci13xxx_ep *mEp) -__releases(mEp->lock) -__acquires(mEp->lock) +static int _ep_nuke(struct ci_hw_ep *hwep) +__releases(hwep->lock) +__acquires(hwep->lock) { - if (mEp == NULL) + struct td_node *node, *tmpnode; + if (hwep == NULL) return -EINVAL; - hw_ep_flush(mEp->ci, mEp->num, mEp->dir); + hw_ep_flush(hwep->ci, hwep->num, hwep->dir); - while (!list_empty(&mEp->qh.queue)) { + while (!list_empty(&hwep->qh.queue)) { /* pop oldest request */ - struct ci13xxx_req *mReq = \ - list_entry(mEp->qh.queue.next, - struct ci13xxx_req, queue); - list_del_init(&mReq->queue); - mReq->req.status = -ESHUTDOWN; - - if (mReq->req.complete != NULL) { - spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); - spin_lock(mEp->lock); + struct ci_hw_req *hwreq = list_entry(hwep->qh.queue.next, + struct ci_hw_req, queue); + + list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { + dma_pool_free(hwep->td_pool, node->ptr, node->dma); + list_del_init(&node->td); + node->ptr = NULL; + kfree(node); + } + + list_del_init(&hwreq->queue); + hwreq->req.status = -ESHUTDOWN; + + if (hwreq->req.complete != NULL) { + spin_unlock(hwep->lock); + hwreq->req.complete(&hwep->ep, &hwreq->req); + spin_lock(hwep->lock); } } + + if (hwep->pending_td) + free_pending_td(hwep); + return 0; } @@ -582,7 +647,7 @@ __acquires(mEp->lock) static int _gadget_stop_activity(struct usb_gadget *gadget) { struct usb_ep *ep; - struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; spin_lock_irqsave(&ci->lock, flags); @@ -598,9 +663,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) usb_ep_fifo_flush(&ci->ep0out->ep); usb_ep_fifo_flush(&ci->ep0in->ep); - if (ci->driver) - ci->driver->disconnect(gadget); - /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); @@ -623,15 +685,18 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) * * This function resets USB engine after a bus reset occurred */ -static void isr_reset_handler(struct ci13xxx *ci) +static void isr_reset_handler(struct ci_hdrc *ci) __releases(ci->lock) __acquires(ci->lock) { int retval; - dbg_event(0xFF, "BUS RST", 0); - spin_unlock(&ci->lock); + if (ci->gadget.speed != USB_SPEED_UNKNOWN) { + if (ci->driver) + ci->driver->disconnect(&ci->gadget); + } + retval = _gadget_stop_activity(&ci->gadget); if (retval) goto done; @@ -644,6 +709,8 @@ __acquires(ci->lock) if (ci->status == NULL) retval = -ENOMEM; + usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT); + done: spin_lock(&ci->lock); @@ -668,28 +735,82 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) } /** + * _ep_queue: queues (submits) an I/O request to an endpoint + * + * Caller must hold lock + */ +static int _ep_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t __maybe_unused gfp_flags) +{ + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); + struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req); + struct ci_hdrc *ci = hwep->ci; + int retval = 0; + + if (ep == NULL || req == NULL || hwep->ep.desc == NULL) + return -EINVAL; + + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) { + if (req->length) + hwep = (ci->ep0_dir == RX) ? + ci->ep0out : ci->ep0in; + if (!list_empty(&hwep->qh.queue)) { + _ep_nuke(hwep); + retval = -EOVERFLOW; + dev_warn(hwep->ci->dev, "endpoint ctrl %X nuked\n", + _usb_addr(hwep)); + } + } + + if (usb_endpoint_xfer_isoc(hwep->ep.desc) && + hwreq->req.length > (1 + hwep->ep.mult) * hwep->ep.maxpacket) { + dev_err(hwep->ci->dev, "request length too big for isochronous\n"); + return -EMSGSIZE; + } + + /* first nuke then test link, e.g. previous status has not sent */ + if (!list_empty(&hwreq->queue)) { + dev_err(hwep->ci->dev, "request already in queue\n"); + return -EBUSY; + } + + /* push request */ + hwreq->req.status = -EINPROGRESS; + hwreq->req.actual = 0; + + retval = _hardware_enqueue(hwep, hwreq); + + if (retval == -EALREADY) + retval = 0; + if (!retval) + list_add_tail(&hwreq->queue, &hwep->qh.queue); + + return retval; +} + +/** * isr_get_status_response: get_status request response * @ci: ci struct * @setup: setup request packet * * This function returns an error code */ -static int isr_get_status_response(struct ci13xxx *ci, +static int isr_get_status_response(struct ci_hdrc *ci, struct usb_ctrlrequest *setup) -__releases(mEp->lock) -__acquires(mEp->lock) +__releases(hwep->lock) +__acquires(hwep->lock) { - struct ci13xxx_ep *mEp = ci->ep0in; + struct ci_hw_ep *hwep = ci->ep0in; struct usb_request *req = NULL; gfp_t gfp_flags = GFP_ATOMIC; int dir, num, retval; - if (mEp == NULL || setup == NULL) + if (hwep == NULL || setup == NULL) return -EINVAL; - spin_unlock(mEp->lock); - req = usb_ep_alloc_request(&mEp->ep, gfp_flags); - spin_lock(mEp->lock); + spin_unlock(hwep->lock); + req = usb_ep_alloc_request(&hwep->ep, gfp_flags); + spin_lock(hwep->lock); if (req == NULL) return -ENOMEM; @@ -704,7 +825,6 @@ __acquires(mEp->lock) if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Assume that device is bus powered for now. */ *(u16 *)req->buf = ci->remote_wakeup << 1; - retval = 0; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? @@ -714,9 +834,7 @@ __acquires(mEp->lock) } /* else do nothing; reserved for future use */ - spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, req, gfp_flags); - spin_lock(mEp->lock); + retval = _ep_queue(&hwep->ep, req, gfp_flags); if (retval) goto err_free_buf; @@ -725,9 +843,9 @@ __acquires(mEp->lock) err_free_buf: kfree(req->buf); err_free_req: - spin_unlock(mEp->lock); - usb_ep_free_request(&mEp->ep, req); - spin_lock(mEp->lock); + spin_unlock(hwep->lock); + usb_ep_free_request(&hwep->ep, req); + spin_lock(hwep->lock); return retval; } @@ -742,12 +860,14 @@ __acquires(mEp->lock) static void isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx *ci = req->context; + struct ci_hdrc *ci = req->context; unsigned long flags; if (ci->setaddr) { hw_usb_set_address(ci, ci->address); ci->setaddr = false; + if (ci->address) + usb_gadget_set_state(&ci->gadget, USB_STATE_ADDRESS); } spin_lock_irqsave(&ci->lock, flags); @@ -762,248 +882,253 @@ isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) * * This function returns an error code */ -static int isr_setup_status_phase(struct ci13xxx *ci) -__releases(mEp->lock) -__acquires(mEp->lock) +static int isr_setup_status_phase(struct ci_hdrc *ci) { int retval; - struct ci13xxx_ep *mEp; + struct ci_hw_ep *hwep; - mEp = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in; + hwep = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in; ci->status->context = ci; ci->status->complete = isr_setup_status_complete; - spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC); - spin_lock(mEp->lock); + retval = _ep_queue(&hwep->ep, ci->status, GFP_ATOMIC); return retval; } /** * isr_tr_complete_low: transaction complete low level handler - * @mEp: endpoint + * @hwep: endpoint * * This function returns an error code * Caller must hold lock */ -static int isr_tr_complete_low(struct ci13xxx_ep *mEp) -__releases(mEp->lock) -__acquires(mEp->lock) +static int isr_tr_complete_low(struct ci_hw_ep *hwep) +__releases(hwep->lock) +__acquires(hwep->lock) { - struct ci13xxx_req *mReq, *mReqTemp; - struct ci13xxx_ep *mEpTemp = mEp; + struct ci_hw_req *hwreq, *hwreqtemp; + struct ci_hw_ep *hweptemp = hwep; int retval = 0; - list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue, + list_for_each_entry_safe(hwreq, hwreqtemp, &hwep->qh.queue, queue) { - retval = _hardware_dequeue(mEp, mReq); + retval = _hardware_dequeue(hwep, hwreq); if (retval < 0) break; - list_del_init(&mReq->queue); - dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); - if (mReq->req.complete != NULL) { - spin_unlock(mEp->lock); - if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && - mReq->req.length) - mEpTemp = mEp->ci->ep0in; - mReq->req.complete(&mEpTemp->ep, &mReq->req); - spin_lock(mEp->lock); + list_del_init(&hwreq->queue); + if (hwreq->req.complete != NULL) { + spin_unlock(hwep->lock); + if ((hwep->type == USB_ENDPOINT_XFER_CONTROL) && + hwreq->req.length) + hweptemp = hwep->ci->ep0in; + hwreq->req.complete(&hweptemp->ep, &hwreq->req); + spin_lock(hwep->lock); } } if (retval == -EBUSY) retval = 0; - if (retval < 0) - dbg_event(_usb_addr(mEp), "DONE", retval); return retval; } /** - * isr_tr_complete_handler: transaction complete interrupt handler + * isr_setup_packet_handler: setup packet handler * @ci: UDC descriptor * - * This function handles traffic events + * This function handles setup packet */ -static void isr_tr_complete_handler(struct ci13xxx *ci) +static void isr_setup_packet_handler(struct ci_hdrc *ci) __releases(ci->lock) __acquires(ci->lock) { - unsigned i; + struct ci_hw_ep *hwep = &ci->ci_hw_ep[0]; + struct usb_ctrlrequest req; + int type, num, dir, err = -EINVAL; u8 tmode = 0; - for (i = 0; i < ci->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; - int type, num, dir, err = -EINVAL; - struct usb_ctrlrequest req; - - if (mEp->ep.desc == NULL) - continue; /* not configured */ - - if (hw_test_and_clear_complete(ci, 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(ci); - if (err < 0) { - dbg_event(_usb_addr(mEp), - "ERROR", err); - spin_unlock(&ci->lock); - if (usb_ep_set_halt(&mEp->ep)) - dev_err(ci->dev, - "error: ep_set_halt\n"); - spin_lock(&ci->lock); - } - } - } - - if (mEp->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(ci, i)) - continue; - - if (i != 0) { - dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); - continue; - } - - /* - * Flush data and handshake transactions of previous - * setup packet. - */ - _ep_nuke(ci->ep0out); - _ep_nuke(ci->ep0in); - - /* read_setup_packet */ - do { - hw_test_and_set_setup_guard(ci); - memcpy(&req, &mEp->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(ci)); + /* + * Flush data and handshake transactions of previous + * setup packet. + */ + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); - type = req.bRequestType; + /* read_setup_packet */ + do { + hw_test_and_set_setup_guard(ci); + memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); + } while (!hw_test_and_clear_setup_guard(ci)); - ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + type = req.bRequestType; - dbg_setup(_usb_addr(mEp), &req); + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; - switch (req.bRequest) { - case USB_REQ_CLEAR_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - if (!ci->ci13xxx_ep[num].wedge) { - spin_unlock(&ci->lock); - err = usb_ep_clear_halt( - &ci->ci13xxx_ep[num].ep); - spin_lock(&ci->lock); - if (err) - break; - } - err = isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && - le16_to_cpu(req.wValue) == - USB_DEVICE_REMOTE_WAKEUP) { - if (req.wLength != 0) + switch (req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + if (!ci->ci_hw_ep[num].wedge) { + spin_unlock(&ci->lock); + err = usb_ep_clear_halt( + &ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (err) break; - ci->remote_wakeup = 0; - err = isr_setup_status_phase(ci); - } else { - goto delegate; } - break; - case USB_REQ_GET_STATUS: - if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && - type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && - type != (USB_DIR_IN|USB_RECIP_INTERFACE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 2 || - le16_to_cpu(req.wValue) != 0) - break; - err = isr_get_status_response(ci, &req); - break; - case USB_REQ_SET_ADDRESS: - if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 0 || - le16_to_cpu(req.wIndex) != 0) + err = isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && + le16_to_cpu(req.wValue) == + USB_DEVICE_REMOTE_WAKEUP) { + if (req.wLength != 0) break; - ci->address = (u8)le16_to_cpu(req.wValue); - ci->setaddr = true; + ci->remote_wakeup = 0; err = isr_setup_status_phase(ci); + } else { + goto delegate; + } + break; + case USB_REQ_GET_STATUS: + if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && + type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && + type != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 2 || + le16_to_cpu(req.wValue) != 0) break; - case USB_REQ_SET_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; + err = isr_get_status_response(ci, &req); + break; + case USB_REQ_SET_ADDRESS: + if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 0 || + le16_to_cpu(req.wIndex) != 0) + break; + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); + break; + case USB_REQ_SET_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; - spin_unlock(&ci->lock); - err = usb_ep_set_halt(&ci->ci13xxx_ep[num].ep); - spin_lock(&ci->lock); - if (!err) - isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { - if (req.wLength != 0) - break; - switch (le16_to_cpu(req.wValue)) { - case USB_DEVICE_REMOTE_WAKEUP: - ci->remote_wakeup = 1; - err = isr_setup_status_phase(ci); + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (!err) + isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { + if (req.wLength != 0) + break; + switch (le16_to_cpu(req.wValue)) { + case USB_DEVICE_REMOTE_WAKEUP: + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); + break; + case USB_DEVICE_TEST_MODE: + tmode = le16_to_cpu(req.wIndex) >> 8; + switch (tmode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + ci->test_mode = tmode; + err = isr_setup_status_phase( + ci); break; - case USB_DEVICE_TEST_MODE: - tmode = le16_to_cpu(req.wIndex) >> 8; - switch (tmode) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: - ci->test_mode = tmode; - err = isr_setup_status_phase( - ci); - break; - default: - break; - } default: - goto delegate; + break; + } + break; + case USB_DEVICE_B_HNP_ENABLE: + if (ci_otg_is_fsm_mode(ci)) { + ci->gadget.b_hnp_enable = 1; + err = isr_setup_status_phase( + ci); } - } else { + break; + default: goto delegate; } - break; - default: + } else { + goto delegate; + } + break; + default: delegate: - if (req.wLength == 0) /* no data phase */ - ci->ep0_dir = TX; + if (req.wLength == 0) /* no data phase */ + ci->ep0_dir = TX; - spin_unlock(&ci->lock); - err = ci->driver->setup(&ci->gadget, &req); - spin_lock(&ci->lock); - break; - } + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); + break; + } - if (err < 0) { - dbg_event(_usb_addr(mEp), "ERROR", err); + if (err < 0) { + spin_unlock(&ci->lock); + if (usb_ep_set_halt(&hwep->ep)) + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); + } +} - spin_unlock(&ci->lock); - if (usb_ep_set_halt(&mEp->ep)) - dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&ci->lock); +/** + * isr_tr_complete_handler: transaction complete interrupt handler + * @ci: UDC descriptor + * + * This function handles traffic events + */ +static void isr_tr_complete_handler(struct ci_hdrc *ci) +__releases(ci->lock) +__acquires(ci->lock) +{ + unsigned i; + int err; + + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; + + if (hwep->ep.desc == NULL) + continue; /* not configured */ + + if (hw_test_and_clear_complete(ci, i)) { + err = isr_tr_complete_low(hwep); + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) { + if (err > 0) /* needs status phase */ + err = isr_setup_status_phase(ci); + if (err < 0) { + spin_unlock(&ci->lock); + if (usb_ep_set_halt(&hwep->ep)) + dev_err(ci->dev, + "error: ep_set_halt\n"); + spin_lock(&ci->lock); + } + } } + + /* Only handle setup packet below */ + if (i == 0 && + hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0))) + isr_setup_packet_handler(ci); } } @@ -1018,51 +1143,60 @@ delegate: 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); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); int retval = 0; unsigned long flags; + u32 cap = 0; if (ep == NULL || desc == NULL) return -EINVAL; - spin_lock_irqsave(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); /* only internal SW should enable ctrl endpts */ - mEp->ep.desc = desc; + hwep->ep.desc = desc; + + if (!list_empty(&hwep->qh.queue)) + dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n"); - if (!list_empty(&mEp->qh.queue)) - dev_warn(mEp->ci->dev, "enabling a non-empty endpoint!\n"); + hwep->dir = usb_endpoint_dir_in(desc) ? TX : RX; + hwep->num = usb_endpoint_num(desc); + hwep->type = usb_endpoint_type(desc); - mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; - mEp->num = usb_endpoint_num(desc); - mEp->type = usb_endpoint_type(desc); + hwep->ep.maxpacket = usb_endpoint_maxp(desc) & 0x07ff; + hwep->ep.mult = QH_ISO_MULT(usb_endpoint_maxp(desc)); - mEp->ep.maxpacket = usb_endpoint_maxp(desc); + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) + cap |= QH_IOS; - dbg_event(_usb_addr(mEp), "ENABLE", 0); + cap |= QH_ZLT; + cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; + /* + * For ISO-TX, we set mult at QH as the largest value, and use + * MultO at TD as real mult value. + */ + if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX) + cap |= 3 << __ffs(QH_MULT); - mEp->qh.ptr->cap = 0; + hwep->qh.ptr->cap = cpu_to_le32(cap); - 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; + hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ - mEp->qh.ptr->cap |= - (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; - mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ + if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) { + dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n"); + retval = -EINVAL; + } /* * Enable endpoints in the HW other than ep0 as ep0 * is always enabled */ - if (mEp->num) - retval |= hw_ep_enable(mEp->ci, mEp->num, mEp->dir, mEp->type); + if (hwep->num) + retval |= hw_ep_enable(hwep->ci, hwep->num, hwep->dir, + hwep->type); - spin_unlock_irqrestore(mEp->lock, flags); + spin_unlock_irqrestore(hwep->lock, flags); return retval; } @@ -1073,34 +1207,32 @@ static int ep_enable(struct usb_ep *ep, */ static int ep_disable(struct usb_ep *ep) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); int direction, retval = 0; unsigned long flags; if (ep == NULL) return -EINVAL; - else if (mEp->ep.desc == NULL) + else if (hwep->ep.desc == NULL) return -EBUSY; - spin_lock_irqsave(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); /* only internal SW should disable ctrl endpts */ - direction = mEp->dir; + direction = hwep->dir; do { - dbg_event(_usb_addr(mEp), "DISABLE", 0); - - retval |= _ep_nuke(mEp); - retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir); + retval |= _ep_nuke(hwep); + retval |= hw_ep_disable(hwep->ci, hwep->num, hwep->dir); - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) + hwep->dir = (hwep->dir == TX) ? RX : TX; - } while (mEp->dir != direction); + } while (hwep->dir != direction); - mEp->ep.desc = NULL; + hwep->ep.desc = NULL; - spin_unlock_irqrestore(mEp->lock, flags); + spin_unlock_irqrestore(hwep->lock, flags); return retval; } @@ -1111,27 +1243,18 @@ static int ep_disable(struct usb_ep *ep) */ 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; + struct ci_hw_req *hwreq = NULL; if (ep == NULL) return NULL; - mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags); - if (mReq != NULL) { - INIT_LIST_HEAD(&mReq->queue); - - mReq->ptr = dma_pool_alloc(mEp->td_pool, gfp_flags, - &mReq->dma); - if (mReq->ptr == NULL) { - kfree(mReq); - mReq = NULL; - } + hwreq = kzalloc(sizeof(struct ci_hw_req), gfp_flags); + if (hwreq != NULL) { + INIT_LIST_HEAD(&hwreq->queue); + INIT_LIST_HEAD(&hwreq->tds); } - dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); - - return (mReq == NULL) ? NULL : &mReq->req; + return (hwreq == NULL) ? NULL : &hwreq->req; } /** @@ -1141,26 +1264,30 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) */ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); + struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req); + struct td_node *node, *tmpnode; unsigned long flags; if (ep == NULL || req == NULL) { return; - } else if (!list_empty(&mReq->queue)) { - dev_err(mEp->ci->dev, "freeing queued request\n"); + } else if (!list_empty(&hwreq->queue)) { + dev_err(hwep->ci->dev, "freeing queued request\n"); return; } - spin_lock_irqsave(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); - if (mReq->ptr) - dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma); - kfree(mReq); + list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { + dma_pool_free(hwep->td_pool, node->ptr, node->dma); + list_del_init(&node->td); + node->ptr = NULL; + kfree(node); + } - dbg_event(_usb_addr(mEp), "FREE", 0); + kfree(hwreq); - spin_unlock_irqrestore(mEp->lock, flags); + spin_unlock_irqrestore(hwep->lock, flags); } /** @@ -1171,59 +1298,16 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) static int ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t __maybe_unused gfp_flags) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - struct ci13xxx *ci = mEp->ci; + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); int retval = 0; unsigned long flags; - if (ep == NULL || req == NULL || mEp->ep.desc == NULL) + if (ep == NULL || req == NULL || hwep->ep.desc == NULL) return -EINVAL; - spin_lock_irqsave(mEp->lock, flags); - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { - if (req->length) - mEp = (ci->ep0_dir == RX) ? - ci->ep0out : ci->ep0in; - if (!list_empty(&mEp->qh.queue)) { - _ep_nuke(mEp); - retval = -EOVERFLOW; - dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n", - _usb_addr(mEp)); - } - } - - /* first nuke then test link, e.g. previous status has not sent */ - if (!list_empty(&mReq->queue)) { - retval = -EBUSY; - dev_err(mEp->ci->dev, "request already in queue\n"); - goto done; - } - - if (req->length > 4 * CI13XXX_PAGE_SIZE) { - req->length = 4 * CI13XXX_PAGE_SIZE; - retval = -EMSGSIZE; - dev_warn(mEp->ci->dev, "request length truncated\n"); - } - - dbg_queue(_usb_addr(mEp), req, retval); - - /* push request */ - mReq->req.status = -EINPROGRESS; - mReq->req.actual = 0; - - retval = _hardware_enqueue(mEp, mReq); - - if (retval == -EALREADY) { - dbg_event(_usb_addr(mEp), "QUEUE", retval); - retval = 0; - } - if (!retval) - list_add_tail(&mReq->queue, &mEp->qh.queue); - - done: - spin_unlock_irqrestore(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); + retval = _ep_queue(ep, req, gfp_flags); + spin_unlock_irqrestore(hwep->lock, flags); return retval; } @@ -1234,35 +1318,40 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, */ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); + struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req); unsigned long flags; + struct td_node *node, *tmpnode; - if (ep == NULL || req == NULL || mReq->req.status != -EALREADY || - mEp->ep.desc == NULL || list_empty(&mReq->queue) || - list_empty(&mEp->qh.queue)) + if (ep == NULL || req == NULL || hwreq->req.status != -EALREADY || + hwep->ep.desc == NULL || list_empty(&hwreq->queue) || + list_empty(&hwep->qh.queue)) return -EINVAL; - spin_lock_irqsave(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); - dbg_event(_usb_addr(mEp), "DEQUEUE", 0); + hw_ep_flush(hwep->ci, hwep->num, hwep->dir); - hw_ep_flush(mEp->ci, mEp->num, mEp->dir); + list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { + dma_pool_free(hwep->td_pool, node->ptr, node->dma); + list_del(&node->td); + kfree(node); + } /* pop request */ - list_del_init(&mReq->queue); + list_del_init(&hwreq->queue); - usb_gadget_unmap_request(&mEp->ci->gadget, req, mEp->dir); + usb_gadget_unmap_request(&hwep->ci->gadget, req, hwep->dir); req->status = -ECONNRESET; - if (mReq->req.complete != NULL) { - spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); - spin_lock(mEp->lock); + if (hwreq->req.complete != NULL) { + spin_unlock(hwep->lock); + hwreq->req.complete(&hwep->ep, &hwreq->req); + spin_lock(hwep->lock); } - spin_unlock_irqrestore(mEp->lock, flags); + spin_unlock_irqrestore(hwep->lock, flags); return 0; } @@ -1273,38 +1362,40 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) */ static int ep_set_halt(struct usb_ep *ep, int value) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); int direction, retval = 0; unsigned long flags; - if (ep == NULL || mEp->ep.desc == NULL) + if (ep == NULL || hwep->ep.desc == NULL) return -EINVAL; - spin_lock_irqsave(mEp->lock, flags); + if (usb_endpoint_xfer_isoc(hwep->ep.desc)) + return -EOPNOTSUPP; + + spin_lock_irqsave(hwep->lock, flags); #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.queue)) { - spin_unlock_irqrestore(mEp->lock, flags); + if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX && + !list_empty(&hwep->qh.queue)) { + spin_unlock_irqrestore(hwep->lock, flags); return -EAGAIN; } #endif - direction = mEp->dir; + direction = hwep->dir; do { - dbg_event(_usb_addr(mEp), "HALT", value); - retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value); + retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); if (!value) - mEp->wedge = 0; + hwep->wedge = 0; - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) + hwep->dir = (hwep->dir == TX) ? RX : TX; - } while (mEp->dir != direction); + } while (hwep->dir != direction); - spin_unlock_irqrestore(mEp->lock, flags); + spin_unlock_irqrestore(hwep->lock, flags); return retval; } @@ -1315,18 +1406,15 @@ static int ep_set_halt(struct usb_ep *ep, int value) */ static int ep_set_wedge(struct usb_ep *ep) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); unsigned long flags; - if (ep == NULL || mEp->ep.desc == NULL) + if (ep == NULL || hwep->ep.desc == NULL) return -EINVAL; - spin_lock_irqsave(mEp->lock, flags); - - dbg_event(_usb_addr(mEp), "WEDGE", 0); - mEp->wedge = 1; - - spin_unlock_irqrestore(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); + hwep->wedge = 1; + spin_unlock_irqrestore(hwep->lock, flags); return usb_ep_set_halt(ep); } @@ -1338,20 +1426,19 @@ static int ep_set_wedge(struct usb_ep *ep) */ static void ep_fifo_flush(struct usb_ep *ep) { - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); unsigned long flags; if (ep == NULL) { - dev_err(mEp->ci->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); + dev_err(hwep->ci->dev, "%02X: -EINVAL\n", _usb_addr(hwep)); return; } - spin_lock_irqsave(mEp->lock, flags); + spin_lock_irqsave(hwep->lock, flags); - dbg_event(_usb_addr(mEp), "FFLUSH", 0); - hw_ep_flush(mEp->ci, mEp->num, mEp->dir); + hw_ep_flush(hwep->ci, hwep->num, hwep->dir); - spin_unlock_irqrestore(mEp->lock, flags); + spin_unlock_irqrestore(hwep->lock, flags); } /** @@ -1373,15 +1460,12 @@ static const struct usb_ep_ops usb_ep_ops = { /****************************************************************************** * GADGET block *****************************************************************************/ -static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) +static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { - struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; int gadget_ready = 0; - if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) - return -EOPNOTSUPP; - spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; if (ci->driver) @@ -1392,24 +1476,27 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) if (is_active) { pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci, USBMODE_CM_DC); - hw_enable_vbus_intr(ci); hw_device_state(ci, ci->ep0out->qh.dma); + usb_gadget_set_state(_gadget, USB_STATE_POWERED); } else { + if (ci->driver) + ci->driver->disconnect(&ci->gadget); hw_device_state(ci, 0); if (ci->platdata->notify_event) ci->platdata->notify_event(ci, - CI13XXX_CONTROLLER_STOPPED_EVENT); + CI_HDRC_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); + usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED); } } return 0; } -static int ci13xxx_wakeup(struct usb_gadget *_gadget) +static int ci_udc_wakeup(struct usb_gadget *_gadget) { - struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; int ret = 0; @@ -1428,21 +1515,24 @@ out: return ret; } -static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma) { - struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); if (ci->transceiver) - return usb_phy_set_power(ci->transceiver, mA); + return usb_phy_set_power(ci->transceiver, ma); return -ENOTSUPP; } /* Change Data+ pullup status * this func is used by usb_gadget_connect/disconnet */ -static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_on) +static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { - struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + + if (!ci->vbus_active) + return -EOPNOTSUPP; if (is_on) hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); @@ -1452,9 +1542,9 @@ static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int ci13xxx_start(struct usb_gadget *gadget, +static int ci_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver); -static int ci13xxx_stop(struct usb_gadget *gadget, +static int ci_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver); /** * Device operations part of the API to the USB controller hardware, @@ -1462,46 +1552,46 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * Check "usb_gadget.h" for details */ static const struct usb_gadget_ops usb_gadget_ops = { - .vbus_session = ci13xxx_vbus_session, - .wakeup = ci13xxx_wakeup, - .pullup = ci13xxx_pullup, - .vbus_draw = ci13xxx_vbus_draw, - .udc_start = ci13xxx_start, - .udc_stop = ci13xxx_stop, + .vbus_session = ci_udc_vbus_session, + .wakeup = ci_udc_wakeup, + .pullup = ci_udc_pullup, + .vbus_draw = ci_udc_vbus_draw, + .udc_start = ci_udc_start, + .udc_stop = ci_udc_stop, }; -static int init_eps(struct ci13xxx *ci) +static int init_eps(struct ci_hdrc *ci) { int retval = 0, i, j; for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { int k = i + j * ci->hw_ep_max/2; - struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[k]; + struct ci_hw_ep *hwep = &ci->ci_hw_ep[k]; - scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i, + scnprintf(hwep->name, sizeof(hwep->name), "ep%i%s", i, (j == TX) ? "in" : "out"); - mEp->ci = ci; - mEp->lock = &ci->lock; - mEp->td_pool = ci->td_pool; + hwep->ci = ci; + hwep->lock = &ci->lock; + hwep->td_pool = ci->td_pool; - mEp->ep.name = mEp->name; - mEp->ep.ops = &usb_ep_ops; + hwep->ep.name = hwep->name; + hwep->ep.ops = &usb_ep_ops; /* * for ep0: maxP defined in desc, for other * eps, maxP is set by epautoconfig() called * by gadget layer */ - mEp->ep.maxpacket = (unsigned short)~0; + usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0); - INIT_LIST_HEAD(&mEp->qh.queue); - mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, - &mEp->qh.dma); - if (mEp->qh.ptr == NULL) + INIT_LIST_HEAD(&hwep->qh.queue); + hwep->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, + &hwep->qh.dma); + if (hwep->qh.ptr == NULL) retval = -ENOMEM; else - memset(mEp->qh.ptr, 0, sizeof(*mEp->qh.ptr)); + memset(hwep->qh.ptr, 0, sizeof(*hwep->qh.ptr)); /* * set up shorthands for ep0 out and in endpoints, @@ -1509,42 +1599,44 @@ static int init_eps(struct ci13xxx *ci) */ if (i == 0) { if (j == RX) - ci->ep0out = mEp; + ci->ep0out = hwep; else - ci->ep0in = mEp; + ci->ep0in = hwep; - mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; + usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX); continue; } - list_add_tail(&mEp->ep.ep_list, &ci->gadget.ep_list); + list_add_tail(&hwep->ep.ep_list, &ci->gadget.ep_list); } return retval; } -static void destroy_eps(struct ci13xxx *ci) +static void destroy_eps(struct ci_hdrc *ci) { int i; for (i = 0; i < ci->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; + struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; - dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma); + if (hwep->pending_td) + free_pending_td(hwep); + dma_pool_free(ci->qh_pool, hwep->qh.ptr, hwep->qh.dma); } } /** - * ci13xxx_start: register a gadget driver + * ci_udc_start: register a gadget driver * @gadget: our gadget * @driver: the driver being registered * * Interrupts are enabled here. */ -static int ci13xxx_start(struct usb_gadget *gadget, +static int ci_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; int retval = -ENOMEM; @@ -1561,55 +1653,55 @@ static int ci13xxx_start(struct usb_gadget *gadget, retval = usb_ep_enable(&ci->ep0in->ep); if (retval) return retval; - spin_lock_irqsave(&ci->lock, flags); ci->driver = driver; + + /* Start otg fsm for B-device */ + if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) { + ci_hdrc_otg_fsm_start(ci); + return retval; + } + pm_runtime_get_sync(&ci->gadget.dev); - if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { - if (ci->vbus_active) { - if (ci->platdata->flags & CI13XXX_REGS_SHARED) { - hw_device_reset(ci, USBMODE_CM_DC); - hw_enable_vbus_intr(ci); - } - } else { - pm_runtime_put_sync(&ci->gadget.dev); - goto done; - } + if (ci->vbus_active) { + spin_lock_irqsave(&ci->lock, flags); + hw_device_reset(ci, USBMODE_CM_DC); + } else { + pm_runtime_put_sync(&ci->gadget.dev); + return retval; } retval = hw_device_state(ci, ci->ep0out->qh.dma); + spin_unlock_irqrestore(&ci->lock, flags); if (retval) pm_runtime_put_sync(&ci->gadget.dev); - done: - spin_unlock_irqrestore(&ci->lock, flags); return retval; } /** - * ci13xxx_stop: unregister a gadget driver + * ci_udc_stop: unregister a gadget driver */ -static int ci13xxx_stop(struct usb_gadget *gadget, +static int ci_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; spin_lock_irqsave(&ci->lock, flags); - if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || - ci->vbus_active) { + if (ci->vbus_active) { hw_device_state(ci, 0); if (ci->platdata->notify_event) ci->platdata->notify_event(ci, - CI13XXX_CONTROLLER_STOPPED_EVENT); - ci->driver = NULL; + CI_HDRC_CONTROLLER_STOPPED_EVENT); spin_unlock_irqrestore(&ci->lock, flags); _gadget_stop_activity(&ci->gadget); spin_lock_irqsave(&ci->lock, flags); pm_runtime_put(&ci->gadget.dev); } + ci->driver = NULL; spin_unlock_irqrestore(&ci->lock, flags); return 0; @@ -1624,7 +1716,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -static irqreturn_t udc_irq(struct ci13xxx *ci) +static irqreturn_t udc_irq(struct ci_hdrc *ci) { irqreturn_t retval; u32 intr; @@ -1634,7 +1726,7 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) spin_lock(&ci->lock); - if (ci->platdata->flags & CI13XXX_REGS_SHARED) { + if (ci->platdata->flags & CI_HDRC_REGS_SHARED) { if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { spin_unlock(&ci->lock); @@ -1642,7 +1734,6 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) } } intr = hw_test_and_clear_intr_active(ci); - dbg_interrupt(intr); if (intr) { /* order defines priority - do NOT change it */ @@ -1669,6 +1760,8 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) ci->suspended = 1; spin_unlock(&ci->lock); ci->driver->suspend(&ci->gadget); + usb_gadget_set_state(&ci->gadget, + USB_STATE_SUSPENDED); spin_lock(&ci->lock); } } @@ -1676,33 +1769,16 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) } else { retval = IRQ_NONE; } - - intr = hw_read(ci, OP_OTGSC, ~0); - hw_write(ci, OP_OTGSC, ~0, intr); - - if (intr & (OTGSC_AVVIE & OTGSC_AVVIS)) - queue_work(ci->wq, &ci->vbus_work); - spin_unlock(&ci->lock); return retval; } /** - * udc_release: driver release function - * @dev: device - * - * Currently does nothing - */ -static void udc_release(struct device *dev) -{ -} - -/** * udc_start: initialize gadget role * @ci: chipidea controller */ -static int udc_start(struct ci13xxx *ci) +static int udc_start(struct ci_hdrc *ci) { struct device *dev = ci->dev; int retval = 0; @@ -1712,27 +1788,21 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.ops = &usb_gadget_ops; ci->gadget.speed = USB_SPEED_UNKNOWN; ci->gadget.max_speed = USB_SPEED_HIGH; - ci->gadget.is_otg = 0; + ci->gadget.is_otg = ci->is_otg ? 1 : 0; ci->gadget.name = ci->platdata->name; INIT_LIST_HEAD(&ci->gadget.ep_list); - dev_set_name(&ci->gadget.dev, "gadget"); - ci->gadget.dev.dma_mask = dev->dma_mask; - ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; - ci->gadget.dev.parent = dev; - ci->gadget.dev.release = udc_release; - /* alloc resources */ - ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, - sizeof(struct ci13xxx_qh), - 64, CI13XXX_PAGE_SIZE); + ci->qh_pool = dma_pool_create("ci_hw_qh", dev, + sizeof(struct ci_hw_qh), + 64, CI_HDRC_PAGE_SIZE); if (ci->qh_pool == NULL) return -ENOMEM; - ci->td_pool = dma_pool_create("ci13xxx_td", dev, - sizeof(struct ci13xxx_td), - 64, CI13XXX_PAGE_SIZE); + ci->td_pool = dma_pool_create("ci_hw_td", dev, + sizeof(struct ci_hw_td), + 64, CI_HDRC_PAGE_SIZE); if (ci->td_pool == NULL) { retval = -ENOMEM; goto free_qh_pool; @@ -1744,64 +1814,15 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.ep0 = &ci->ep0in->ep; - if (ci->global_phy) - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - - if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { - if (ci->transceiver == NULL) { - retval = -ENODEV; - goto destroy_eps; - } - } - - if (!(ci->platdata->flags & CI13XXX_REGS_SHARED)) { - retval = hw_device_reset(ci, USBMODE_CM_DC); - if (retval) - goto put_transceiver; - hw_enable_vbus_intr(ci); - } - - retval = device_register(&ci->gadget.dev); - if (retval) { - put_device(&ci->gadget.dev); - goto put_transceiver; - } - - retval = dbg_create_files(&ci->gadget.dev); - if (retval) - goto unreg_device; - - if (!IS_ERR_OR_NULL(ci->transceiver)) { - retval = otg_set_peripheral(ci->transceiver->otg, - &ci->gadget); - if (retval) - goto remove_dbg; - } - retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) - goto remove_trans; + goto destroy_eps; pm_runtime_no_callbacks(&ci->gadget.dev); pm_runtime_enable(&ci->gadget.dev); return retval; -remove_trans: - if (!IS_ERR_OR_NULL(ci->transceiver)) { - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - } - - dev_err(dev, "error = %i\n", retval); -remove_dbg: - dbg_remove_files(&ci->gadget.dev); -unreg_device: - device_unregister(&ci->gadget.dev); -put_transceiver: - if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy) - usb_put_phy(ci->transceiver); destroy_eps: destroy_eps(ci); free_pools: @@ -1812,43 +1833,50 @@ free_qh_pool: } /** - * udc_remove: parent remove must call this to remove UDC + * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC * * No interrupts active, the IRQ has been released */ -static void udc_stop(struct ci13xxx *ci) +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) { - if (ci == NULL) + if (!ci->roles[CI_ROLE_GADGET]) return; - hw_disable_vbus_intr(ci); - cancel_work_sync(&ci->vbus_work); - usb_del_gadget_udc(&ci->gadget); destroy_eps(ci); dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->qh_pool); +} - if (!IS_ERR_OR_NULL(ci->transceiver)) { - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - } - dbg_remove_files(&ci->gadget.dev); - device_unregister(&ci->gadget.dev); - /* my kobject is dynamic, I swear! */ - memset(&ci->gadget, 0, sizeof(ci->gadget)); +static int udc_id_switch_for_device(struct ci_hdrc *ci) +{ + if (ci->is_otg) + /* Clear and enable BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, + OTGSC_BSVIS | OTGSC_BSVIE); + + return 0; +} + +static void udc_id_switch_for_host(struct ci_hdrc *ci) +{ + /* + * host doesn't care B_SESSION_VALID event + * so clear and disbale BSV irq + */ + if (ci->is_otg) + hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); } /** * ci_hdrc_gadget_init - initialize device related bits * ci: the controller * - * This function enables the gadget role, if the device is "device capable". + * This function initializes the gadget, if the device is "device capable". */ -int ci_hdrc_gadget_init(struct ci13xxx *ci) +int ci_hdrc_gadget_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; @@ -1859,12 +1887,11 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci) if (!rdrv) return -ENOMEM; - rdrv->start = udc_start; - rdrv->stop = udc_stop; + rdrv->start = udc_id_switch_for_device; + rdrv->stop = udc_id_switch_for_host; rdrv->irq = udc_irq; rdrv->name = "gadget"; ci->roles[CI_ROLE_GADGET] = rdrv; - INIT_WORK(&ci->vbus_work, vbus_work); - return 0; + return udc_start(ci); } |
