diff options
Diffstat (limited to 'drivers/otg/hardware/arc-pcd.c')
-rw-r--r-- | drivers/otg/hardware/arc-pcd.c | 1088 |
1 files changed, 1088 insertions, 0 deletions
diff --git a/drivers/otg/hardware/arc-pcd.c b/drivers/otg/hardware/arc-pcd.c new file mode 100644 index 000000000000..03e37695d7ee --- /dev/null +++ b/drivers/otg/hardware/arc-pcd.c @@ -0,0 +1,1088 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* + * otg/hardware/arc-pcd.c -- Freescale HS (ARC) USBOTG Peripheral Controller driver + * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/arc/arc-pcd.c|20070918212334|07623 + * + * Copyright (c) 2007 Belcarra Technologies 2005 Corp + * + * By: + * Shahrad Payandeh Lynne <sp@lbelcarra.com>, + * Stuart Lynne <sl@lbelcarra.com>, + */ +/*! + * @file otg/hardware/arc-pcd.c + * @brief USB Peripheral Controller Driver + * This implements the Freescale HS (ARC) USBOTG Peripheral Controller Driver. + * + * @ingroup ARC + * @ingroup PCD + * @ingroup OTGDEV + * @ingroup LINUXOS + */ + +#include <otg/pcd-include.h> +#include <otg/otg-dev.h> +#include <linux/dma-mapping.h> +#include <asm/arch/arc_otg.h> +#include <linux/dmapool.h> +#include <linux/delay.h> +#include "arc-hardware.h" +#include "arc.h" + +#define HAVE_PLATFORM_ARC_REMOTE_WAKEUP 1 + +#define TRACE_VERBOSE 1 +#define UDC_MAX_ENDPOINTS 16 +#define UDC_NAME "arc" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) +#include <linux/platform_device.h> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */ +#include <linux/fsl_devices.h> +#include <linux/usb/fsl_xcvr.h> + +/* ep_qh_base store the base address before 2K align */ +static struct ep_queue_head *ep_qh_base; +static struct arcotg_udc *udc_controller; +extern void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on); + +/*! arc_read_setup_buffer + */ +static void arc_read_setup_buffer(struct pcd_instance *pcd, u8 * buffer_ptr) +{ + struct ep_queue_head *qh = &udc_controller->ep_qh[0 * 2 + ARC_DIR_OUT]; + int timeout; + + consistent_sync(qh, sizeof(struct ep_queue_head), DMA_FROM_DEVICE); + + /* C.f 39.16.3.2.1 Setup Phase - Setup Packet Handling (2.3 hardware and later) + */ + + /* 1. Clear ENDPTSETUPSTAT */ + UOG_ENDPTSETUPSTAT |= (1 << 0); /* Clear recieve endpoint status */ + + do { + /* 2. set tripwire */ + UOG_USBCMD |= USB_CMD_SUTW; + + /* 3. read setup buffer */ + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + + /* 4. check tripwire, loop if reset */ + } while (!(UOG_USBCMD & USB_CMD_SUTW)); + + /* 5. reset tripwire */ + UOG_USBCMD &= ~USB_CMD_SUTW; + + // XXX This needs to be fixed, should not need to resort to timeout + timeout = 10000000; + while ((UOG_ENDPTSETUPSTAT & 1) && --timeout) { continue; } + if (timeout == 0) printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); +} + +/*! arc_read_rcv_buffer + * + * Recover number of bytes DMA'd to receive buffer, sync. + */ +int arc_read_rcv_buffer (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + struct arc_private_struct *privdata = endpoint->privdata; + struct ep_td_struct *curr_td = privdata->cur_dtd; + struct ep_queue_head *qh = privdata->cur_dqh; + struct usbd_urb *rx_urb = endpoint->rcv_urb; + + /* sync qh and td structures, note that urb-buffer was invalidated in arc_add_buffer_to_dtd() */ + consistent_sync(qh, sizeof(struct ep_queue_head), DMA_FROM_DEVICE); + consistent_sync(curr_td, sizeof(struct ep_td_struct), DMA_FROM_DEVICE); + + if (rx_urb) { + int length = rx_urb->buffer_length - + ((le32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS); + + if (TRACE_VERBOSE) TRACE_MSG4(pcd->TAG, "buffer_length: %d alloc_length: %d Len: %d (%x)" , + rx_urb->buffer_length, rx_urb->alloc_length, length, + le32_to_cpu(curr_td->size_ioc_sts)); + return length; + } + TRACE_MSG1(pcd->TAG, "NO RCV URB (%x)" , le32_to_cpu(curr_td->size_ioc_sts)); + return 0; +} + +/*! arc_udc_init + */ +void arc_udc_release (void) +{ + kfree(ep_qh_base); + ep_qh_base = NULL; + if (udc_controller) { + if (udc_controller->ep_dtd) LKFREE(udc_controller->ep_dtd); + LKFREE(udc_controller); + udc_controller = NULL; + } +} + +/*! arc_udc_init + */ +static struct arcotg_udc *arc_udc_init (struct otg_dev *otg_dev) +{ + struct device *device = otg_dev_get_drvdata(otg_dev); + struct platform_device *pdev = to_platform_device(device); + + struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data; + + struct otg_instance *otg = otg_dev->otg_instance; + struct pcd_instance *pcd = otg_dev->pcd_instance; + struct arcotg_udc *udc = NULL; + int timeout; + + + /* Setting up the udc structure */ + THROW_UNLESS((udc = (struct arcotg_udc *) CKMALLOC(sizeof(struct arcotg_udc))), error); + + /* Allocate queue */ + THROW_UNLESS((udc->ep_qh = (struct ep_queue_head *) KMALLOC_ALIGN( USB_MAX_PIPES * sizeof(struct ep_queue_head), + GFP_KERNEL | GFP_DMA, 2 * 1024, (void **)&ep_qh_base)), error); + THROW_UNLESS(ep_qh_base, error); + THROW_UNLESS((udc->ep_dtd = (struct ep_td_struct *) CKMALLOC(USB_MAX_PIPES * sizeof(struct ep_td_struct))), error); + + /* Stop, reset and wait for the UDC to reset */ + UOG_USBCMD &= ~USB_CMD_RUN_STOP; + UOG_USBCMD |= USB_CMD_CTRL_RESET; + + timeout = 10000000; // XXX This needs to be fixed, should not need to resort to timeout + while ((UOG_USBCMD & USB_CMD_CTRL_RESET) && --timeout) { continue; } + if (timeout == 0) { + printk(KERN_DEBUG "%s: TIMEOUT\n", __FUNCTION__); + return NULL; + } + + /* Setup UDC mode and disable lock out mode*/ + UOG_USBMODE |= USB_MODE_CTRL_MODE_DEVICE | USB_MODE_SETUP_LOCK_OFF; + + UOG_EPLISTADDR = virt_to_phys(udc->ep_qh); + UOG_EPLISTADDR &= USB_EP_LIST_ADDRESS_MASK; + + /* Setup transceiver type, N.B. this must be done in one assignment */ + UOG_PORTSC1 = (UOG_PORTSC1 & ~PORTSCX_PHY_TYPE_SEL) | pdata->xcvr_type; + + #if !defined(CONFIG_OTG_HIGH_SPEED) + UOG_PORTSC1 |= (0x01000000); + #endif + fsl_platform_set_vbus_power(pdata, 0); + + CATCH(error) { + if (ep_qh_base) kfree(ep_qh_base); + if (udc) { + if (udc->ep_dtd) LKFREE(udc->ep_dtd); + LKFREE(udc); + } + udc = NULL; + } + return udc; +} + +/*! arc_udc_run + */ +static int arc_udc_run (void) +{ + /* enable the interrupt sources we want */ + UOG_USBINTR |= USB_INTR_INT_EN | USB_INTR_ERR_INT_EN | USB_INTR_PTC_DETECT_EN | + USB_INTR_RESET_EN | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + /* Set to peripheral mode */ + UOG_USBMODE |= USB_MODE_CTRL_MODE_DEVICE; + + /* set interrupt threshold to zero, run udc */ + UOG_USBCMD &= ~USB_CMD_ITC; + UOG_USBCMD |= USB_CMD_ITC_NO_THRESHOLD | USB_CMD_RUN_STOP; + return 0; +} + +/*! arc_udc_stop + */ +void arc_udc_stop (void) +{ + /* Disable all interrupts */ + UOG_USBINTR = 0; + UOG_USBCMD &= ~USB_CMD_RUN_STOP; +} + +/*! arc_pcd_start + */ +void arc_pcd_start (struct pcd_instance *pcd) +{ + u32 id = (u32) UOG_ID; + TRACE_MSG1(pcd->TAG, "Initialize UDC and start id: %08x", id); + arc_udc_run (); +} + +/*! arc_pcd_stop + */ +void arc_pcd_stop(struct pcd_instance *pcd) +{ + TRACE_MSG0(pcd->TAG, "Stop"); + arc_udc_stop (); +} + +/*! arc_pcd_disable + */ +void arc_pcd_disable(struct pcd_instance *pcd) +{ + TRACE_MSG0(pcd->TAG, "Disabled"); +} + +/*! arc_pcd_framenum() - get current framenum + */ +static u16 arc_pcd_framenum (struct pcd_instance *pcd) +{ + u16 frame = (UOG_FRINDEX & USB_FRINDEX_MASKS); + frame &= ~(0x7); + frame >>= 3; + return (int)( frame ); +} + +/* arc_pcd_ticks - get current ticks + */ +#define MXC_GPT_GPTCNT (IO_ADDRESS(GPT1_BASE_ADDR + 0x24)) +otg_tick_t arc_pcd_ticks (struct pcd_instance *pcd) +{ + unsigned long ticks = 0; + ticks = __raw_readl(MXC_GPT_GPTCNT); + return (otg_tick_t) ticks; +} + +/* arc_pcd_elapsed - return micro-seconds between two tick values + */ +otg_tick_t arc_pcd_elapsed(otg_tick_t *t1, otg_tick_t *t2) +{ + otg_tick_t ticks = (((*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1))); + return (otg_tick_t) ticks / 17; // 185 vs 189 +} + +/*****************************************************************************************/ +/*! arc_add_buffer_to_dtd + * + * C.f. 39.16.5.3 - case 1: Link list is empty + */ +static void arc_add_buffer_to_dtd (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, + int dir, int len, int offset) +{ + struct otg_instance *otg = pcd->otg; + struct usbd_urb *urb = endpoint->active_urb; + struct arc_private_struct *privdata = endpoint->privdata; + + u8 hs = pcd->bus->high_speed; + u8 physicalEndpoint = endpoint->physicalEndpoint[hs]; + u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + u8 epnum = bEndpointAddress & 0x3f; + u16 wMaxPacketSize = endpoint->wMaxPacketSize[hs]; + struct ep_queue_head *dQH = &udc_controller->ep_qh[2 * epnum + dir]; + struct ep_td_struct *dtd = &(udc_controller->ep_dtd[2 * epnum + dir]); + + u32 mask = 0; + int timeout1 = 0; + int timeout2 = 0; + + u32 endptstat = -1; + u32 endptprime = -1; + u32 endptcomplete = -1; + + TRACE_MSG6(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x STATUS: %08x %s", + 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, + (u32)dQH->size_ioc_int_sts, (dir == ARC_DIR_OUT) ? "OUT" : "IN"); + + if (urb && urb->buffer) { + + TRACE_MSG4(pcd->TAG, "buffer: %x length: %d alloc: %d dir: %d ", + urb->buffer, urb->actual_length, urb->buffer_length, dir); + + /* flush cache for IN */ + if ((dir == ARC_DIR_IN) && urb->actual_length) + consistent_sync(urb->buffer, urb->actual_length, DMA_TO_DEVICE); + + /* invalidate cache for OUT */ + else if ((dir == ARC_DIR_OUT) && urb->buffer_length) + consistent_sync(urb->buffer, urb->alloc_length, DMA_FROM_DEVICE); + } + + /* Set size and interrupt on each dtd, Clear reserved field, + * set pointers and flush from cache, and save in cur_dqh for dtd_releases() + */ + memset(dtd, 0, sizeof(struct ep_td_struct)); + dtd->size_ioc_sts = cpu_to_le32(((len << DTD_LENGTH_BIT_POS) | DTD_IOC | DTD_STATUS_ACTIVE)); + dtd->size_ioc_sts &= cpu_to_le32(~DTD_RESERVED_FIELDS); + dtd->buff_ptr0 = cpu_to_le32(endpoint->active_urb ? (u32) (virt_to_phys (endpoint->active_urb->buffer + offset)) : 0); + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + dtd->next_td_virt = NULL; + consistent_sync(dtd, sizeof(struct ep_td_struct), DMA_TO_DEVICE); + privdata->cur_dqh = dQH; + + /* Case 1 - Step 1 - Write dQH next pointer and dQH terminate bit to 0 as single DWord */ + dQH->next_dtd_ptr = cpu_to_le32( virt_to_phys((void *)dtd) & EP_QUEUE_HEAD_NEXT_POINTER_MASK); + + /* Case 1 - Step 2 - Clear active and halt bit */ + dQH->size_ioc_int_sts &= le32_to_cpu(~(EP_QUEUE_HEAD_STATUS_ACTIVE | EP_QUEUE_HEAD_STATUS_HALT)); + + consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_TO_DEVICE); + + /* Case 1 - Step 3 - Prime endpoint by writing ENDPTPRIME */ + mask = (dir == ARC_DIR_OUT) ? (1 << epnum) : (1 << (epnum + 16)); + + /* Verify that endpoint PRIME is not set, wait if necessary. */ + for (timeout1 = 0; (UOG_ENDPTPRIME & mask) && (timeout1 ++ < 100); udelay(1)); + + /* ep0 needs extra tests */ + UNLESS(epnum) { + /* C.f. 39.16.3.2.2 Data Phase */ + UOG_ENDPTPRIME |= mask; + for (timeout2 = 0; timeout2++ < 100; ) { + endptprime = UOG_ENDPTPRIME; // order may be important + endptstat = UOG_ENDPTSTAT; // we check stat after prime + BREAK_IF(endptstat & mask); + BREAK_UNLESS(endptprime & mask); + } + if (!(endptstat & mask) && !(endptprime & mask)) { + TRACE_MSG2(pcd->TAG, "[%2d] ENDPTSETUPSTAT: %04x PREMATURE FAILUURE", 2*epnum+dir, UOG_ENDPTSETUPSTAT); + } + TRACE_MSG6(pcd->TAG, "[%2d] ENDPTPRIME %08x ENPTSTAT: %08x mask: %08x timeout: %d:%d SET", + 2*epnum+dir, UOG_ENDPTPRIME, UOG_ENDPTSTAT, mask, timeout1, timeout2);; + } + /* epn general case */ + else { + /* Hit PRIME bit until STATUS bit is set. */ + UOG_ENDPTPRIME |= mask; + //for (timeout2 = 0; !(UOG_ENDPTSTAT & mask) && (timeout2++ < 100); /* UOG_ENDPTPRIME |= mask */); + + for (timeout2 = 0; timeout2++ < 100; ) { + endptprime = UOG_ENDPTPRIME; // order may be important + endptstat = UOG_ENDPTSTAT; // we check stat after prime + endptcomplete = UOG_ENDPTCOMPLETE; + BREAK_IF(endptstat & mask); + BREAK_IF(endptcomplete & mask); + UNLESS(endptprime & mask) { + TRACE_MSG8(pcd->TAG, "[%2d] ENDPTPRIME %08x:%08x ENPTSTAT: %08x:%08x COMPLETE: %08x " + "mask: %08x timeout: %x NOT SET", + 2*epnum+dir, endptprime, UOG_ENDPTPRIME, endptstat, UOG_ENDPTSTAT, + UOG_ENDPTCOMPLETE, + mask, (timeout1 << 8) | timeout2);; + udelay(1); + endptstat = UOG_ENDPTSTAT; // we check stat after prime + BREAK_IF(endptstat & mask); + UOG_ENDPTPRIME |= mask; + } + } + TRACE_MSG8(pcd->TAG, "[%2d] ENDPTPRIME %08x:%08x ENPTSTAT: %08x:%08x COMPLETE: %08x " + "mask: %08x timeout: %x SET", + 2*epnum+dir, endptprime, UOG_ENDPTPRIME, endptstat, UOG_ENDPTSTAT, + UOG_ENDPTCOMPLETE, + mask, (timeout1 << 8) | timeout2);; + } + +} + +/* arc_dtd_releases + */ +static void arc_dtd_releases (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int dir) +{ + struct arc_private_struct *privdata = endpoint->privdata; + consistent_sync(privdata->cur_dtd, sizeof(struct ep_td_struct), (dir == ARC_DIR_OUT) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + consistent_sync(privdata->cur_dqh, sizeof(struct ep_queue_head), (dir == ARC_DIR_OUT) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + + +/* arc_pcd_start_endpoint_in - start transmit + */ +void arc_pcd_start_endpoint_in (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + struct otg_instance *otg = pcd->otg; + struct usbd_urb *tx_urb; + u8 hs = pcd->bus->high_speed; + u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + u8 epnum = bEndpointAddress & 0x3f; + + UNLESS(epnum) { + TRACE_MSG1(pcd->TAG, "[ 0] ENDPTSETUPSTAT: %04x", UOG_ENDPTSETUPSTAT); + RETURN_IF (UOG_ENDPTSETUPSTAT & 0x1); + } + if ((tx_urb = endpoint->tx_urb)) { + endpoint->last = pcd_tx_sendzlp(endpoint) ? 0 : tx_urb->actual_length; + TRACE_MSG4(pcd->TAG, "[%2d] urb length: %d sent: %d %s", + epnum, tx_urb->actual_length, endpoint->sent, endpoint->last ? "" : "ZLP"); + arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_IN, endpoint->last, endpoint->sent); + } +} + +/* arc_pcd_start_endpoint_out - start receive + */ +void arc_pcd_start_endpoint_out (struct pcd_instance *pcd,struct usbd_endpoint_instance *endpoint) +{ + //struct usbd_urb *rcv_urb; + //u8 hs = pcd->bus->high_speed; + //u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + //u8 epnum = bEndpointAddress & 0x3f; + + struct usbd_urb *rcv_urb; + u8 hs; + u8 bEndpointAddress; + u8 epnum; + + UNLESS (pcd) printk(KERN_INFO"%s: pcd: %p\n", __FUNCTION__, pcd); + UNLESS (pcd) printk(KERN_INFO"%s: pcd->bus: %p\n", __FUNCTION__, pcd->bus); + hs = pcd->bus->high_speed; + + UNLESS (endpoint) printk(KERN_INFO"%s: endpoint: %p\n", __FUNCTION__, endpoint); + bEndpointAddress = endpoint->bEndpointAddress[hs]; + epnum = bEndpointAddress & 0x3f; + + UNLESS(epnum) { + //otg_led(LED2, 0); + TRACE_MSG1(pcd->TAG, "[ 0] ENDPTSETUPSTAT: %04x", UOG_ENDPTSETUPSTAT); + RETURN_IF (UOG_ENDPTSETUPSTAT & 0x1); + //otg_led(LED2, 1); + } + if ((rcv_urb = pcd_rcv_next_irq(endpoint))) { + TRACE_MSG3(pcd->TAG, "[%2d] urb length: %d actual: %d length: %d", epnum, + rcv_urb->actual_length, rcv_urb->buffer_length); + arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_OUT, rcv_urb->buffer_length, 0); + } +} + +/*! arc_pcd_setup_ep - setup endpoint + * @param pcd - + * @param ep - + * @param endpoint + * @return none + */ +void arc_pcd_setup_ep (struct pcd_instance *pcd, unsigned int ep, struct usbd_endpoint_instance *endpoint) +{ + struct usbd_bus_instance *bus = pcd->bus; + struct ep_queue_head *dQH = NULL; + + u8 hs = pcd->bus->high_speed; + u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + u8 bmAttributes = endpoint->bmAttributes[hs]; + u8 epnum = bEndpointAddress & 0x3f; + u16 wMaxPacketSize = endpoint->wMaxPacketSize[hs]; + u8 dir = (bEndpointAddress & 0x80) ? 1 : 0; + u8 type = bmAttributes & 0x3; + + int timeout; + unsigned int tmp = 0; + + struct arc_private_struct *privdata; + + dQH = &udc_controller->ep_qh[2 * epnum + dir]; + UNLESS(endpoint->privdata) { + endpoint->privdata = (struct arc_private_struct *) CKMALLOC (sizeof(struct arc_private_struct)); + } + RETURN_UNLESS(privdata = endpoint->privdata); + + /* Set the packet size */ + switch (type) { + case 0: /* Control */ + tmp = (wMaxPacketSize << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS; + break; + + case 2: /* Bulk */ + case 3: /* Interrupt */ + tmp = wMaxPacketSize << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + break; + } + dQH->max_pkt_length = cpu_to_le32(tmp); + + consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_TO_DEVICE); + + /* Set the control register of endpoint */ + tmp = dir ? + (EPCTRL_TX_ENABLE | ((u32)(type) << EPCTRL_TX_EP_TYPE_SHIFT) | (epnum ? EPCTRL_TX_DATA_TOGGLE_RST : 0)) : + (EPCTRL_RX_ENABLE | ((u32)(type) << EPCTRL_RX_EP_TYPE_SHIFT) | (epnum ? EPCTRL_RX_DATA_TOGGLE_RST : 0)); + + USBOTG_REG32(0x1c0 + epnum*4) |= tmp; + + /* wait for correct writing */ + timeout = 10000000; // XXX This needs to be fixed, should not need to resort to timeout + while ( ( !(USBOTG_REG32(0x1c0 + epnum*4) & (tmp & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE)) ) ) && --timeout) { continue; } + if (timeout == 0) + printk(KERN_INFO "%s: TIMEOUT\n", __FUNCTION__); + + TRACE_MSG4(pcd->TAG, "[%2x] Control register for size: %d is %x (%x)", + 2*epnum+dir, wMaxPacketSize, tmp, USBOTG_REG32(0x1c0 + epnum*4)); + + if (!epnum) { + dir = 1; + dQH = &udc_controller->ep_qh[2 * epnum + dir]; + tmp = (wMaxPacketSize << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS; + dQH->max_pkt_length = le32_to_cpu(tmp); + consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_FROM_DEVICE); + tmp = USBOTG_REG32(0x1c0 + epnum*4); + tmp |= EPCTRL_TX_ENABLE; + tmp |= ((unsigned int)(type) << EPCTRL_TX_EP_TYPE_SHIFT); + USBOTG_REG32(0x1c0 + epnum*4) = tmp; + + // XXX This needs to be fixed, should not need to resort to timeout, wait for correct writing + timeout = 10000000; + while ( ( !(USBOTG_REG32(0x1c0 + epnum*4) & (tmp & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE)) ) ) && --timeout) + continue; + if (timeout == 0) printk(KERN_INFO "%s: TIMEOUT\n", __FUNCTION__); + } + privdata->cur_dtd = &(udc_controller->ep_dtd[2 * epnum + dir]); + privdata->cur_dqh = &udc_controller->ep_qh[2 * epnum + dir]; +} + +/* arc_pcd_disable_ep - + */ +void arc_pcd_disable_ep(struct pcd_instance *pcd, unsigned int ep, struct usbd_endpoint_instance *endpoint) +{ + TRACE_MSG1(pcd->TAG, "endpoint->privdata: %p", endpoint->privdata); + RETURN_UNLESS (endpoint->privdata); + LKFREE(endpoint->privdata); + endpoint->privdata = NULL; +} + + +/*! arc_pcd_cancel_irq + */ +void arc_pcd_cancel_irq (struct pcd_instance *pcd, struct usbd_urb *urb) +{ + struct usbd_endpoint_instance *endpoint = urb->endpoint; + + u8 hs = pcd->bus->high_speed; + u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + u8 epnum = bEndpointAddress & 0x3f; + u8 dir = (bEndpointAddress & 0x80) ? 1 : 0; + + u32 mask = (dir == ARC_DIR_OUT) ? (1 << epnum) : (1 << (epnum + 16)); + + TRACE_MSG5(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x mask: %08x FLUSH", + 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, mask); + + + UOG_ENDPTFLUSH = mask; + + TRACE_MSG5(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x mask: %08x FLUSH", + 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, mask); + + TRACE_MSG5(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x mask: %08x FLUSH", + 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, mask); + +} + + +extern void fsl_platform_test_mode_select(struct fsl_usb2_platform_data *pdata, int on); +/* arc_pcd_device_feature - device feature + */ +int arc_pcd_device_feature(struct pcd_instance *pcd, int selector, int flag) +{ + u32 tmp = UOG_PORTSC1 | (selector << 16); + UOG_PORTSC1 = tmp; + return 0; +} + +/* + * arc_endpoint_halted() - is endpoint halted + */ +static int +arc_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) +{ + u8 hs = pcd->bus->high_speed; + u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + u8 epnum = bEndpointAddress & 0x3f; + u8 dir = (bEndpointAddress & 0x80) ? 1 : 0; + u32 tmp = USBOTG_REG32(0x1c0 + epnum*4); + switch (dir) { + case USB_DIR_IN: + TRACE_MSG3(pcd->TAG, "epn: %d dir: %d ENDPTCTRLn: %04x USB_DIR_IN", epnum, dir, tmp); + return BOOLEAN( tmp & EPCTRL_TX_EP_STALL ); + + case USB_DIR_OUT: + TRACE_MSG3(pcd->TAG, "epn: %d dir: %d ENDPTCTRLn: %04x USB_DIR_OUT", epnum, dir, tmp); + return BOOLEAN( tmp & EPCTRL_RX_EP_STALL ); + } + return 0; +} + +/* arc_halt_endpoint - halt endpoint + */ +int arc_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag) +{ + u8 hs = pcd->bus->high_speed; + u8 bEndpointAddress = endpoint->bEndpointAddress[hs]; + u8 epnum = bEndpointAddress & 0x3f; + u8 dir = (bEndpointAddress & 0x80) ? 1 : 0; + u32 tmp = USBOTG_REG32(0x1c0 + epnum*4); + u32 mask = (dir == USB_DIR_IN) ? EPCTRL_TX_EP_STALL : EPCTRL_RX_EP_STALL; + + tmp = flag ? (tmp | mask) : (tmp & ~mask); + USBOTG_REG32(0x1c0 + epnum*4) = tmp; + + TRACE_MSG5(pcd->TAG, "epn: %d dir: %d flag: %d ENDPTCTRLn: %04x USB_DIR_%s STALLING", + epnum, dir, flag, tmp, (dir == USB_DIR_IN) ? "IN" : "OUT"); + return 0; +} + + +#if HAVE_PLATFORM_ARC_REMOTE_WAKEUP +extern void fsl_platform_perform_remote_wakeup(struct fsl_usb2_platform_data *pdata); + +/*! arc_remote_wakeup + */ +static int arc_remote_wakeup(struct pcd_instance *pcd) +{ + struct usbd_bus_instance *bus = pcd->bus; + struct otg_dev *otg_dev = pcd->privdata; + struct device *device = otg_dev_get_drvdata(otg_dev); + struct platform_device *pdev = to_platform_device(device); + struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data; + + printk(KERN_INFO"%s:\n", __FUNCTION__); + fsl_platform_perform_remote_wakeup(pdata); + return 0; +} +#endif + +/*! arc_vbus_status() - enable + * This is called to return status of the PCD and USBD stack. + * Start or stop the UDC. + */ +static int +arc_vbus_status (struct pcd_instance *pcd) +{ + u32 vbus = UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS; + TRACE_MSG2(pcd->TAG, "UOG_PORTSC1: %x, vbus: %x", UOG_PORTSC1, vbus); + return BOOLEAN(vbus); +} + +/*! arc_softcon + * @param pcd - pcd pointer + * @param flag - + * + * Enable or disable pullup. + */ +int arc_softcon(struct pcd_instance *pcd, int flag) +{ + TRACE_MSG1(pcd->TAG, "ARC PULLUP %s", flag ? "SET": "RESET"); + UOG_USBCMD = flag ? (UOG_USBCMD | USB_CMD_RUN_STOP) : (UOG_USBCMD & ~USB_CMD_RUN_STOP); + return 0; +} + + +/* ********************************************************************************************* */ + +struct usbd_pcd_ops usbd_pcd_ops = { + .bmAttributes = + #ifdef CONFIG_OTG_SELF_POWERED + USB_BMATTRIBUTE_SELF_POWERED | + #endif /* CONFIG_OTG_SELF_POWERED */ + #ifdef CONFIG_OTG_REMOTE_WAKEUP + USB_BMATTRIBUTE_REMOTE_WAKEUP | + #endif /* CONFIG_OTG_REMOTE_WAKEUP */ + //USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED | + USB_BMATTRIBUTE_RESERVED, + #ifndef CONFIG_OTG_SELF_POWERED + .bMaxPower = CONFIG_OTG_BMAXPOWER, + #else /* CONFIG_OTG_SELF_POWERED */ + .bMaxPower = 1, + #endif /* CONFIG_OTG_SELF_POWERED */ + .max_endpoints = 0, + .high_speed_capable = TRUE, + .ep0_packetsize = 64, + .name = UDC_NAME, + .start = arc_pcd_start, + .stop = arc_pcd_stop, + .disable = arc_pcd_disable, + .disable_ep = arc_pcd_disable_ep, + .start_endpoint_in = arc_pcd_start_endpoint_in, + .start_endpoint_out = arc_pcd_start_endpoint_out, + .request_endpoints = pcd_request_endpoints, + .setup_ep = arc_pcd_setup_ep, + .cancel_in_irq = arc_pcd_cancel_irq, + .cancel_out_irq = arc_pcd_cancel_irq, + .device_feature = arc_pcd_device_feature, + .halt_endpoint = arc_halt_endpoint, + .endpoint_halted = arc_endpoint_halted, + .remote_wakeup = arc_remote_wakeup, + .vbus_status = arc_vbus_status, + .softcon = arc_softcon, + .ticks = arc_pcd_ticks, + .elapsed = arc_pcd_elapsed, + .framenum = arc_pcd_framenum, +}; + +/* ********************************************************************************************* */ +/*! arc_ep0_irq + */ +static irqreturn_t arc_ep0_irq(struct pcd_instance *pcd) +{ + struct usbd_endpoint_instance *endpoint = pcd->bus->endpoint_array + 0; + struct usbd_urb *urb = endpoint->active_urb; + + /* SETUP - Device request received */ + if (UOG_ENDPTSETUPSTAT & 0x1) { + struct usbd_device_request request; + TRACE_MSG2(pcd->TAG, "EP0 SETUP active: %p %x", urb, urb ? urb->flags : 0); + + /* Complete any outstanding transfers */ + if (urb && (urb->flags & USBD_URB_IN)) pcd_tx_complete_irq(endpoint, 0); + if (urb && (urb->flags & USBD_URB_OUT)) pcd_rcv_complete_irq(endpoint, 0, 0); + + /* Read setup - reset UOG_ENDPTSETUPSTAT */ + arc_read_setup_buffer(pcd, (u8 *) &request); + + if (pcd_recv_setup_irq(pcd, &request)) { + /* endpoint requested not handled - need to stall */ + TRACE_MSG0(pcd->TAG, "REQUEST NOT HANDLED - STALLED"); + USBOTG_REG32(0x1c0 + 0*4) |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; + return IRQ_HANDLED; + } + /* Finish if no data phase */ + UNLESS (request.wLength) { + TRACE_MSG0(pcd->TAG, "REQUEST HANDLED OK - NO DATA PHASE"); + arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_IN, 0, 0); + } + return IRQ_HANDLED; + } + /* TX/IN Complete */ + if (UOG_ENDPTCOMPLETE & 0x10000) { + TRACE_MSG2(pcd->TAG, "EP0 IN COMPLETE active: %p %x", urb, urb ? urb->flags : 0); + + /* set address here if previous request was SET_ADDRESS */ + if (pcd->new_address) { + UOG_DEVICEADDR = pcd->new_address << USB_DEVICE_ADDRESS_BIT_POS; + pcd->new_address = 0; + } + + /* sync and clear complete bit */ + arc_dtd_releases (pcd, endpoint, ARC_DIR_IN); + UOG_ENDPTCOMPLETE = 0x10000; + + /* finish urb, reset zlp flag if it's there, we always send zlp */ + if (pcd_tx_complete_irq(endpoint, 0)) { + TRACE_MSG2(pcd->TAG, "EP0 IN COMPLETE ZLP check: %p %x", urb, urb ? urb->flags : 0); + if (pcd_tx_sendzlp(endpoint)) { + pcd_tx_complete_irq(endpoint, 0); + TRACE_MSG2(pcd->TAG, "EP0 IN COMPLETE ZLP active: %p %x", urb, urb ? urb->flags : 0); + } + } + arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_OUT, 0, 0); + return IRQ_HANDLED; + } + /* RX/OUT Complete */ + if (UOG_ENDPTCOMPLETE & 0x1) { + struct arc_private_struct *privdata = endpoint->privdata; + struct ep_td_struct *curr_td = privdata->cur_dtd; + int length; + + /* sync and clear complete bit */ + arc_dtd_releases (pcd, endpoint, ARC_DIR_OUT); + length = ((le32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS); + UOG_ENDPTCOMPLETE = 0x1; + + TRACE_MSG3(pcd->TAG, "EP0 OUT COMPLETE active: %p %x length: %d", urb, urb ? urb->flags : 0, length); + UNLESS (urb && (urb->flags & USBD_URB_OUT)) { + TRACE_MSG0(pcd->TAG, "EP0 OUT COMPLETE - ZLP received"); + return IRQ_HANDLED; + } + length = arc_read_rcv_buffer (pcd, endpoint); + pcd_rcv_complete_irq(endpoint, length, 0); + arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_IN, 0, 0); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/*! arc_epn_irq + */ +static irqreturn_t arc_epn_irq(struct pcd_instance *pcd) +{ + u32 endptcomplete; + /* N.B. need to loop until nothing left to do, endpoints can complete while + * working on other endpoints. + */ + while ((endptcomplete = (UOG_ENDPTCOMPLETE & 0xfffefffe))) { + + /* We have endptcomplete bits, immediately reset so that subsequent + * completions will be seen when we loop. Note that we mask ep0 status. + */ + UOG_ENDPTCOMPLETE = endptcomplete; + + /* Loop while non-zero - N.B. clz requires pre-test as it is undefined on zero */ + while (endptcomplete) { + int bit = 31 - __builtin_clz(endptcomplete); /* highest non-zero bit */ + int ep = bit & 0xf; + int dir = (bit > 15) ? ARC_DIR_IN : ARC_DIR_OUT; + struct usbd_endpoint_instance *endpoint = pcd->bus->endpoint_array + (2 * ep + dir); + struct arc_private_struct *privdata = endpoint->privdata; + struct ep_td_struct *cur_dtd = privdata->cur_dtd; + int errors = le32_to_cpu(cur_dtd->size_ioc_sts) & DTD_ERROR_MASK; + int remain_len; + TRACE_MSG6(pcd->TAG, "[%2d:%2d] mask: %08x endptcomplete: %08x %s %s", bit, ep, + (1 << bit), endptcomplete, (ARC_DIR_IN) ? "IN" : "OUT", ep ? "EPN" : "EP0"); + switch (dir) { + case ARC_DIR_IN: /* Transmitting */ + arc_dtd_releases (pcd, endpoint, ARC_DIR_IN); + if ((pcd_tx_complete_irq(endpoint, 0))) + arc_pcd_start_endpoint_in(pcd, endpoint); + break; + + case ARC_DIR_OUT: /* Receiving */ + remain_len = arc_read_rcv_buffer (pcd, endpoint); + arc_dtd_releases (pcd, endpoint, ARC_DIR_OUT); + if ( errors & DTD_STATUS_HALTED ) { + TRACE_MSG4(pcd->TAG, "[%2d:%2d] Current dtd is halted size_ioc_sts: %08x errors: %02x", + bit, ep, le32_to_cpu(cur_dtd->size_ioc_sts), errors); + cur_dtd->size_ioc_sts &= cpu_to_le32(errors); + consistent_sync(cur_dtd, sizeof(struct ep_td_struct), DMA_TO_DEVICE); + } + + if ((pcd_rcv_complete_irq(endpoint, remain_len, 0))) { + + UNLESS(pcd && pcd->bus && endpoint) { + printk(KERN_INFO"%s: NULL pcd: %p bus: %p endpoint: %p\n", + __FUNCTION__, pcd, pcd ? pcd->bus : NULL, endpoint); + break; + } + arc_pcd_start_endpoint_out(pcd, endpoint); + } + break; + } + endptcomplete &= ~(1 << bit); + } + } + return IRQ_HANDLED; +} + +/*! arc_pcd_isr + */ +static irqreturn_t arc_pcd_isr(struct otg_dev *otg_dev, void *data, u32 mask) +{ + u32 irq_src; + struct otg_instance *otg = otg_dev ? otg_dev->otg_instance : NULL; + struct pcd_instance *pcd = otg_dev ? otg_dev->pcd_instance : NULL; + struct usbd_bus_instance *bus = pcd->bus; + int timeout; + irqreturn_t status = IRQ_NONE; + + otg_led(LED2, 1); + otg->interrupts++; + if (TRACE_VERBOSE) { + TRACE_MSG0(pcd->TAG, "--"); + TRACE_MSG5(pcd->TAG, "UOG_USBINTR: %08x UOG_ENDPTSTAT: %08x " + "UOG_ENDPTSETUPSTAT: %04x, UOG_ENDPTCOMPLETE: %08x, UOG_PORTSC1: %08x", + UOG_USBINTR, UOG_ENDPTSTAT, UOG_ENDPTSETUPSTAT, UOG_ENDPTCOMPLETE, UOG_PORTSC1); + } + + /* verify controller is running */ + RETURN_IRQ_NONE_UNLESS(UOG_USBCMD & USB_CMD_RUN_STOP); + + /* get interrupt source and reset in USBSTS register */ + irq_src = UOG_USBSTS & UOG_USBINTR; + UOG_USBSTS &= irq_src; + /* USB */ + if (irq_src & USB_STS_INT) { + if ( (UOG_ENDPTSETUPSTAT & 0x1) || (UOG_ENDPTCOMPLETE & 0x1) || (UOG_ENDPTCOMPLETE & 0x10000)) + status = arc_ep0_irq (pcd); + if ( (UOG_ENDPTCOMPLETE & 0xfffefffe) ) + status = arc_epn_irq (pcd); + } + /* SOF */ + if (irq_src & USB_STS_SOF) { + // TRACE_MSG0(pcd->TAG, "SOF interrupt"); + status = IRQ_HANDLED; + } + /* port changed */ + if (irq_src & USB_STS_PORT_CHANGE) { + u32 speed; + TRACE_MSG0(pcd->TAG, "PORT CHANGE interrupt"); + if (!(UOG_PORTSC1 & PORTSCX_PORT_RESET)) { + speed = (UOG_PORTSC1 & PORTSCX_PORT_SPEED_MASK); + switch (speed) { + case PORTSCX_PORT_SPEED_HIGH: + TRACE_MSG0(pcd->TAG, "High Speed"); + bus->high_speed = TRUE; + break; + case PORTSCX_PORT_SPEED_FULL: + TRACE_MSG0(pcd->TAG, "Full Speed"); + bus->high_speed = FALSE; + break; + case PORTSCX_PORT_SPEED_LOW: + TRACE_MSG0(pcd->TAG, "Low Speed"); + break; + default: + TRACE_MSG0(pcd->TAG, "Unknown Speed"); + break; + } + status = IRQ_HANDLED; + } + if ((UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS)) { + TRACE_MSG0(pcd->TAG, "Cable is attached"); + otg_event(otg, VBUS_VLD | B_SESS_VLD, otg->pcd->TAG, "ARC VBUS VALID"); + status = IRQ_HANDLED; + } + if (!(UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS)) { + TRACE_MSG0(pcd->TAG, "Cable is detached"); + otg_event(otg, VBUS_VLD_ | B_SESS_VLD_ | A_SESS_VLD_, otg->pcd->TAG, "ARC VBUS INVALID"); + status = IRQ_HANDLED; + } + } + /* reset */ + if (irq_src & USB_STS_RESET) { + u32 temp; + TRACE_MSG0(pcd->TAG, "RESET interrupt"); + + /* Reset address, endpoint status and complete */ + UOG_DEVICEADDR &= ~USB_DEVICE_ADDRESS_MASK; + UOG_ENDPTSTAT = UOG_ENDPTSTAT; + UOG_ENDPTCOMPLETE = UOG_ENDPTCOMPLETE; + + timeout = 10000000; // XXX + /* Wait until all endptprime bits cleared */ + while (UOG_ENDPTPRIME && --timeout) { continue; } + if (timeout == 0) printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__); + + UOG_ENDPTFLUSH = 0xFFFFFFFF; + + if (UOG_PORTSC1 & PORTSCX_PORT_RESET) { + TRACE_MSG0(pcd->TAG, "Bus reset"); + } + else { + TRACE_MSG0(pcd->TAG, "Controller reset"); + arc_udc_run (); + } + pcd_bus_event_handler_irq (pcd->bus, DEVICE_RESET, 0); + pcd_bus_event_handler_irq (pcd->bus, DEVICE_ADDRESS_ASSIGNED, 0); + status = IRQ_HANDLED; + } + /* SUSPEND / RESUME */ + if (irq_src & USB_STS_SUSPEND) { + if (UOG_PORTSC1 & PORTSCX_PORT_SUSPEND) { + TRACE_MSG0(pcd->TAG, "SUSPEND interrupt"); + pcd_check_device_feature(pcd->bus, USB_DEVICE_REMOTE_WAKEUP, 1); + pcd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_INACTIVE, 0); + } + else { + TRACE_MSG0(pcd->TAG, "RESUME interrupt"); + pcd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_ACTIVITY, 0); + pcd_check_device_feature(pcd->bus, USB_DEVICE_REMOTE_WAKEUP, 0); + } + status = IRQ_HANDLED; + } + if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { + TRACE_MSG0(pcd->TAG, "Error in interrupt"); + status = IRQ_HANDLED; + } + if (TRACE_VERBOSE) TRACE_MSG6(pcd->TAG, "UOG_USBINTR: %08x UOG_ENDPTSTAT: %08x " + "UOG_ENDPTSETUPSTAT: %04x, UOG_ENDPTCOMPLETE: %08x, UOG_PORTSC1: %08x %s", + UOG_USBINTR, UOG_ENDPTSTAT, UOG_ENDPTSETUPSTAT, UOG_ENDPTCOMPLETE, UOG_PORTSC1, + (status == IRQ_HANDLED) ? "IRQ_HANDLED" : "IRQ_NONE"); + + otg_led(LED2, 0); + return status; +} + +/*! default initialization + */ +int pcd_mod_init (struct otg_instance *otg); +void pcd_mod_exit (struct otg_instance *otg); + +/*! arc_pcd_remove() - called to remove hardware + * @param otg_dev - otg device + */ +static void +arc_pcd_remove(struct otg_dev *otg_dev) +{ + struct otg_instance *otg = otg_dev->otg_instance; + struct device *device = otg_dev_get_drvdata(otg_dev); + struct platform_device *pdev = to_platform_device(device); + struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data; + + TRACE_MSG0(otg->pcd->TAG, "--"); + arc_udc_release (); + udc_controller = NULL; + pdata->platform_uninit(pdata); +//printk(KERN_INFO"%s: jumpin116 start--------\n ", __FUNCTION__); + pcd_mod_exit(otg); +//printk(KERN_INFO"%s: jumpin116 end--------- \n", __FUNCTION__); +} + +/*! arc_pcd_probe() - called to probe hardware + * @param otg_dev - otg device + * + * This function should do minimal, one time only hardware recognition, + * resource reservation and minimal setup. Typically to get to known + * disabled state. It should not start the hardware. + * + */ +static int arc_pcd_probe(struct otg_dev *otg_dev) +{ + struct device *device = otg_dev_get_drvdata(otg_dev); + struct otg_instance *otg = otg_dev->otg_instance; + struct platform_device *pdev = to_platform_device(device); + struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data; + + usbd_pcd_ops.max_endpoints = UDC_MAX_ENDPOINTS; + + RETURN_ENODEV_IF(strcmp(pdev->name, "arc_udc")); + UNLESS(pdata && pdata->platform_init) { + printk(KERN_INFO"%s: NO TRANSCEIVER CONFIGURED\n", __FUNCTION__); + return -ENODEV; + } + RETURN_ZERO_IF (pdata->platform_init(pdev)); + + //fsl_platform_set_vbus_power(pdata, 0); + RETURN_ENODEV_IF(pcd_mod_init(otg)); + udc_controller = (struct arcotg_udc *) arc_udc_init (otg_dev); + return 0; +} + +/*! arc_pcd_suspend + */ +static void arc_pcd_suspend(struct otg_dev *otg_dev, u32 state) +{ + /* NOTHING */ +} + +/*! arc_pcd_resume + */ +static void arc_pcd_resume(struct otg_dev *otg_dev) +{ + /* NOTHING */ +} + +/* ********************************************************************************************* */ +static struct otg_dev_driver arc_pcd_driver = { + .name = "arc_udc", + .id = OTG_DRIVER_PCD, + .probe = arc_pcd_probe, + .remove = arc_pcd_remove, + .suspend = arc_pcd_suspend, + .resume = arc_pcd_resume, + .irqs = (1 << 1), /* use irq at location #0 in udc_resources */ + .isr = arc_pcd_isr, + .ops = &pcd_ops, +}; + +/*! arc_pcd_module_init() - module init + */ +int arc_pcd_module_init (struct otg_device_driver *otg_device_driver) +{ + return otg_dev_register_driver(otg_device_driver, &arc_pcd_driver); +} + +/*! arc_pcd_module_exit() - module exit + */ +void arc_pcd_module_exit (struct otg_device_driver *otg_device_driver) +{ + otg_dev_unregister_driver (otg_device_driver, &arc_pcd_driver); +} |