diff options
Diffstat (limited to 'drivers/usb/chipidea/udc.c')
| -rw-r--r-- | drivers/usb/chipidea/udc.c | 1736 |
1 files changed, 912 insertions, 824 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 51f96942dc5..b8125aa64ad 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -13,25 +13,22 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dmapool.h> -#include <linux/dma-mapping.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/err.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 @@ -66,34 +63,33 @@ static inline int hw_ep_bit(int num, int dir) return num + (dir ? 16 : 0); } -static inline int ep_to_bit(struct ci13xxx *udc, int n) +static inline int ep_to_bit(struct ci_hdrc *ci, int n) { - int fill = 16 - udc->hw_ep_max / 2; + int fill = 16 - ci->hw_ep_max / 2; - if (n >= udc->hw_ep_max / 2) + if (n >= ci->hw_ep_max / 2) n += fill; return n; } /** - * hw_device_state: enables/disables interrupts & starts/stops device (execute - * without interruption) + * hw_device_state: enables/disables interrupts (execute without interruption) * @dma: 0 => disable, !0 => enable and set dma engine * * This function returns an error code */ -static int hw_device_state(struct ci13xxx *udc, u32 dma) +static int hw_device_state(struct ci_hdrc *ci, u32 dma) { if (dma) { - hw_write(udc, OP_ENDPTLISTADDR, ~0, dma); + hw_write(ci, OP_ENDPTLISTADDR, ~0, dma); /* interrupt, error, port change, reset, sleep/suspend */ - hw_write(udc, OP_USBINTR, ~0, + hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_write(udc, OP_USBCMD, USBCMD_RS, USBCMD_RS); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { - hw_write(udc, OP_USBCMD, USBCMD_RS, 0); - hw_write(udc, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); } return 0; } @@ -105,16 +101,16 @@ static int hw_device_state(struct ci13xxx *udc, u32 dma) * * This function returns an error code */ -static int hw_ep_flush(struct ci13xxx *udc, 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(udc, OP_ENDPTFLUSH, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTFLUSH, BIT(n))) + hw_write(ci, OP_ENDPTFLUSH, ~0, BIT(n)); + while (hw_read(ci, OP_ENDPTFLUSH, BIT(n))) cpu_relax(); - } while (hw_read(udc, OP_ENDPTSTAT, BIT(n))); + } while (hw_read(ci, OP_ENDPTSTAT, BIT(n))); return 0; } @@ -126,10 +122,10 @@ static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) +static int hw_ep_disable(struct ci_hdrc *ci, int num, int dir) { - hw_ep_flush(udc, num, dir); - hw_write(udc, OP_ENDPTCTRL + num, + hw_ep_flush(ci, num, dir); + hw_write(ci, OP_ENDPTCTRL + num, dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); return 0; } @@ -142,13 +138,13 @@ static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_enable(struct ci13xxx *udc, 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 */ @@ -157,7 +153,7 @@ static int hw_ep_enable(struct ci13xxx *udc, 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 */ @@ -165,7 +161,7 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) mask |= ENDPTCTRL_RXE; /* enable */ data |= ENDPTCTRL_RXE; } - hw_write(udc, OP_ENDPTCTRL + num, mask, data); + hw_write(ci, OP_ENDPTCTRL + num, mask, data); return 0; } @@ -176,24 +172,11 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) * * This function returns 1 if endpoint halted */ -static int hw_ep_get_halt(struct ci13xxx *udc, 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; - return hw_read(udc, OP_ENDPTCTRL + num, mask) ? 1 : 0; -} - -/** - * 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 *udc, int n) -{ - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTSETUPSTAT, BIT(n)); + return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0; } /** @@ -204,18 +187,18 @@ static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_ep_prime(struct ci13xxx *udc, 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(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; - hw_write(udc, OP_ENDPTPRIME, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTPRIME, ~0, BIT(n)); - while (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; /* status shoult be tested according with manual but it doesn't work */ @@ -231,20 +214,20 @@ static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) * * This function returns an error code */ -static int hw_ep_set_halt(struct ci13xxx *udc, 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; /* data toggle - reserved for EP0 but it's in ESS */ - hw_write(udc, reg, mask_xs|mask_xr, + hw_write(ci, reg, mask_xs|mask_xr, value ? mask_xs : mask_xr); - } while (value != hw_ep_get_halt(udc, num, dir)); + } while (value != hw_ep_get_halt(ci, num, dir)); return 0; } @@ -254,30 +237,10 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) * * This function returns true if high speed port */ -static int hw_port_is_high_speed(struct ci13xxx *udc) -{ - return udc->hw_bank.lpm ? hw_read(udc, OP_DEVLC, DEVLC_PSPD) : - hw_read(udc, 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 *udc) -{ - return hw_read(udc, OP_USBINTR, ~0); -} - -/** - * hw_read_intr_status: returns interrupt status register - * - * This function returns register data - */ -static u32 hw_read_intr_status(struct ci13xxx *udc) +static int hw_port_is_high_speed(struct ci_hdrc *ci) { - return hw_read(udc, OP_USBSTS, ~0); + return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) : + hw_read(ci, OP_PORTSC, PORTSC_HSP); } /** @@ -287,10 +250,10 @@ static u32 hw_read_intr_status(struct ci13xxx *udc) * * This function returns complete status */ -static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) +static int hw_test_and_clear_complete(struct ci_hdrc *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTCOMPLETE, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n)); } /** @@ -299,11 +262,11 @@ static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) * * This function returns active interrutps */ -static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) +static u32 hw_test_and_clear_intr_active(struct ci_hdrc *ci) { - u32 reg = hw_read_intr_status(udc) & hw_read_intr_enable(udc); + u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci); - hw_write(udc, OP_USBSTS, ~0, reg); + hw_write(ci, OP_USBSTS, ~0, reg); return reg; } @@ -313,9 +276,9 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) +static int hw_test_and_clear_setup_guard(struct ci_hdrc *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, 0); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0); } /** @@ -324,9 +287,9 @@ static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_set_setup_guard(struct ci13xxx *udc) +static int hw_test_and_set_setup_guard(struct ci_hdrc *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); } /** @@ -336,10 +299,10 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *udc) * 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 *udc, u8 value) +static void hw_usb_set_address(struct ci_hdrc *ci, u8 value) { - hw_write(udc, OP_DEVICEADDR, DEVICEADDR_USBADR, - value << ffs_nr(DEVICEADDR_USBADR)); + hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR, + value << __ffs(DEVICEADDR_USBADR)); } /** @@ -348,21 +311,21 @@ static void hw_usb_set_address(struct ci13xxx *udc, u8 value) * * This function returns an error code */ -static int hw_usb_reset(struct ci13xxx *udc) +static int hw_usb_reset(struct ci_hdrc *ci) { - hw_usb_set_address(udc, 0); + hw_usb_set_address(ci, 0); /* ESS flushes only at end?!? */ - hw_write(udc, OP_ENDPTFLUSH, ~0, ~0); + hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); /* clear setup token semaphores */ - hw_write(udc, OP_ENDPTSETUPSTAT, 0, 0); + hw_write(ci, OP_ENDPTSETUPSTAT, 0, 0); /* clear complete status */ - hw_write(udc, OP_ENDPTCOMPLETE, 0, 0); + hw_write(ci, OP_ENDPTCOMPLETE, 0, 0); /* wait until all bits cleared */ - while (hw_read(udc, OP_ENDPTPRIME, ~0)) + while (hw_read(ci, OP_ENDPTPRIME, ~0)) udelay(10); /* not RTOS friendly */ /* reset all endpoints ? */ @@ -376,11 +339,68 @@ static int hw_usb_reset(struct ci13xxx *udc) /****************************************************************************** * UTIL block *****************************************************************************/ + +static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, + unsigned length) +{ + int i; + u32 temp; + struct td_node *lastnode, *node = kzalloc(sizeof(struct td_node), + GFP_ATOMIC); + + 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; +} + /** * _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; } @@ -388,168 +408,233 @@ 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 *udc = mEp->udc; - 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(&udc->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; - 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; + hwreqprev = list_entry(hwep->qh.queue.prev, + struct ci_hw_req, queue); + prevlastnode = list_entry(hwreqprev->tds.prev, + struct td_node, td); + + prevlastnode->ptr->next = cpu_to_le32(next); wmb(); - if (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) goto done; do { - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); - tmp_stat = hw_read(udc, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(udc, OP_USBCMD, USBCMD_ATDTW)); - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, 0); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); + tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; } /* 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(udc, 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->udc->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); + } + + usb_gadget_unmap_request(&hwep->ci->gadget, &hwreq->req, hwep->dir); + + hwreq->req.actual += actual; - 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; + if (hwreq->req.status) + return hwreq->req.status; - return mReq->req.actual; + 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->udc, 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; } @@ -562,33 +647,30 @@ __acquires(mEp->lock) static int _gadget_stop_activity(struct usb_gadget *gadget) { struct usb_ep *ep; - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->remote_wakeup = 0; - udc->suspended = 0; - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->remote_wakeup = 0; + ci->suspended = 0; + spin_unlock_irqrestore(&ci->lock, flags); /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); } - usb_ep_fifo_flush(&udc->ep0out->ep); - usb_ep_fifo_flush(&udc->ep0in->ep); - - if (udc->driver) - udc->driver->disconnect(gadget); + usb_ep_fifo_flush(&ci->ep0out->ep); + usb_ep_fifo_flush(&ci->ep0in->ep); /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - if (udc->status != NULL) { - usb_ep_free_request(&udc->ep0in->ep, udc->status); - udc->status = NULL; + if (ci->status != NULL) { + usb_ep_free_request(&ci->ep0in->ep, ci->status); + ci->status = NULL; } return 0; @@ -599,36 +681,41 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) *****************************************************************************/ /** * isr_reset_handler: USB reset interrupt handler - * @udc: UDC device + * @ci: UDC device * * This function resets USB engine after a bus reset occurred */ -static void isr_reset_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +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); + } - spin_unlock(&udc->lock); - retval = _gadget_stop_activity(&udc->gadget); + retval = _gadget_stop_activity(&ci->gadget); if (retval) goto done; - retval = hw_usb_reset(udc); + retval = hw_usb_reset(ci); if (retval) goto done; - udc->status = usb_ep_alloc_request(&udc->ep0in->ep, GFP_ATOMIC); - if (udc->status == NULL) + ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC); + if (ci->status == NULL) retval = -ENOMEM; + usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT); + done: - spin_lock(&udc->lock); + spin_lock(&ci->lock); if (retval) - dev_err(udc->dev, "error: %i\n", retval); + dev_err(ci->dev, "error: %i\n", retval); } /** @@ -648,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 - * @udc: udc struct + * @ci: ci struct * @setup: setup request packet * * This function returns an error code */ -static int isr_get_status_response(struct ci13xxx *udc, +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 = udc->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; @@ -683,20 +824,17 @@ __acquires(mEp->lock) if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Assume that device is bus powered for now. */ - *(u16 *)req->buf = udc->remote_wakeup << 1; - retval = 0; + *(u16 *)req->buf = ci->remote_wakeup << 1; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? TX : RX; num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; - *(u16 *)req->buf = hw_ep_get_halt(udc, num, dir); + *(u16 *)req->buf = hw_ep_get_halt(ci, num, dir); } /* 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; @@ -705,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; } @@ -722,271 +860,275 @@ __acquires(mEp->lock) static void isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx *udc = req->context; + struct ci_hdrc *ci = req->context; unsigned long flags; - if (udc->setaddr) { - hw_usb_set_address(udc, udc->address); - udc->setaddr = false; + 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(&udc->lock, flags); - if (udc->test_mode) - hw_port_test_set(udc, udc->test_mode); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (ci->test_mode) + hw_port_test_set(ci, ci->test_mode); + spin_unlock_irqrestore(&ci->lock, flags); } /** * isr_setup_status_phase: queues the status phase of a setup transation - * @udc: udc struct + * @ci: ci struct * * This function returns an error code */ -static int isr_setup_status_phase(struct ci13xxx *udc) -__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 = (udc->ep0_dir == TX) ? udc->ep0out : udc->ep0in; - udc->status->context = udc; - udc->status->complete = isr_setup_status_complete; + 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, udc->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; - int uninitialized_var(retval); - - if (list_empty(&mEp->qh.queue)) - return -EINVAL; + 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->udc->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 - * @udc: UDC descriptor + * 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 *udc) -__releases(udc->lock) -__acquires(udc->lock) +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 < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->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(udc, 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(udc); - if (err < 0) { - dbg_event(_usb_addr(mEp), - "ERROR", err); - spin_unlock(&udc->lock); - if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, - "error: ep_set_halt\n"); - spin_lock(&udc->lock); - } - } - } - - if (mEp->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(udc, i)) - continue; - - if (i != 0) { - dev_warn(udc->dev, "ctrl traffic at endpoint %d\n", i); - 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(udc); - memcpy(&req, &mEp->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(udc)); + /* + * 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)); - udc->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 += udc->hw_ep_max/2; - if (!udc->ci13xxx_ep[num].wedge) { - spin_unlock(&udc->lock); - err = usb_ep_clear_halt( - &udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); - if (err) - break; - } - err = isr_setup_status_phase(udc); - } 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; - udc->remote_wakeup = 0; - err = isr_setup_status_phase(udc); - } 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) + 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; - err = isr_get_status_response(udc, &req); + 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_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; - udc->address = (u8)le16_to_cpu(req.wValue); - udc->setaddr = true; - err = isr_setup_status_phase(udc); + 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; - 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 += udc->hw_ep_max/2; - - spin_unlock(&udc->lock); - err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); - if (!err) - isr_setup_status_phase(udc); - } 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: - udc->remote_wakeup = 1; - err = isr_setup_status_phase(udc); + 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->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: - udc->test_mode = tmode; - err = isr_setup_status_phase( - udc); - break; - default: - break; - } default: - goto delegate; + break; } - } else { + 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); + } + break; + default: goto delegate; } - break; - default: + } else { + goto delegate; + } + break; + default: delegate: - if (req.wLength == 0) /* no data phase */ - udc->ep0_dir = TX; + if (req.wLength == 0) /* no data phase */ + ci->ep0_dir = TX; - spin_unlock(&udc->lock); - err = udc->driver->setup(&udc->gadget, &req); - spin_lock(&udc->lock); - break; - } + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); + break; + } + + 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); + } +} + +/** + * 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; - if (err < 0) { - dbg_event(_usb_addr(mEp), "ERROR", 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 */ - spin_unlock(&udc->lock); - if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + 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); } } @@ -1001,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(&mEp->qh.queue)) - dev_warn(mEp->udc->dev, "enabling a non-empty endpoint!\n"); + if (!list_empty(&hwep->qh.queue)) + dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n"); - mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; - mEp->num = usb_endpoint_num(desc); - mEp->type = usb_endpoint_type(desc); + hwep->dir = usb_endpoint_dir_in(desc) ? TX : RX; + hwep->num = usb_endpoint_num(desc); + hwep->type = usb_endpoint_type(desc); - mEp->ep.maxpacket = usb_endpoint_maxp(desc); + hwep->ep.maxpacket = usb_endpoint_maxp(desc) & 0x07ff; + hwep->ep.mult = QH_ISO_MULT(usb_endpoint_maxp(desc)); - dbg_event(_usb_addr(mEp), "ENABLE", 0); + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) + cap |= QH_IOS; - mEp->qh.ptr->cap = 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); - 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->cap = cpu_to_le32(cap); - mEp->qh.ptr->cap |= - (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; - mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ + hwep->qh.ptr->td.next |= cpu_to_le32(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->udc, 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; } @@ -1056,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->udc, 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; } @@ -1094,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; } /** @@ -1124,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->udc->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); } /** @@ -1154,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 *udc = mEp->udc; + 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 = (udc->ep0_dir == RX) ? - udc->ep0out : udc->ep0in; - if (!list_empty(&mEp->qh.queue)) { - _ep_nuke(mEp); - retval = -EOVERFLOW; - dev_warn(mEp->udc->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->udc->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->udc->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; } @@ -1217,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->udc, 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->udc->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; } @@ -1256,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->udc, 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; } @@ -1298,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); } @@ -1321,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->udc->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->udc, 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); } /** @@ -1356,72 +1460,91 @@ 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 *udc = 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 (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS)) - return -EOPNOTSUPP; - - spin_lock_irqsave(&udc->lock, flags); - udc->vbus_active = is_active; - if (udc->driver) + spin_lock_irqsave(&ci->lock, flags); + ci->vbus_active = is_active; + if (ci->driver) gadget_ready = 1; - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); if (gadget_ready) { if (is_active) { pm_runtime_get_sync(&_gadget->dev); - hw_device_reset(udc, USBMODE_CM_DC); - hw_device_state(udc, udc->ep0out->qh.dma); + hw_device_reset(ci, USBMODE_CM_DC); + hw_device_state(ci, ci->ep0out->qh.dma); + usb_gadget_set_state(_gadget, USB_STATE_POWERED); } else { - hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, - CI13XXX_CONTROLLER_STOPPED_EVENT); - _gadget_stop_activity(&udc->gadget); + if (ci->driver) + ci->driver->disconnect(&ci->gadget); + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, + 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 *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; int ret = 0; - spin_lock_irqsave(&udc->lock, flags); - if (!udc->remote_wakeup) { + spin_lock_irqsave(&ci->lock, flags); + if (!ci->remote_wakeup) { ret = -EOPNOTSUPP; goto out; } - if (!hw_read(udc, OP_PORTSC, PORTSC_SUSP)) { + if (!hw_read(ci, OP_PORTSC, PORTSC_SUSP)) { ret = -EINVAL; goto out; } - hw_write(udc, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); + hw_write(ci, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); out: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); 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 *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); - if (udc->transceiver) - return usb_phy_set_power(udc->transceiver, mA); + if (ci->transceiver) + return usb_phy_set_power(ci->transceiver, ma); return -ENOTSUPP; } -static int ci13xxx_start(struct usb_gadget *gadget, +/* Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnet + */ +static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) +{ + 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); + else + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + + return 0; +} + +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, @@ -1429,40 +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, - .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 *udc) +static int init_eps(struct ci_hdrc *ci) { int retval = 0, i, j; - for (i = 0; i < udc->hw_ep_max/2; i++) + for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { - int k = i + j * udc->hw_ep_max/2; - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k]; + int k = i + j * ci->hw_ep_max/2; + 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->udc = udc; - mEp->lock = &udc->lock; - mEp->td_pool = udc->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; - mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; + 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 + */ + usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0); - INIT_LIST_HEAD(&mEp->qh.queue); - mEp->qh.ptr = dma_pool_alloc(udc->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, @@ -1470,30 +1599,44 @@ static int init_eps(struct ci13xxx *udc) */ if (i == 0) { if (j == RX) - udc->ep0out = mEp; + ci->ep0out = hwep; else - udc->ep0in = mEp; + ci->ep0in = hwep; + usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX); continue; } - list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + list_add_tail(&hwep->ep.ep_list, &ci->gadget.ep_list); } return retval; } +static void destroy_eps(struct ci_hdrc *ci) +{ + int i; + + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; + + 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 *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; int retval = -ENOMEM; @@ -1501,63 +1644,65 @@ static int ci13xxx_start(struct usb_gadget *gadget, return -EINVAL; - udc->ep0out->ep.desc = &ctrl_endpt_out_desc; - retval = usb_ep_enable(&udc->ep0out->ep); + ci->ep0out->ep.desc = &ctrl_endpt_out_desc; + retval = usb_ep_enable(&ci->ep0out->ep); if (retval) return retval; - udc->ep0in->ep.desc = &ctrl_endpt_in_desc; - retval = usb_ep_enable(&udc->ep0in->ep); + ci->ep0in->ep.desc = &ctrl_endpt_in_desc; + retval = usb_ep_enable(&ci->ep0in->ep); if (retval) return retval; - spin_lock_irqsave(&udc->lock, flags); - - 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, USBMODE_CM_DC); - } else { - pm_runtime_put_sync(&udc->gadget.dev); - goto done; - } + + 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->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(udc, udc->ep0out->qh.dma); + retval = hw_device_state(ci, ci->ep0out->qh.dma); + spin_unlock_irqrestore(&ci->lock, flags); if (retval) - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); - done: - spin_unlock_irqrestore(&udc->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 *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); - - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || - udc->vbus_active) { - hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, - CI13XXX_CONTROLLER_STOPPED_EVENT); - udc->driver = NULL; - spin_unlock_irqrestore(&udc->lock, flags); - _gadget_stop_activity(&udc->gadget); - spin_lock_irqsave(&udc->lock, flags); - pm_runtime_put(&udc->gadget.dev); + spin_lock_irqsave(&ci->lock, flags); + + if (ci->vbus_active) { + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, + 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); } - spin_unlock_irqrestore(&udc->lock, flags); + ci->driver = NULL; + spin_unlock_irqrestore(&ci->lock, flags); return 0; } @@ -1566,229 +1711,172 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * BUS block *****************************************************************************/ /** - * udc_irq: udc interrupt handler + * udc_irq: ci interrupt handler * * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -static irqreturn_t udc_irq(struct ci13xxx *udc) +static irqreturn_t udc_irq(struct ci_hdrc *ci) { irqreturn_t retval; u32 intr; - if (udc == NULL) + if (ci == NULL) return IRQ_HANDLED; - spin_lock(&udc->lock); + spin_lock(&ci->lock); - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) { - if (hw_read(udc, OP_USBMODE, USBMODE_CM) != + if (ci->platdata->flags & CI_HDRC_REGS_SHARED) { + if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return IRQ_NONE; } } - intr = hw_test_and_clear_intr_active(udc); - dbg_interrupt(intr); + intr = hw_test_and_clear_intr_active(ci); if (intr) { /* order defines priority - do NOT change it */ if (USBi_URI & intr) - isr_reset_handler(udc); + isr_reset_handler(ci); if (USBi_PCI & intr) { - udc->gadget.speed = hw_port_is_high_speed(udc) ? + ci->gadget.speed = hw_port_is_high_speed(ci) ? USB_SPEED_HIGH : USB_SPEED_FULL; - if (udc->suspended && udc->driver->resume) { - spin_unlock(&udc->lock); - udc->driver->resume(&udc->gadget); - spin_lock(&udc->lock); - udc->suspended = 0; + if (ci->suspended && ci->driver->resume) { + spin_unlock(&ci->lock); + ci->driver->resume(&ci->gadget); + spin_lock(&ci->lock); + ci->suspended = 0; } } if (USBi_UI & intr) - isr_tr_complete_handler(udc); + isr_tr_complete_handler(ci); if (USBi_SLI & intr) { - if (udc->gadget.speed != USB_SPEED_UNKNOWN && - udc->driver->suspend) { - udc->suspended = 1; - spin_unlock(&udc->lock); - udc->driver->suspend(&udc->gadget); - spin_lock(&udc->lock); + if (ci->gadget.speed != USB_SPEED_UNKNOWN && + ci->driver->suspend) { + 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); } } retval = IRQ_HANDLED; } else { retval = IRQ_NONE; } - spin_unlock(&udc->lock); + 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 - * @udc: chipidea controller + * @ci: chipidea controller */ -static int udc_start(struct ci13xxx *udc) +static int udc_start(struct ci_hdrc *ci) { - struct device *dev = udc->dev; + struct device *dev = ci->dev; int retval = 0; - if (!udc) - return -EINVAL; - - spin_lock_init(&udc->lock); - - udc->gadget.ops = &usb_gadget_ops; - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->gadget.max_speed = USB_SPEED_HIGH; - udc->gadget.is_otg = 0; - udc->gadget.name = udc->udc_driver->name; + spin_lock_init(&ci->lock); - INIT_LIST_HEAD(&udc->gadget.ep_list); + ci->gadget.ops = &usb_gadget_ops; + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->gadget.max_speed = USB_SPEED_HIGH; + ci->gadget.is_otg = ci->is_otg ? 1 : 0; + ci->gadget.name = ci->platdata->name; - 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; + INIT_LIST_HEAD(&ci->gadget.ep_list); /* alloc resources */ - udc->qh_pool = dma_pool_create("ci13xxx_qh", dev, - sizeof(struct ci13xxx_qh), - 64, CI13XXX_PAGE_SIZE); - if (udc->qh_pool == NULL) + 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; - udc->td_pool = dma_pool_create("ci13xxx_td", dev, - sizeof(struct ci13xxx_td), - 64, CI13XXX_PAGE_SIZE); - if (udc->td_pool == NULL) { + 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; } - retval = init_eps(udc); + retval = init_eps(ci); if (retval) goto free_pools; - udc->gadget.ep0 = &udc->ep0in->ep; - - udc->transceiver = usb_get_transceiver(); - - if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { - if (udc->transceiver == NULL) { - retval = -ENODEV; - goto free_pools; - } - } - - if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { - retval = hw_device_reset(udc, USBMODE_CM_DC); - if (retval) - goto put_transceiver; - } - - retval = device_register(&udc->gadget.dev); - if (retval) { - put_device(&udc->gadget.dev); - goto put_transceiver; - } - - retval = dbg_create_files(&udc->gadget.dev); - if (retval) - goto unreg_device; - - if (udc->transceiver) { - retval = otg_set_peripheral(udc->transceiver->otg, - &udc->gadget); - if (retval) - goto remove_dbg; - } + ci->gadget.ep0 = &ci->ep0in->ep; - retval = usb_add_gadget_udc(dev, &udc->gadget); + retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) - goto remove_trans; + goto destroy_eps; - pm_runtime_no_callbacks(&udc->gadget.dev); - pm_runtime_enable(&udc->gadget.dev); + pm_runtime_no_callbacks(&ci->gadget.dev); + pm_runtime_enable(&ci->gadget.dev); return retval; -remove_trans: - if (udc->transceiver) { - otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_transceiver(udc->transceiver); - } - - dev_err(dev, "error = %i\n", retval); -remove_dbg: - dbg_remove_files(&udc->gadget.dev); -unreg_device: - device_unregister(&udc->gadget.dev); -put_transceiver: - if (udc->transceiver) - usb_put_transceiver(udc->transceiver); +destroy_eps: + destroy_eps(ci); free_pools: - dma_pool_destroy(udc->td_pool); + dma_pool_destroy(ci->td_pool); free_qh_pool: - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->qh_pool); return retval; } /** - * 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 *udc) +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) { - int i; - - if (udc == NULL) + if (!ci->roles[CI_ROLE_GADGET]) return; - usb_del_gadget_udc(&udc->gadget); + usb_del_gadget_udc(&ci->gadget); - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + destroy_eps(ci); - dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma); - } + dma_pool_destroy(ci->td_pool); + dma_pool_destroy(ci->qh_pool); +} - dma_pool_destroy(udc->td_pool); - dma_pool_destroy(udc->qh_pool); +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); - if (udc->transceiver) { - otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_transceiver(udc->transceiver); - } - dbg_remove_files(&udc->gadget.dev); - device_unregister(&udc->gadget.dev); - /* my kobject is dynamic, I swear! */ - memset(&udc->gadget, 0, sizeof(udc->gadget)); + 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; @@ -1799,11 +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; - return 0; + return udc_start(ci); } |
