Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / u-boot / drivers / usb / musb / musb_udc.c
diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_udc.c b/qemu/roms/u-boot/drivers/usb/musb/musb_udc.c
new file mode 100644 (file)
index 0000000..87640f4
--- /dev/null
@@ -0,0 +1,959 @@
+/*
+ * Copyright (c) 2009 Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * This file is a rewrite of the usb device part of
+ * repository git.omapzoom.org/repo/u-boot.git, branch master,
+ * file cpu/omap3/fastboot.c
+ *
+ * This is the unique part of its copyright :
+ *
+ * -------------------------------------------------------------------------
+ *
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * -------------------------------------------------------------------------
+ *
+ * The details of connecting the device to the uboot usb device subsystem
+ * came from the old omap3 repository www.sakoman.net/u-boot-omap3.git,
+ * branch omap3-dev-usb, file drivers/usb/usbdcore_musb.c
+ *
+ * This is the unique part of its copyright :
+ *
+ * -------------------------------------------------------------------------
+ *
+ * (C) Copyright 2008 Texas Instruments Incorporated.
+ *
+ * Based on
+ * u-boot OMAP1510 USB drivers (drivers/usbdcore_omap1510.c)
+ * twl4030 init based on linux (drivers/i2c/chips/twl4030_usb.c)
+ *
+ * Author: Diego Dompe (diego.dompe@ridgerun.com)
+ *         Atin Malaviya (atin.malaviya@gmail.com)
+ *
+ * -------------------------------------------------------------------------
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <usbdevice.h>
+#include <usb/udc.h>
+#include "../gadget/ep0.h"
+#include "musb_core.h"
+#if defined(CONFIG_USB_OMAP3)
+#include "omap3.h"
+#elif defined(CONFIG_USB_AM35X)
+#include "am35x.h"
+#elif defined(CONFIG_USB_DAVINCI)
+#include "davinci.h"
+#endif
+
+/* Define MUSB_DEBUG for debugging */
+/* #define MUSB_DEBUG */
+#include "musb_debug.h"
+
+#define MAX_ENDPOINT 15
+
+#define GET_ENDPOINT(dev,ep)                                           \
+(((struct usb_device_instance *)(dev))->bus->endpoint_array + ep)
+
+#define SET_EP0_STATE(s)                                               \
+do {                                                                   \
+       if ((0 <= (s)) && (SET_ADDRESS >= (s))) {                       \
+               if ((s) != ep0_state) {                                 \
+                       if ((debug_setup) && (debug_level > 1))         \
+                               serial_printf("INFO : Changing state "  \
+                                             "from %s to %s in %s at " \
+                                             "line %d\n",              \
+                                             ep0_state_strings[ep0_state],\
+                                             ep0_state_strings[s],     \
+                                             __PRETTY_FUNCTION__,      \
+                                             __LINE__);                \
+                       ep0_state = s;                                  \
+               }                                                       \
+       } else {                                                        \
+               if (debug_level > 0)                                    \
+                       serial_printf("Error at %s %d with setting "    \
+                                     "state %d is invalid\n",          \
+                                     __PRETTY_FUNCTION__, __LINE__, s); \
+       }                                                               \
+} while (0)
+
+/* static implies these initialized to 0 or NULL */
+static int debug_setup;
+static int debug_level;
+static struct musb_epinfo epinfo[MAX_ENDPOINT * 2];
+static enum ep0_state_enum {
+       IDLE = 0,
+       TX,
+       RX,
+       SET_ADDRESS
+} ep0_state = IDLE;
+static char *ep0_state_strings[4] = {
+       "IDLE",
+       "TX",
+       "RX",
+       "SET_ADDRESS",
+};
+
+static struct urb *ep0_urb;
+struct usb_endpoint_instance *ep0_endpoint;
+static struct usb_device_instance *udc_device;
+static int enabled;
+
+#ifdef MUSB_DEBUG
+static void musb_db_regs(void)
+{
+       u8 b;
+       u16 w;
+
+       b = readb(&musbr->faddr);
+       serial_printf("\tfaddr   0x%2.2x\n", b);
+
+       b = readb(&musbr->power);
+       musb_print_pwr(b);
+
+       w = readw(&musbr->ep[0].ep0.csr0);
+       musb_print_csr0(w);
+
+       b = readb(&musbr->devctl);
+       musb_print_devctl(b);
+
+       b = readb(&musbr->ep[0].ep0.configdata);
+       musb_print_config(b);
+
+       w = readw(&musbr->frame);
+       serial_printf("\tframe   0x%4.4x\n", w);
+
+       b = readb(&musbr->index);
+       serial_printf("\tindex   0x%2.2x\n", b);
+
+       w = readw(&musbr->ep[1].epN.rxmaxp);
+       musb_print_rxmaxp(w);
+
+       w = readw(&musbr->ep[1].epN.rxcsr);
+       musb_print_rxcsr(w);
+
+       w = readw(&musbr->ep[1].epN.txmaxp);
+       musb_print_txmaxp(w);
+
+       w = readw(&musbr->ep[1].epN.txcsr);
+       musb_print_txcsr(w);
+}
+#else
+#define musb_db_regs()
+#endif /* DEBUG_MUSB */
+
+static void musb_peri_softconnect(void)
+{
+       u8 power, devctl;
+
+       /* Power off MUSB */
+       power = readb(&musbr->power);
+       power &= ~MUSB_POWER_SOFTCONN;
+       writeb(power, &musbr->power);
+
+       /* Read intr to clear */
+       readb(&musbr->intrusb);
+       readw(&musbr->intrrx);
+       readw(&musbr->intrtx);
+
+       udelay(1000 * 1000); /* 1 sec */
+
+       /* Power on MUSB */
+       power = readb(&musbr->power);
+       power |= MUSB_POWER_SOFTCONN;
+       /*
+        * The usb device interface is usb 1.1
+        * Disable 2.0 high speed by clearring the hsenable bit.
+        */
+       power &= ~MUSB_POWER_HSENAB;
+       writeb(power, &musbr->power);
+
+       /* Check if device is in b-peripheral mode */
+       devctl = readb(&musbr->devctl);
+       if (!(devctl & MUSB_DEVCTL_BDEVICE) ||
+           (devctl & MUSB_DEVCTL_HM)) {
+               serial_printf("ERROR : Unsupport USB mode\n");
+               serial_printf("Check that mini-B USB cable is attached "
+                             "to the device\n");
+       }
+
+       if (debug_setup && (debug_level > 1))
+               musb_db_regs();
+}
+
+static void musb_peri_reset(void)
+{
+       if ((debug_setup) && (debug_level > 1))
+               serial_printf("INFO : %s reset\n", __PRETTY_FUNCTION__);
+
+       if (ep0_endpoint)
+               ep0_endpoint->endpoint_address = 0xff;
+
+       /* Sync sw and hw addresses */
+       writeb(udc_device->address, &musbr->faddr);
+
+       SET_EP0_STATE(IDLE);
+}
+
+static void musb_peri_resume(void)
+{
+       /* noop */
+}
+
+static void musb_peri_ep0_stall(void)
+{
+       u16 csr0;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+       csr0 |= MUSB_CSR0_P_SENDSTALL;
+       writew(csr0, &musbr->ep[0].ep0.csr0);
+       if ((debug_setup) && (debug_level > 1))
+               serial_printf("INFO : %s stall\n", __PRETTY_FUNCTION__);
+}
+
+static void musb_peri_ep0_ack_req(void)
+{
+       u16 csr0;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+       csr0 |= MUSB_CSR0_P_SVDRXPKTRDY;
+       writew(csr0, &musbr->ep[0].ep0.csr0);
+}
+
+static void musb_ep0_tx_ready(void)
+{
+       u16 csr0;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+       csr0 |= MUSB_CSR0_TXPKTRDY;
+       writew(csr0, &musbr->ep[0].ep0.csr0);
+}
+
+static void musb_ep0_tx_ready_and_last(void)
+{
+       u16 csr0;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+       csr0 |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_P_DATAEND);
+       writew(csr0, &musbr->ep[0].ep0.csr0);
+}
+
+static void musb_peri_ep0_last(void)
+{
+       u16 csr0;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+       csr0 |= MUSB_CSR0_P_DATAEND;
+       writew(csr0, &musbr->ep[0].ep0.csr0);
+}
+
+static void musb_peri_ep0_set_address(void)
+{
+       u8 faddr;
+       writeb(udc_device->address, &musbr->faddr);
+
+       /* Verify */
+       faddr = readb(&musbr->faddr);
+       if (udc_device->address == faddr) {
+               SET_EP0_STATE(IDLE);
+               usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
+               if ((debug_setup) && (debug_level > 1))
+                       serial_printf("INFO : %s Address set to %d\n",
+                                     __PRETTY_FUNCTION__, udc_device->address);
+       } else {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s Address missmatch "
+                                     "sw %d vs hw %d\n",
+                                     __PRETTY_FUNCTION__,
+                                     udc_device->address, faddr);
+       }
+}
+
+static void musb_peri_rx_ack(unsigned int ep)
+{
+       u16 peri_rxcsr;
+
+       peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
+       peri_rxcsr &= ~MUSB_RXCSR_RXPKTRDY;
+       writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
+}
+
+static void musb_peri_tx_ready(unsigned int ep)
+{
+       u16 peri_txcsr;
+
+       peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
+       peri_txcsr |= MUSB_TXCSR_TXPKTRDY;
+       writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
+}
+
+static void musb_peri_ep0_zero_data_request(int err)
+{
+       musb_peri_ep0_ack_req();
+
+       if (err) {
+               musb_peri_ep0_stall();
+               SET_EP0_STATE(IDLE);
+       } else {
+
+               musb_peri_ep0_last();
+
+               /* USBD state */
+               switch (ep0_urb->device_request.bRequest) {
+               case USB_REQ_SET_ADDRESS:
+                       if ((debug_setup) && (debug_level > 1))
+                               serial_printf("INFO : %s received set "
+                                             "address\n", __PRETTY_FUNCTION__);
+                       break;
+
+               case USB_REQ_SET_CONFIGURATION:
+                       if ((debug_setup) && (debug_level > 1))
+                               serial_printf("INFO : %s Configured\n",
+                                             __PRETTY_FUNCTION__);
+                       usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
+                       break;
+               }
+
+               /* EP0 state */
+               if (USB_REQ_SET_ADDRESS == ep0_urb->device_request.bRequest) {
+                       SET_EP0_STATE(SET_ADDRESS);
+               } else {
+                       SET_EP0_STATE(IDLE);
+               }
+       }
+}
+
+static void musb_peri_ep0_rx_data_request(void)
+{
+       /*
+        * This is the completion of the data OUT / RX
+        *
+        * Host is sending data to ep0 that is not
+        * part of setup.  This comes from the cdc_recv_setup
+        * op that is device specific.
+        *
+        */
+       musb_peri_ep0_ack_req();
+
+       ep0_endpoint->rcv_urb = ep0_urb;
+       ep0_urb->actual_length = 0;
+       SET_EP0_STATE(RX);
+}
+
+static void musb_peri_ep0_tx_data_request(int err)
+{
+       if (err) {
+               musb_peri_ep0_stall();
+               SET_EP0_STATE(IDLE);
+       } else {
+               musb_peri_ep0_ack_req();
+
+               ep0_endpoint->tx_urb = ep0_urb;
+               ep0_endpoint->sent = 0;
+               SET_EP0_STATE(TX);
+       }
+}
+
+static void musb_peri_ep0_idle(void)
+{
+       u16 count0;
+       int err;
+       u16 csr0;
+
+       /*
+        * Verify addresses
+        * A lot of confusion can be caused if the address
+        * in software, udc layer, does not agree with the
+        * hardware.  Since the setting of the hardware address
+        * must be set after the set address request, the
+        * usb state machine is out of sync for a few frame.
+        * It is a good idea to run this check when changes
+        * are made to the state machine.
+        */
+       if ((debug_level > 0) &&
+           (ep0_state != SET_ADDRESS)) {
+               u8 faddr;
+
+               faddr = readb(&musbr->faddr);
+               if (udc_device->address != faddr) {
+                       serial_printf("ERROR : %s addresses do not"
+                                     "match sw %d vs hw %d\n",
+                                     __PRETTY_FUNCTION__,
+                                     udc_device->address, faddr);
+                       udelay(1000 * 1000);
+                       hang();
+               }
+       }
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+
+       if (!(MUSB_CSR0_RXPKTRDY & csr0))
+               goto end;
+
+       count0 = readw(&musbr->ep[0].ep0.count0);
+       if (count0 == 0)
+               goto end;
+
+       if (count0 != 8) {
+               if ((debug_setup) && (debug_level > 1))
+                       serial_printf("WARN : %s SETUP incorrect size %d\n",
+                                     __PRETTY_FUNCTION__, count0);
+               musb_peri_ep0_stall();
+               goto end;
+       }
+
+       read_fifo(0, count0, &ep0_urb->device_request);
+
+       if (debug_level > 2)
+               print_usb_device_request(&ep0_urb->device_request);
+
+       if (ep0_urb->device_request.wLength == 0) {
+               err = ep0_recv_setup(ep0_urb);
+
+               /* Zero data request */
+               musb_peri_ep0_zero_data_request(err);
+       } else {
+               /* Is data coming or going ? */
+               u8 reqType = ep0_urb->device_request.bmRequestType;
+
+               if (USB_REQ_DEVICE2HOST == (reqType & USB_REQ_DIRECTION_MASK)) {
+                       err = ep0_recv_setup(ep0_urb);
+                       /* Device to host */
+                       musb_peri_ep0_tx_data_request(err);
+               } else {
+                       /*
+                        * Host to device
+                        *
+                        * The RX routine will call ep0_recv_setup
+                        * when the data packet has arrived.
+                        */
+                       musb_peri_ep0_rx_data_request();
+               }
+       }
+
+end:
+       return;
+}
+
+static void musb_peri_ep0_rx(void)
+{
+       /*
+        * This is the completion of the data OUT / RX
+        *
+        * Host is sending data to ep0 that is not
+        * part of setup.  This comes from the cdc_recv_setup
+        * op that is device specific.
+        *
+        * Pass the data back to driver ep0_recv_setup which
+        * should give the cdc_recv_setup the chance to handle
+        * the rx
+        */
+       u16 csr0;
+       u16 count0;
+
+       if (debug_level > 3) {
+               if (0 != ep0_urb->actual_length) {
+                       serial_printf("%s finished ? %d of %d\n",
+                                     __PRETTY_FUNCTION__,
+                                     ep0_urb->actual_length,
+                                     ep0_urb->device_request.wLength);
+               }
+       }
+
+       if (ep0_urb->device_request.wLength == ep0_urb->actual_length) {
+               musb_peri_ep0_last();
+               SET_EP0_STATE(IDLE);
+               ep0_recv_setup(ep0_urb);
+               return;
+       }
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+       if (!(MUSB_CSR0_RXPKTRDY & csr0))
+               return;
+
+       count0 = readw(&musbr->ep[0].ep0.count0);
+
+       if (count0) {
+               struct usb_endpoint_instance *endpoint;
+               u32 length;
+               u8 *data;
+
+               endpoint = ep0_endpoint;
+               if (endpoint && endpoint->rcv_urb) {
+                       struct urb *urb = endpoint->rcv_urb;
+                       unsigned int remaining_space = urb->buffer_length -
+                               urb->actual_length;
+
+                       if (remaining_space) {
+                               int urb_bad = 0; /* urb is good */
+
+                               if (count0 > remaining_space)
+                                       length = remaining_space;
+                               else
+                                       length = count0;
+
+                               data = (u8 *) urb->buffer_data;
+                               data += urb->actual_length;
+
+                               /* The common musb fifo reader */
+                               read_fifo(0, length, data);
+
+                               musb_peri_ep0_ack_req();
+
+                               /*
+                                * urb's actual_length is updated in
+                                * usbd_rcv_complete
+                                */
+                               usbd_rcv_complete(endpoint, length, urb_bad);
+
+                       } else {
+                               if (debug_level > 0)
+                                       serial_printf("ERROR : %s no space in "
+                                                     "rcv buffer\n",
+                                                     __PRETTY_FUNCTION__);
+                       }
+               } else {
+                       if (debug_level > 0)
+                               serial_printf("ERROR : %s problem with "
+                                             "endpoint\n",
+                                             __PRETTY_FUNCTION__);
+               }
+       } else {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s with nothing to do\n",
+                                     __PRETTY_FUNCTION__);
+       }
+}
+
+static void musb_peri_ep0_tx(void)
+{
+       u16 csr0;
+       int transfer_size = 0;
+       unsigned int p, pm;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+
+       /* Check for pending tx */
+       if (csr0 & MUSB_CSR0_TXPKTRDY)
+               goto end;
+
+       /* Check if this is the last packet sent */
+       if (ep0_endpoint->sent >= ep0_urb->actual_length) {
+               SET_EP0_STATE(IDLE);
+               goto end;
+       }
+
+       transfer_size = ep0_urb->actual_length - ep0_endpoint->sent;
+       /* Is the transfer size negative ? */
+       if (transfer_size <= 0) {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s problem with the"
+                                     " transfer size %d\n",
+                                     __PRETTY_FUNCTION__,
+                                     transfer_size);
+               SET_EP0_STATE(IDLE);
+               goto end;
+       }
+
+       /* Truncate large transfers to the fifo size */
+       if (transfer_size > ep0_endpoint->tx_packetSize)
+               transfer_size = ep0_endpoint->tx_packetSize;
+
+       write_fifo(0, transfer_size, &ep0_urb->buffer[ep0_endpoint->sent]);
+       ep0_endpoint->sent += transfer_size;
+
+       /* Done or more to send ? */
+       if (ep0_endpoint->sent >= ep0_urb->actual_length)
+               musb_ep0_tx_ready_and_last();
+       else
+               musb_ep0_tx_ready();
+
+       /* Wait a bit */
+       pm = 10;
+       for (p = 0; p < pm; p++) {
+               csr0 = readw(&musbr->ep[0].ep0.csr0);
+               if (!(csr0 & MUSB_CSR0_TXPKTRDY))
+                       break;
+
+               /* Double the delay. */
+               udelay(1 << pm);
+       }
+
+       if ((ep0_endpoint->sent >= ep0_urb->actual_length) && (p < pm))
+               SET_EP0_STATE(IDLE);
+
+end:
+       return;
+}
+
+static void musb_peri_ep0(void)
+{
+       u16 csr0;
+
+       if (SET_ADDRESS == ep0_state)
+               return;
+
+       csr0 = readw(&musbr->ep[0].ep0.csr0);
+
+       /* Error conditions */
+       if (MUSB_CSR0_P_SENTSTALL & csr0) {
+               csr0 &= ~MUSB_CSR0_P_SENTSTALL;
+               writew(csr0, &musbr->ep[0].ep0.csr0);
+               SET_EP0_STATE(IDLE);
+       }
+       if (MUSB_CSR0_P_SETUPEND & csr0) {
+               csr0 |= MUSB_CSR0_P_SVDSETUPEND;
+               writew(csr0, &musbr->ep[0].ep0.csr0);
+               SET_EP0_STATE(IDLE);
+               if ((debug_setup) && (debug_level > 1))
+                       serial_printf("WARN: %s SETUPEND\n",
+                                     __PRETTY_FUNCTION__);
+       }
+
+       /* Normal states */
+       if (IDLE == ep0_state)
+               musb_peri_ep0_idle();
+
+       if (TX == ep0_state)
+               musb_peri_ep0_tx();
+
+       if (RX == ep0_state)
+               musb_peri_ep0_rx();
+}
+
+static void musb_peri_rx_ep(unsigned int ep)
+{
+       u16 peri_rxcount;
+       u8 peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
+
+       if (!(peri_rxcsr & MUSB_RXCSR_RXPKTRDY)) {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s %d without MUSB_RXCSR_RXPKTRDY set\n",
+                                     __PRETTY_FUNCTION__, ep);
+               return;
+       }
+
+       peri_rxcount = readw(&musbr->ep[ep].epN.rxcount);
+       if (peri_rxcount) {
+               struct usb_endpoint_instance *endpoint;
+               u32 length;
+               u8 *data;
+
+               endpoint = GET_ENDPOINT(udc_device, ep);
+               if (endpoint && endpoint->rcv_urb) {
+                       struct urb *urb = endpoint->rcv_urb;
+                       unsigned int remaining_space = urb->buffer_length -
+                               urb->actual_length;
+
+                       if (remaining_space) {
+                               int urb_bad = 0; /* urb is good */
+
+                               if (peri_rxcount > remaining_space)
+                                       length = remaining_space;
+                               else
+                                       length = peri_rxcount;
+
+                               data = (u8 *) urb->buffer_data;
+                               data += urb->actual_length;
+
+                               /* The common musb fifo reader */
+                               read_fifo(ep, length, data);
+
+                               musb_peri_rx_ack(ep);
+
+                               /*
+                                * urb's actual_length is updated in
+                                * usbd_rcv_complete
+                                */
+                               usbd_rcv_complete(endpoint, length, urb_bad);
+
+                       } else {
+                               if (debug_level > 0)
+                                       serial_printf("ERROR : %s %d no space "
+                                                     "in rcv buffer\n",
+                                                     __PRETTY_FUNCTION__, ep);
+                       }
+               } else {
+                       if (debug_level > 0)
+                               serial_printf("ERROR : %s %d problem with "
+                                             "endpoint\n",
+                                             __PRETTY_FUNCTION__, ep);
+               }
+
+       } else {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s %d with nothing to do\n",
+                                     __PRETTY_FUNCTION__, ep);
+       }
+}
+
+static void musb_peri_rx(u16 intr)
+{
+       unsigned int ep;
+
+       /* Check for EP0 */
+       if (0x01 & intr)
+               musb_peri_ep0();
+
+       for (ep = 1; ep < 16; ep++) {
+               if ((1 << ep) & intr)
+                       musb_peri_rx_ep(ep);
+       }
+}
+
+static void musb_peri_tx(u16 intr)
+{
+       /* Check for EP0 */
+       if (0x01 & intr)
+               musb_peri_ep0_tx();
+
+       /*
+        * Use this in the future when handling epN tx
+        *
+        * u8 ep;
+        *
+        * for (ep = 1; ep < 16; ep++) {
+        *      if ((1 << ep) & intr) {
+        *              / * handle tx for this endpoint * /
+        *      }
+        * }
+        */
+}
+
+void udc_irq(void)
+{
+       /* This is a high freq called function */
+       if (enabled) {
+               u8 intrusb;
+
+               intrusb = readb(&musbr->intrusb);
+
+               /*
+                * See drivers/usb/gadget/mpc8xx_udc.c for
+                * state diagram going from detached through
+                * configuration.
+                */
+               if (MUSB_INTR_RESUME & intrusb) {
+                       usbd_device_event_irq(udc_device,
+                                             DEVICE_BUS_ACTIVITY, 0);
+                       musb_peri_resume();
+               }
+
+               musb_peri_ep0();
+
+               if (MUSB_INTR_RESET & intrusb) {
+                       usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+                       musb_peri_reset();
+               }
+
+               if (MUSB_INTR_DISCONNECT & intrusb) {
+                       /* cable unplugged from hub/host */
+                       usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+                       musb_peri_reset();
+                       usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0);
+               }
+
+               if (MUSB_INTR_SOF & intrusb) {
+                       usbd_device_event_irq(udc_device,
+                                             DEVICE_BUS_ACTIVITY, 0);
+                       musb_peri_resume();
+               }
+
+               if (MUSB_INTR_SUSPEND & intrusb) {
+                       usbd_device_event_irq(udc_device,
+                                             DEVICE_BUS_INACTIVE, 0);
+               }
+
+               if (ep0_state != SET_ADDRESS) {
+                       u16 intrrx, intrtx;
+
+                       intrrx = readw(&musbr->intrrx);
+                       intrtx = readw(&musbr->intrtx);
+
+                       if (intrrx)
+                               musb_peri_rx(intrrx);
+
+                       if (intrtx)
+                               musb_peri_tx(intrtx);
+               } else {
+                       if (MUSB_INTR_SOF & intrusb) {
+                               u8 faddr;
+                               faddr = readb(&musbr->faddr);
+                               /*
+                                * Setting of the address can fail.
+                                * Normally it succeeds the second time.
+                                */
+                               if (udc_device->address != faddr)
+                                       musb_peri_ep0_set_address();
+                       }
+               }
+       }
+}
+
+void udc_set_nak(int ep_num)
+{
+       /* noop */
+}
+
+void udc_unset_nak(int ep_num)
+{
+       /* noop */
+}
+
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
+{
+       int ret = 0;
+
+       /* Transmit only if the hardware is available */
+       if (endpoint->tx_urb && endpoint->state == 0) {
+               unsigned int ep = endpoint->endpoint_address &
+                       USB_ENDPOINT_NUMBER_MASK;
+
+               u16 peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
+
+               /* Error conditions */
+               if (peri_txcsr & MUSB_TXCSR_P_UNDERRUN) {
+                       peri_txcsr &= ~MUSB_TXCSR_P_UNDERRUN;
+                       writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
+               }
+
+               if (debug_level > 1)
+                       musb_print_txcsr(peri_txcsr);
+
+               /* Check if a packet is waiting to be sent */
+               if (!(peri_txcsr & MUSB_TXCSR_TXPKTRDY)) {
+                       u32 length;
+                       u8 *data;
+                       struct urb *urb = endpoint->tx_urb;
+                       unsigned int remaining_packet = urb->actual_length -
+                               endpoint->sent;
+
+                       if (endpoint->tx_packetSize < remaining_packet)
+                               length = endpoint->tx_packetSize;
+                       else
+                               length = remaining_packet;
+
+                       data = (u8 *) urb->buffer;
+                       data += endpoint->sent;
+
+                       /* common musb fifo function */
+                       write_fifo(ep, length, data);
+
+                       musb_peri_tx_ready(ep);
+
+                       endpoint->last = length;
+                       /* usbd_tx_complete will take care of updating 'sent' */
+                       usbd_tx_complete(endpoint);
+               }
+       } else {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s Problem with urb %p "
+                                     "or ep state %d\n",
+                                     __PRETTY_FUNCTION__,
+                                     endpoint->tx_urb, endpoint->state);
+       }
+
+       return ret;
+}
+
+void udc_setup_ep(struct usb_device_instance *device, unsigned int id,
+                 struct usb_endpoint_instance *endpoint)
+{
+       if (0 == id) {
+               /* EP0 */
+               ep0_endpoint = endpoint;
+               ep0_endpoint->endpoint_address = 0xff;
+               ep0_urb = usbd_alloc_urb(device, endpoint);
+       } else if (MAX_ENDPOINT >= id) {
+               int ep_addr;
+
+               /* Check the direction */
+               ep_addr = endpoint->endpoint_address;
+               if (USB_DIR_IN == (ep_addr & USB_ENDPOINT_DIR_MASK)) {
+                       /* IN */
+                       epinfo[(id * 2) + 1].epsize = endpoint->tx_packetSize;
+               } else {
+                       /* OUT */
+                       epinfo[id * 2].epsize = endpoint->rcv_packetSize;
+               }
+
+               musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo));
+       } else {
+               if (debug_level > 0)
+                       serial_printf("ERROR : %s endpoint request %d "
+                                     "exceeds maximum %d\n",
+                                     __PRETTY_FUNCTION__, id, MAX_ENDPOINT);
+       }
+}
+
+void udc_connect(void)
+{
+       /* noop */
+}
+
+void udc_disconnect(void)
+{
+       /* noop */
+}
+
+void udc_enable(struct usb_device_instance *device)
+{
+       /* Save the device structure pointer */
+       udc_device = device;
+
+       enabled = 1;
+}
+
+void udc_disable(void)
+{
+       enabled = 0;
+}
+
+void udc_startup_events(struct usb_device_instance *device)
+{
+       /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
+       usbd_device_event_irq(device, DEVICE_INIT, 0);
+
+       /*
+        * The DEVICE_CREATE event puts the USB device in the state
+        * STATE_ATTACHED.
+        */
+       usbd_device_event_irq(device, DEVICE_CREATE, 0);
+
+       /* Resets the address to 0 */
+       usbd_device_event_irq(device, DEVICE_RESET, 0);
+
+       udc_enable(device);
+}
+
+int udc_init(void)
+{
+       int ret;
+       int ep_loop;
+
+       ret = musb_platform_init();
+       if (ret < 0)
+               goto end;
+
+       /* Configure all the endpoint FIFO's and start usb controller */
+       musbr = musb_cfg.regs;
+
+       /* Initialize the endpoints */
+       for (ep_loop = 0; ep_loop < MAX_ENDPOINT * 2; ep_loop++) {
+               epinfo[ep_loop].epnum = (ep_loop / 2) + 1;
+               epinfo[ep_loop].epdir = ep_loop % 2; /* OUT, IN */
+               epinfo[ep_loop].epsize = 0;
+       }
+
+       musb_peri_softconnect();
+
+       ret = 0;
+end:
+
+       return ret;
+}