@@ -35,6 +35,8 @@
#include <usb/pxa27x_udc.h>
#elif defined(CONFIG_DW_UDC)
#include <usb/designware_udc.h>
+#elif defined(CONFIG_DW_OTG)
+#include <usb/designware_otg.h>
#endif
#include <version.h>
@@ -40,6 +40,7 @@ ifdef CONFIG_USB_DEVICE
COBJS-y += core.o
COBJS-y += ep0.o
COBJS-$(CONFIG_DW_UDC) += designware_udc.o
+COBJS-$(CONFIG_DW_OTG) += designware_otg.o
COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o
COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o
COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o
new file mode 100644
@@ -0,0 +1,990 @@
+/*
+ * Based on drivers/usb/gadget/designware_otg.c
+ * Synopsys DW OTG Device bus interface driver
+ *
+ * (C) Copyright 2011
+ * Pratyush Anand, ST Micoelectronics, pratyush.anand@st.com.
+ *
+ * (C) Copyright 2012
+ * Amit Virdi, ST Micoelectronics, amit.virdi@st.com.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <usbdevice.h>
+#include <watchdog.h>
+#include "ep0.h"
+#include <usb/designware_otg.h>
+#include <asm/arch/hardware.h>
+
+#define UDC_INIT_MDELAY 80 /* Device settle delay */
+
+static struct urb *ep0_urb;
+static struct usb_device_instance *udc_device;
+
+static struct device_if device_if_mem;
+static struct device_if *dev_if = &device_if_mem;
+
+#if defined(CONFIG_USBD_HS)
+#define CONFIG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_HS_PACKET_SIZE
+#endif
+
+void udc_set_nak(int epid)
+{
+ setbits_le32(&dev_if->out_ep_regs[epid]->doepctl, SNAK);
+ setbits_le32(&dev_if->in_ep_regs[epid]->diepctl, SNAK);
+}
+
+void udc_unset_nak(int epid)
+{
+ setbits_le32(&dev_if->out_ep_regs[epid]->doepctl, CNAK);
+ setbits_le32(&dev_if->in_ep_regs[epid]->diepctl, CNAK);
+}
+
+static void udc_set_stall(int epid, int dir)
+{
+ if (dir)
+ setbits_le32(&dev_if->in_ep_regs[epid]->diepctl, SSTALL);
+ else
+ setbits_le32(&dev_if->out_ep_regs[epid]->doepctl, SSTALL);
+}
+
+/*
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets. It is normally called when the "Enumeration
+ * Done" interrupt occurs.
+ */
+static void dwc_otg_ep0_activate(void)
+{
+ struct device_in_ep_regs *in_ep_regs = dev_if->in_ep_regs[0];
+ struct device_out_ep_regs *out_ep_regs = dev_if->out_ep_regs[0];
+
+ /* Read the Device Status and Endpoint 0 Control registers */
+ clrsetbits_le32(&in_ep_regs->diepctl, MPSMSK0, DWC_DEP0CTL_MPS_64);
+
+ /* Enable OUT EP for receive */
+ setbits_le32(&out_ep_regs->doepctl, EPENA);
+}
+
+static struct usb_endpoint_instance *dw_find_ep(int ep)
+{
+ int i;
+
+ for (i = 0; i < udc_device->bus->max_endpoints; i++) {
+ if ((udc_device->bus->endpoint_array[i].endpoint_address &
+ USB_ENDPOINT_NUMBER_MASK) == ep)
+ return &udc_device->bus->endpoint_array[i];
+ }
+ return NULL;
+}
+
+/*
+ * This function reads a packet from the Rx FIFO into the destination buffer.
+ * To read SETUP data use dwc_otg_read_setup_packet.
+ */
+static void dwc_otg_read_packet(struct dwc_ep *ep, u16 bytes)
+{
+ u32 i;
+ int word_count = (bytes + 3) / 4;
+ u32 *fifo = dev_if->data_fifo[0];
+ u32 *data_buff = (u32 *) ep->xfer_buff;
+ u32 unaligned;
+ /*
+ * This requires reading data from the FIFO into a u32 temp buffer,
+ * then moving it into the data buffer.
+ */
+ if ((bytes < 4) && (bytes > 0)) {
+ unaligned = readl(fifo);
+ memcpy(data_buff, &unaligned, bytes);
+ } else {
+ for (i = 0; i < word_count; i++, data_buff++)
+ *data_buff = readl(fifo);
+ }
+}
+
+/* Handle RX transaction on non-ISO endpoint. */
+static void dw_udc_epn_rx(struct dwc_ep *ep, int bcnt)
+{
+ struct urb *urb;
+ struct usb_endpoint_instance *endpoint = dw_find_ep(ep->num);
+
+ if (endpoint) {
+ urb = endpoint->rcv_urb;
+
+ if (urb) {
+ ep->xfer_buff = urb->buffer + urb->actual_length;
+ dwc_otg_read_packet(ep, bcnt);
+ usbd_rcv_complete(endpoint, bcnt, 0);
+ }
+ }
+}
+
+/*
+ * This function writes a packet into the Tx FIFO associated with the EP.
+ * The buffer is padded to DWORD on a per packet basis in
+ * slave/dma mode if the MPS is not DWORD aligned. The last packet, if
+ * short, is also padded to a multiple of DWORD.
+ *
+ * ep->xfer_buff always starts DWORD aligned in memory and is a
+ * multiple of DWORD in length
+ *
+ * ep->xfer_len can be any number of bytes
+ *
+ * FIFO access is DWORD
+ */
+static void dwc_otg_ep_write_packet(struct dwc_ep *ep)
+{
+ u32 i;
+ u32 dword_count;
+ u32 *fifo;
+ u32 *data_buff = (u32 *) ep->xfer_buff;
+ u32 temp, unaligned;
+ u32 timeout = 1; /* 1ms as the timeout */
+ ulong start;
+ struct device_in_ep_regs *in_ep_regs = dev_if->in_ep_regs[ep->num];
+ struct core_global_regs *core_global_regs = dev_if->core_global_regs;
+
+ /*
+ * Find the DWORD length, padded by extra bytes as neccessary if MPS
+ * is not a multiple of DWORD
+ */
+ dword_count = (ep->xfer_len + 3) / 4;
+ fifo = dev_if->data_fifo[ep->num];
+
+ /* program pkt count */
+ temp = ep->xfer_len;
+ temp |= (1 << PKTCNT_SHIFT);
+ writel(temp, &in_ep_regs->dieptsiz);
+
+ /* enable EP*/
+ setbits_le32(&in_ep_regs->diepctl, EPENA | CNAK);
+
+ /* clear TX Fifo Empty intr*/
+ writel(NPTXFEMPTY, &core_global_regs->gintsts);
+
+ setbits_le32(&core_global_regs->gintmsk, NPTXFEMPTY);
+
+ start = get_timer(0);
+ while (!(readl(&core_global_regs->gintsts) & NPTXFEMPTY)) {
+ if (get_timer(start) > timeout) {
+ printf("%s: NPTXFEMPTY: TimeOUT\n", __func__);
+ WATCHDOG_RESET();
+ }
+ }
+
+ /* write to fifo */
+ if ((ep->xfer_len < 4) && (ep->xfer_len > 0)) {
+ memcpy(&unaligned, data_buff, ep->xfer_len);
+ *fifo = unaligned;
+ } else {
+ for (i = 0; i < dword_count; i++, data_buff++)
+ *fifo = *data_buff;
+ }
+
+ writel(NPTXFEMPTY, &core_global_regs->gintsts);
+
+ /* check for transfer completion*/
+ start = get_timer(0);
+ while (!(readl(&in_ep_regs->diepint) & XFERCOMPL)) {
+ if (get_timer(start) > timeout) {
+ printf("%s: XFERCOMPLE: TimeOUT\n", __func__);
+ WATCHDOG_RESET();
+ }
+ }
+
+ writel(XFERCOMPL, &in_ep_regs->diepint);
+ clrbits_le32(&core_global_regs->gintmsk, NPTXFEMPTY);
+}
+
+/* Handle TX transaction on non-ISO endpoint. */
+static void dw_udc_epn_tx(struct dwc_ep *ep)
+{
+ struct usb_endpoint_instance *endpoint = dw_find_ep(ep->num);
+ struct urb *urb = endpoint->tx_urb;
+ int align;
+
+ if (!endpoint)
+ return;
+
+ /*
+ * We need to transmit a terminating zero-length packet now if
+ * we have sent all of the data in this URB and the transfer
+ * size was an exact multiple of the packet size.
+ */
+ if (urb && (endpoint->last == endpoint->tx_packetSize) &&
+ (urb->actual_length - endpoint->sent -
+ endpoint->last == 0)) {
+ /* handle zero length packet here */
+ ep->xfer_len = 0;
+ dwc_otg_ep_write_packet(ep);
+ }
+
+ if (urb && urb->actual_length) {
+ /* retire the data that was just sent */
+ usbd_tx_complete(endpoint);
+ /*
+ * Check to see if we have more data ready to transmit
+ * now.
+ */
+ if (urb && urb->actual_length) {
+ /* write data to FIFO */
+ ep->xfer_len = MIN(urb->actual_length - endpoint->sent,
+ endpoint->tx_packetSize);
+
+ if (ep->xfer_len) {
+ ep->xfer_buff = urb->buffer + endpoint->sent;
+
+ /*
+ * This ensures that USBD packet fifo is
+ * accessed through word aligned pointer or
+ * through non word aligned pointer but only
+ * with a max length to make the next packet
+ * word aligned
+ */
+
+ align = ((ulong)ep->xfer_buff % sizeof(int));
+ if (align)
+ ep->xfer_len = MIN(ep->xfer_len,
+ sizeof(int)-align);
+
+ dwc_otg_ep_write_packet(ep);
+ }
+ endpoint->last = ep->xfer_len;
+
+ }
+ }
+}
+
+/* This function returns pointer to out ep struct with number num */
+static struct dwc_ep *get_out_ep(u32 num)
+{
+ u32 i;
+ int num_out_eps = MAX_EPS_CHANNELS;
+ struct dwc_pcd *pcd = &dev_if->pcd;
+
+ if (num == 0)
+ return &pcd->ep0;
+
+ for (i = 0; i < num_out_eps; ++i) {
+ if (pcd->out_ep[i].num == num)
+ return &pcd->out_ep[i];
+ }
+
+ return 0;
+}
+
+/* This function returns pointer to in ep struct with number num */
+static struct dwc_ep *get_in_ep(u32 num)
+{
+ u32 i;
+ int num_out_eps = MAX_EPS_CHANNELS;
+ struct dwc_pcd *pcd = &dev_if->pcd;
+
+ if (num == 0)
+ return &pcd->ep0;
+
+ for (i = 0; i < num_out_eps; ++i) {
+ if (pcd->in_ep[i].num == num)
+ return &pcd->in_ep[i];
+ }
+
+ return 0;
+}
+
+/*
+ * This function reads the 8 bytes of the setup packet from the Rx FIFO into the
+ * destination buffer. It is called from the Rx Status Queue Level (RxStsQLvl)
+ * interrupt routine when a SETUP packet has been received in Slave mode.
+ */
+static void dwc_otg_read_setup_packet(u32 *dest)
+{
+ dest[0] = readl(dev_if->data_fifo[0]);
+ dest[1] = readl(dev_if->data_fifo[0]);
+}
+
+/*
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO. The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ * info
+ * -# If Receive FIFO is empty then skip to step Clear the interrupt
+ * and exit
+ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ * SETUP data to the buffer
+ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ * to the destination buffer
+ */
+static int dwc_otg_pcd_handle_rx_status_q_level_intr(void)
+{
+ struct core_global_regs *global_regs = dev_if->core_global_regs;
+ struct dwc_pcd *pcd = &dev_if->pcd;
+ u32 status;
+ struct dwc_ep *ep;
+ u32 bcnt;
+
+ /* Disable the Rx Status Queue Level interrupt */
+ clrbits_le32(&global_regs->gintmsk, RXSTSQLVL);
+
+ /* Get the Status from the top of the FIFO */
+ status = readl(&global_regs->grxstsp);
+ /* Get pointer to EP structure */
+ ep = get_out_ep((status & EPNUMMSK) >> EPNUM_SHIFT);
+ bcnt = (status & BCNTMSK) >> BCNT_SHIFT;
+
+ switch ((status & PKTSTSMSK) >> PKTSTS_SHIFT) {
+ case DWC_DSTS_GOUT_NAK:
+ break;
+ case DWC_STS_DATA_UPDT:
+ if (bcnt)
+ dw_udc_epn_rx(ep, bcnt);
+ break;
+ case DWC_STS_XFER_COMP:
+ break;
+ case DWC_DSTS_SETUP_COMP:
+ break;
+ case DWC_DSTS_SETUP_UPDT:
+ dwc_otg_read_setup_packet((u32 *)pcd->req);
+ break;
+ default:
+ break;
+ }
+
+ /* Enable the Rx Status Queue Level interrupt */
+ setbits_le32(&global_regs->gintmsk, RXSTSQLVL);
+
+ /* Clear interrupt */
+ setbits_le32(&global_regs->gintsts, RXSTSQLVL);
+
+ return 1;
+}
+
+/*
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_in_status_phase(struct device_if *dev_if)
+{
+ struct device_out_ep_regs *out_regs =
+ dev_if->out_ep_regs[0];
+
+ setbits_le32(&out_regs->doeptsiz, PKTCNT_SHIFT);
+
+ setbits_le32(&out_regs->doepctl, CNAK | EPENA);
+}
+
+/*
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control tranfers are tracked in ep0state
+ *
+ * A flag set indicates that it is not the first packet, so do not
+ * process setup data now. it has alreday been processed, just send the
+ * next data packet
+ */
+static void handle_ep0(int in_flag)
+{
+ struct dwc_pcd *pcd = &dev_if->pcd;
+ struct dwc_ep *ep0 = &pcd->ep0;
+ struct usb_device_request *ctrl = pcd->req;
+
+ /* handle inepint, only when more than 64 bytes to transfer*/
+ if (in_flag & !ep0_urb->actual_length)
+ return;
+
+ if (!ep0_urb->actual_length) {
+ if (ep0_recv_setup(ep0_urb)) {
+ udc_set_stall(0, ctrl->bmRequestType & USB_DIR_IN);
+ return;
+ }
+ ep0->xfer_buff = (u8 *)ep0_urb->buffer;
+ } else
+ ep0->xfer_buff += EP0_MAX_PACKET_SIZE;
+
+ if (ep0_urb->actual_length <= EP0_MAX_PACKET_SIZE) {
+ ep0->xfer_len = ep0_urb->actual_length;
+ ep0_urb->actual_length = 0;
+ } else {
+ ep0->xfer_len = EP0_MAX_PACKET_SIZE;
+ ep0_urb->actual_length -= EP0_MAX_PACKET_SIZE;
+ }
+
+ if (ctrl->bmRequestType & USB_DIR_IN) {
+ dwc_otg_ep_write_packet(ep0);
+ if (!ep0_urb->actual_length)
+ do_setup_in_status_phase(dev_if);
+ } else {
+ if (!ctrl->wLength)
+ dwc_otg_ep_write_packet(ep0);
+ else
+ udc_set_stall(0, ctrl->bmRequestType & USB_DIR_OUT);
+ }
+}
+
+/*
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static u32 dwc_otg_read_dev_all_out_ep_intr(void)
+{
+ u32 v;
+
+ v = readl(&dev_if->dev_global_regs->daint) &
+ readl(&dev_if->dev_global_regs->daintmsk);
+ return v >> 16;
+}
+
+/*
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static u32 dwc_otg_read_dev_all_in_ep_intr(void)
+{
+ u32 v;
+
+ v = readl(&dev_if->dev_global_regs->daint) &
+ readl(&dev_if->dev_global_regs->daintmsk);
+ return v & 0xffff;
+}
+
+/* This function returns the Device OUT EP Interrupt register */
+static u32 dwc_otg_read_doep_intr(struct dwc_ep *ep)
+{
+ u32 v;
+
+ v = readl(&dev_if->out_ep_regs[ep->num]->doepint) &
+ readl(&dev_if->dev_global_regs->doepmsk);
+ return v;
+}
+
+/*This function returns the Device IN EP Interrupt register */
+static u32 dwc_otg_read_diep_intr(struct dwc_ep *ep)
+{
+ u32 v;
+
+ v = readl(&dev_if->in_ep_regs[ep->num]->diepint) &
+ readl(&dev_if->dev_global_regs->diepmsk);
+ return v;
+}
+
+/*
+ * This function configures EPO to receive SETUP packets.
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet:
+ *
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ * packets)
+ *
+ * In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ */
+static void ep0_out_start(void)
+{
+ u32 temp;
+
+ /* program transfer size*/
+ temp = 8 * 3;
+ /* program packet count*/
+ temp |= PKTCNT;
+ /* program setup packet count */
+ temp |= (3 << SUPCNT_SHIFT);
+ writel(temp, &dev_if->out_ep_regs[0]->doeptsiz);
+}
+
+/* should be called after set address is received */
+void udc_set_address_controller(u32 address)
+{
+ u32 dcfg;
+
+ dcfg = readl(&dev_if->dev_global_regs->dcfg);
+ dcfg &= ~DEVADDRMSK;
+ dcfg |= address << DEVADDR_SHIFT;
+ writel(dcfg, &dev_if->dev_global_regs->dcfg);
+
+ usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
+}
+
+/* should be called after set configuration is received */
+static void dwc_otg_bulk_out_activate(void)
+{
+ struct device_out_ep_regs *out_regs =
+ dev_if->out_ep_regs[UDC_OUT_ENDPOINT];
+ struct device_global_regs *dev_global_regs
+ = dev_if->dev_global_regs;
+
+ setbits_le32(&dev_global_regs->daintmsk,
+ (UDC_OUT_ENDPOINT + DAINTMASK_OUT_SHIFT));
+
+ setbits_le32(&out_regs->doeptsiz,
+ CONFIG_USBD_SERIAL_BULK_PKTSIZE | PKTCNT_SHIFT);
+
+ clrsetbits_le32(&out_regs->doepctl, DOEPCTL_MPSMSK | EPTYPEMSK,
+ CONFIG_USBD_SERIAL_BULK_PKTSIZE | CNAK | EPENA |
+ USBACTEP | DATA0PID | (EPTYPE_BULK << EPTYPE_SHIFT));
+}
+
+/* should be called after set configuration is received */
+static void dwc_otg_bulk_in_activate(void)
+{
+ struct device_in_ep_regs *in_regs =
+ dev_if->in_ep_regs[UDC_IN_ENDPOINT];
+ struct device_global_regs *dev_global_regs
+ = dev_if->dev_global_regs;
+
+ setbits_le32(&dev_global_regs->daintmsk,
+ (UDC_IN_ENDPOINT + DAINTMASK_IN_SHIFT));
+
+ clrsetbits_le32(&in_regs->diepctl,
+ DIEPCTL_MPSMSK | EPTYPEMSK,
+ CONFIG_USBD_SERIAL_BULK_PKTSIZE | USBACTEP | DATA0PID |
+ (EPTYPE_BULK << EPTYPE_SHIFT));
+}
+
+static void dwc_otg_int_in_activate(void)
+{
+ struct device_in_ep_regs *in_regs =
+ dev_if->in_ep_regs[UDC_INT_ENDPOINT];
+ struct device_global_regs *dev_global_regs
+ = dev_if->dev_global_regs;
+
+ setbits_le32(&dev_global_regs->daintmsk,
+ (UDC_INT_ENDPOINT + DAINTMASK_IN_SHIFT));
+
+ clrsetbits_le32(&in_regs->diepctl,
+ DIEPCTL_MPSMSK | EPTYPEMSK,
+ UDC_INT_PACKET_SIZE | USBACTEP | DATA0PID |
+ (EPTYPE_INT << EPTYPE_SHIFT));
+}
+
+/* should be called after set configuration is received */
+void udc_set_configuration_controller(u32 config)
+{
+ dwc_otg_bulk_out_activate();
+ dwc_otg_bulk_in_activate();
+ dwc_otg_int_in_activate();
+ usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
+}
+
+/* should be called to receive next packet */
+static void dwc_otg_bulk_out_enable(void)
+{
+ struct device_out_ep_regs *out_regs =
+ dev_if->out_ep_regs[UDC_OUT_ENDPOINT];
+
+ setbits_le32(&out_regs->doeptsiz, PKTCNT_SHIFT |
+ CONFIG_USBD_SERIAL_BULK_PKTSIZE);
+ setbits_le32(&out_regs->doepctl, CNAK | EPENA);
+}
+
+/* This interrupt indicates that an OUT EP has a pending Interrupt. */
+
+static int dwc_otg_pcd_handle_out_ep_intr(void)
+{
+ u32 ep_intr;
+ u32 doepint;
+ u32 epnum = 0;
+ struct dwc_ep *ep;
+ struct device_out_ep_regs **out_ep_regs
+ = dev_if->out_ep_regs;
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_out_ep_intr();
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ ep = get_out_ep(epnum);
+ doepint = dwc_otg_read_doep_intr(ep);
+
+ /* Transfer complete */
+ if (doepint & XFERCOMPL) {
+ /* Clear xfercompl */
+ writel(XFERCOMPL, &out_ep_regs[epnum]->doepint);
+ if (!epnum)
+ ep0_out_start();
+ else if (epnum == UDC_OUT_ENDPOINT)
+ dwc_otg_bulk_out_enable();
+ }
+ /* Setup Phase Done (control EPs) */
+ if (doepint & SETUP) {
+ writel(SETUP, &out_ep_regs[epnum]->doepint);
+ handle_ep0(0);
+ }
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+ return 1;
+}
+
+/* This interrupt indicates that an IN EP has a pending Interrupt. */
+
+static int dwc_otg_pcd_handle_in_ep_intr(void)
+{
+ u32 ep_intr;
+ u32 diepint;
+ u32 epnum = 0;
+ struct dwc_ep *ep;
+ struct device_in_ep_regs **in_ep_regs
+ = dev_if->in_ep_regs;
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_in_ep_intr();
+ while (ep_intr) {
+ if (!(ep_intr & 0x1))
+ continue;
+ ep = get_in_ep(epnum);
+ diepint = dwc_otg_read_diep_intr(ep);
+
+ /* IN token received when txfifo empty */
+ if (diepint & INTKNTXFEMP) {
+ /* Clear xfercompl */
+ writel(INTKNTXFEMP,
+ &in_ep_regs[epnum]->diepint);
+ if (!epnum)
+ handle_ep0(1);
+ else if (epnum == UDC_IN_ENDPOINT)
+ dw_udc_epn_tx(ep);
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+ return 1;
+}
+
+static void dwc_otg_flush_tx_fifo(const int num)
+{
+ struct core_global_regs *global_regs = dev_if->core_global_regs;
+ u32 val = 0;
+ u32 timeout = 10; /* 10ms timeout */
+ ulong start = 0;
+
+ clrsetbits_le32(&global_regs->grstctl, TXFNUM,
+ TXFFLSH | (num << TXFNUM_SHIFT));
+
+ start = get_timer(0);
+ while (val & TXFFLSH) {
+ if (get_timer(start) > timeout)
+ WATCHDOG_RESET();
+
+ val = readl(&global_regs->grstctl);
+ }
+
+ /* Wait for 3 PHY Clocks */
+ udelay(1);
+}
+
+static void dwc_otg_flush_rx_fifo(void)
+{
+ struct core_global_regs *global_regs = dev_if->core_global_regs;
+ int timeout = 10; /* 10 ms timeout */
+ u32 val = 0;
+ ulong start;
+
+ setbits_le32(&global_regs->grstctl, RXFFLSH);
+
+ start = get_timer(0);
+ while (val & RXFFLSH) {
+ if (get_timer(start) > timeout)
+ WATCHDOG_RESET();
+
+ val = readl(&global_regs->grstctl);
+ }
+
+ /* Wait for 3 PHY Clocks */
+ udelay(1);
+}
+
+/*
+ * This interrupt occurs when a USB Reset is detected. When the USB Reset
+ * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set
+ * to IDLE.
+ *
+ */
+static int dwc_otg_pcd_handle_usb_reset_intr(void)
+{
+ u32 temp;
+ u32 i;
+ struct device_out_ep_regs **out_ep_regs
+ = dev_if->out_ep_regs;
+ struct device_global_regs *dev_global_regs
+ = dev_if->dev_global_regs;
+ struct core_global_regs *core_global_regs
+ = dev_if->core_global_regs;
+ /* Set NAK for all OUT EPs */
+ for (i = 0; i < MAX_EPS_CHANNELS; i++)
+ setbits_le32(&out_ep_regs[i]->doepctl, SNAK);
+
+ /* Flush the NP Tx FIFO */
+ dwc_otg_flush_tx_fifo(DWC_GRSTCTL_TXFNUM_ALL);
+ dwc_otg_flush_rx_fifo();
+ setbits_le32(&dev_global_regs->daintmsk, DAINTMASK_IN_SHIFT |
+ DAINTMASK_OUT_SHIFT);
+
+ setbits_le32(&dev_global_regs->doepmsk, SETUPMSK | XFERCOMPLMSK |
+ AHBERRMSK | EPDISABLEDMSK);
+
+ writel(INTKNTXFEMP, &dev_global_regs->diepmsk);
+
+ setbits_le32(&core_global_regs->gintmsk, GOUTNAKEFF);
+
+ /* program fifo size for ep0 */
+ writel(DWC_OTG_RX_FIFO_SIZE, &core_global_regs->grxfsiz);
+
+ temp = readl(&dev_if->in_ep_regs[0]->diepctl);
+ temp &= 0xFFC3FFFF; /* TxFNumBF = 0, bits 25:22 */
+ writel(temp, &dev_if->in_ep_regs[0]->diepctl);
+
+ temp = readl(&core_global_regs->gnptxfsiz);
+
+ writel((DWC_OTG_RX_FIFO_SIZE | DWC_OTG_NPTX_FIFO_SIZE << 16),
+ &core_global_regs->gnptxfsiz);
+
+ /* Reset Device Address */
+ clrbits_le32(&dev_global_regs->dcfg, DEVADDRMSK);
+
+ /* setup EP0 to receive SETUP packets */
+ ep0_out_start();
+
+ /* Clear interrupt */
+ writel(USBRESET, &core_global_regs->gintsts);
+
+ usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0);
+
+ return 1;
+}
+
+/*
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+static int dwc_otg_pcd_handle_enum_done_intr(void)
+{
+ struct core_global_regs *global_regs = dev_if->core_global_regs;
+
+ dwc_otg_ep0_activate();
+
+ clrsetbits_le32(&global_regs->gusbcfg, USBTRDTIMMSK, PHYIF_16BIT |
+ (USBTRDTIM_MAC_IF_8BIT_UTMI << USBTRDTIM_SHIFT));
+
+ /* Clear interrupt */
+ writel(ENUMDONE, &global_regs->gintsts);
+ usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+
+ return 1;
+}
+
+static u32 dwc_otg_read_core_intr(void)
+{
+ return readl(&dev_if->core_global_regs->gintsts) &
+ readl(&dev_if->core_global_regs->gintmsk);
+}
+
+static void dwc_otg_init(const void *reg_base)
+{
+ struct dwc_pcd *pcd = &dev_if->pcd;
+ u32 offset;
+ u32 i;
+
+ dev_if->core_global_regs = (struct core_global_regs *) reg_base;
+ dev_if->dev_global_regs = (struct device_global_regs *) ((u32)reg_base +
+ DWC_DEV_GLOBAL_REG_OFFSET);
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ offset = i * DWC_EP_REG_OFFSET;
+
+ dev_if->in_ep_regs[i] = (struct device_in_ep_regs *)
+ ((u32)reg_base + DWC_DEV_IN_EP_REG_OFFSET + offset);
+
+ dev_if->out_ep_regs[i] = (struct device_out_ep_regs *)
+ ((u32)reg_base + DWC_DEV_OUT_EP_REG_OFFSET + offset);
+ }
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ dev_if->data_fifo[i] =
+ (u32 *) ((u32)reg_base + DWC_OTG_DATA_FIFO_OFFSET +
+ (i * DWC_OTG_DATA_FIFO_SIZE));
+ }
+
+ dev_if->speed = 0; /* unknown */
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ pcd->in_ep[i].num = i;
+ pcd->out_ep[i].num = i;
+ }
+}
+
+/*
+ * This function initializes the DWC_otg controller registers and prepares the
+ * core for device mode
+ */
+static void dwc_otg_core_init(void)
+{
+ struct core_global_regs *global_regs = dev_if->core_global_regs;
+
+ /* Step 1: Program the GAHBCFG Register. */
+ setbits_le32(&global_regs->gahbcfg, DWC_NPTXEMPTYLVL_EMPTY |
+ DWC_PTXEMPTYLVL_EMPTY);
+
+ /* Step 2: write usbcfg regs*/
+ setbits_le32 (&global_regs->gusbcfg, SRPCAP | HNPCAP);
+
+ /* step3: write int_msk reg*/
+ setbits_le32(&global_regs->gintmsk, USBRESET | ENUMDONE | RXSTSQLVL |
+ OUTEPINTR | INEPINTR);
+}
+
+/* Switch on the UDC */
+static void usbotg_init(void)
+{
+ udc_device = NULL;
+ dwc_otg_init((void *)CONFIG_SYS_USBD_BASE);
+
+ /* Initialize the DWC_otg core. */
+ dwc_otg_core_init();
+
+}
+
+void udc_irq(void)
+{
+ u32 status;
+
+ status = dwc_otg_read_core_intr();
+ while (status) {
+ if (status & USBRESET)
+ dwc_otg_pcd_handle_usb_reset_intr();
+ if (status & ENUMDONE)
+ dwc_otg_pcd_handle_enum_done_intr();
+ if (status & RXSTSQLVL)
+ dwc_otg_pcd_handle_rx_status_q_level_intr();
+ if (status & OUTEPINTR)
+ dwc_otg_pcd_handle_out_ep_intr();
+ if (status & INEPINTR)
+ dwc_otg_pcd_handle_in_ep_intr();
+ status = dwc_otg_read_core_intr();
+ }
+
+}
+
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
+{
+ udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK);
+ return 0;
+}
+
+static void udc_enable(struct usb_device_instance *device)
+{
+ struct dwc_pcd *pcd = &dev_if->pcd;
+
+ /* Save the device structure pointer */
+ udc_device = device;
+
+ /* Setup ep0 urb */
+ if (!ep0_urb) {
+ ep0_urb =
+ usbd_alloc_urb(udc_device,
+ udc_device->bus->endpoint_array);
+ pcd->req =
+ (struct usb_device_request *)&ep0_urb->device_request;
+ pcd->ep0.xfer_buff = (u8 *)ep0_urb->buffer;
+ } else {
+ printf("udc_enable: ep0_urb already allocated %p\n",
+ ep0_urb);
+ }
+}
+
+void udc_connect(void)
+{
+ struct device_global_regs *dev_regs = dev_if->dev_global_regs;
+
+ /* remove soft disconnect */
+ clrbits_le32(&dev_regs->dctl, SFTDISCON);
+}
+
+void udc_disconnect(void)
+{
+ struct device_global_regs *dev_regs = dev_if->dev_global_regs;
+
+ /* soft disconnect */
+ setbits_le32(&dev_regs->dctl, SFTDISCON);
+
+ udelay(150);
+}
+
+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);
+
+ /*
+ * Some USB controller driver implementations signal
+ * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here.
+ * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED,
+ * and DEVICE_RESET causes a transition to the state STATE_DEFAULT.
+ * The DW USB client controller has the capability to detect when the
+ * USB cable is connected to a powered USB bus, so we will defer the
+ * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later.
+ */
+
+ udc_enable(device);
+
+}
+
+void udc_setup_ep(struct usb_device_instance *device, unsigned int ep,
+ struct usb_endpoint_instance *endpoint)
+{
+ /*
+ * Nothing to do here. Hob of this function has laready been
+ * done during init.
+ */
+}
+
+int is_usbd_high_speed(void)
+{
+ struct device_global_regs *dev_regs = dev_if->dev_global_regs;
+ u32 dsts;
+
+ dsts = readl(&dev_regs->dsts);
+ dsts &= ENUMSPDMSK;
+ if (dsts == DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ)
+ return 1;
+ else
+ return 0;
+}
+
+int udc_init(void)
+{
+ phy_init();
+ udc_disconnect();
+ usbotg_init();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,523 @@
+/*
+ * (C) Copyright 2011
+ * Pratyush Anand, ST Micoelectronics, pratyush.anand@st.com.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DW_OTG_H
+#define __DW_OTG_H
+
+#include "usbdevice.h"
+/* USBTTY definitions */
+#define EP0_MAX_PACKET_SIZE 64
+#define UDC_INT_ENDPOINT 1
+#define UDC_INT_PACKET_SIZE 64
+#define UDC_OUT_ENDPOINT 2
+#define UDC_BULK_PACKET_SIZE 512
+#if defined(CONFIG_USBD_HS)
+#define UDC_BULK_HS_PACKET_SIZE 512
+#endif
+#define UDC_IN_ENDPOINT 3
+#define UDC_OUT_PACKET_SIZE 64
+#define UDC_IN_PACKET_SIZE 64
+
+/* UDC endpoint definitions */
+#define UDC_EP0 0
+#define UDC_EP1 1
+#define UDC_EP2 2
+#define UDC_EP3 3
+
+#define CMD_SIZE 12
+/* OTG Register Definitions */
+
+/*
+ * The application interfaces with the HS OTG core by reading from and
+ * writing to the Control and Status Register (CSR) space through the
+ * AHB Slave interface. These registers are 32 bits wide, and the
+ * addresses are 32-bit-block aligned.
+ * CSRs are classified as follows:
+ * - Core Global Registers
+ * - Device Mode Registers
+ * - Device Global Registers
+ * - Device Endpoint Specific Registers
+ * - Host Mode Registers
+ * - Host Global Registers
+ * - Host Port CSRs
+ * - Host Channel Specific Registers
+ *
+ * Only the Core Global registers can be accessed in both Device and
+ * Host modes. When the HS OTG core is operating in one mode, either
+ * Device or Host, the application must not access registers from the
+ * other mode. When the core switches from one mode to another, the
+ * registers in the new mode of operation must be reprogrammed as they
+ * would be after a power-on reset.
+ */
+
+/*
+ * DWC_otg Core registers. The core_global_regs structure defines the
+ * size and relative field offsets for the Core Global registers.
+ */
+struct core_global_regs {
+ /* OTG Control and Status Register. Offset: 000h */
+ u32 gotgctl;
+ /* OTG Interrupt Register. Offset: 004h */
+ u32 gotgint;
+ /* Core AHB Configuration Register. Offset: 008h */
+ u32 gahbcfg;
+
+#define DWC_GLBINTRMASK 0x0001
+#define DWC_DMAENABLE 0x0020
+#define DWC_NPTXEMPTYLVL_EMPTY 0x0080
+#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000
+#define DWC_PTXEMPTYLVL_EMPTY 0x0100
+#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000
+
+ /* Core USB Configuration Register. Offset: 00Ch */
+ u32 gusbcfg;
+#define PHYIF_16BIT (1 << 3)
+#define SRPCAP (1 << 8)
+#define HNPCAP (1 << 9)
+#define TERM_SEL_DL_PULSE (1 << 22)
+#define USBTRDTIM_SHIFT 10
+#define USBTRDTIMMSK (0xF << USBTRDTIM_SHIFT)
+#define USBTRDTIM_MAC_IF_8BIT_UTMI 0x9
+#define USBTRDTIM_MAC_IF_16BIT_UTMI 0x5
+ /* Core Reset Register. Offset: 010h */
+ u32 grstctl;
+#define DWC_GRSTCTL_TXFNUM_ALL 0x10
+#define CSFTRST (1 << 0)
+#define INTKNQFLSH (1 << 3)
+#define RXFFLSH (1 << 4)
+#define TXFFLSH (1 << 5)
+#define TXFNUM_SHIFT 6
+#define TXFNUM (0x1F << TXFNUM_SHIFT)
+#define AHBIDLE ((u32)1 << 31)
+ /* Core Interrupt Register. Offset: 014h */
+ u32 gintsts;
+#define RXSTSQLVL (1 << 4)
+#define NPTXFEMPTY (1 << 5)
+#define GOUTNAKEFF (1 << 7)
+#define USBRESET (1 << 12)
+#define ENUMDONE (1 << 13)
+#define INEPINTR (1 << 18)
+#define OUTEPINTR (1 << 19)
+ /* Core Interrupt Mask Register. Offset: 018h */
+ u32 gintmsk;
+ /*
+ * Receive Status Queue Read Register
+ * (Read Only) Offset: 01Ch
+ */
+ u32 grxstsr;
+ /*
+ * Receive Status Queue Read & POP Register
+ * (Read Only) Offset: 020h
+ */
+ u32 grxstsp;
+#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */
+#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */
+#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */
+#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */
+#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */
+#define EPNUM_SHIFT 0
+#define EPNUMMSK (0xF << EPNUM_SHIFT)
+#define BCNT_SHIFT 4
+#define BCNTMSK (0x7FF << BCNT_SHIFT)
+#define PKTSTS_SHIFT 17
+#define PKTSTSMSK (0xF << PKTSTS_SHIFT)
+ /* Receive FIFO Size Register. Offset: 024h */
+ u32 grxfsiz;
+#define dwc_param_dev_rx_fifo_size_default 1064
+ /* Non Periodic Transmit FIFO Size Register. Offset: 028h */
+ u32 gnptxfsiz;
+#define dwc_param_dev_nperio_tx_fifo_size_default 1024
+#define DWC_OTG_RX_FIFO_SIZE 0x200
+#define DWC_OTG_NPTX_FIFO_SIZE 0x200
+ /*
+ * Non Periodic Transmit FIFO/Queue Status Register
+ * (Read Only). Offset: 02Ch
+ */
+ u32 gnptxsts;
+#define NPTXQSPCAVAIL_SHIFT 16
+#define NPTXQSPCAVAILMSK (0xFF << NPTXQSPCAVAIL_SHIFT)
+#define NPTXFSPCAVAIL_SHIFT 0
+#define NPTXFSPCAVAILMSK (0xFFFF << NPTXFSPCAVAIL_SHIFT)
+ /* I2C Access Register. Offset: 030h */
+ u32 gi2cctl;
+ /* PHY Vendor Control Register. Offset: 034h */
+ u32 gpvndctl;
+ /* General Purpose Input/Output Register. Offset: 038h */
+ u32 ggpio;
+ /* User ID Register. Offset: 03Ch */
+ u32 guid;
+ /* Synopsys ID Register (Read Only). Offset: 040h */
+ u32 gsnpsid;
+ /* User HW Config1 Register (Read Only). Offset: 044h */
+ u32 ghwcfg1;
+ /* User HW Config2 Register (Read Only). Offset: 048h */
+
+ u32 ghwcfg2;
+#define DWC_SLAVE_ONLY_ARCH 0
+#define DWC_EXT_DMA_ARCH 1
+#define DWC_INT_DMA_ARCH 2
+
+#define DWC_MODE_HNP_SRP_CAPABLE 0
+#define DWC_MODE_SRP_ONLY_CAPABLE 1
+#define DWC_MODE_NO_HNP_SRP_CAPABLE 2
+#define DWC_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_MODE_SRP_CAPABLE_HOST 5
+#define DWC_MODE_NO_SRP_CAPABLE_HOST 6
+#define DYNAMIC_FIFO (1 << 19)
+#define NUM_DEV_EP_SHIFT 10
+#define NUM_DEV_EP (0xF << NUM_DEV_EP_SHIFT)
+#define HSPHYTYPE_SHIFT 6
+#define HSPHYTYPEMSK (3 << HSPHYTYPE_SHIFT)
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
+#define TKNQDEPTH_SHIFT 26
+#define TKNQDEPTHMSK (0x1F << TKNQDEPTH_SHIFT)
+
+ /* User HW Config3 Register (Read Only). Offset: 04Ch */
+ u32 ghwcfg3;
+#define DFIFO_DEPTH_SHIFT 16
+#define DFIFO_DEPTH ((u32)0xFFFF << DFIFO_DEPTH_SHIFT)
+ /* User HW Config4 Register (Read Only). Offset: 050h */
+ u32 ghwcfg4;
+#define NUM_DEV_PERIO_IN_EP_SHIFT 0
+#define NUM_DEV_PERIO_IN_EP (0xF << NUM_DEV_PERIO_IN_EP_SHIFT)
+#define DED_FIFO_EN (1 << 25)
+#define NUM_IN_EPS_SHIFT 26
+#define NUM_IN_EPS (0xF << NUM_IN_EPS_SHIFT)
+#define UTMI_PHY_DATA_WIDTH_SHIFT 14
+#define UTMI_PHY_DATA_WIDTH (0x3 << UTMI_PHY_DATA_WIDTH_SHIFT)
+ /* Reserved Offset: 054h-0FFh */
+ u32 reserved[43];
+ /* Host Periodic Transmit FIFO Size Register. Offset: 100h */
+ u32 hptxfsiz;
+
+ /*
+ * Device Periodic Transmit FIFO#n Register, if dedicated fifos are
+ * disabled. Otherwise Device Transmit FIFO#n Register.
+ *
+ * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15)
+ */
+ u32 dptxfsiz_dieptxf[15];
+#define dwc_param_dev_tx_fifo_size_default 256
+#define dwc_param_dev_perio_tx_fifo_size_default 256
+};
+
+/*
+ * Device Global Registers. Offsets 800h-BFFh
+ *
+ * The following structures define the size and relative field offsets for the
+ * Device Mode Registers.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_global_regs { /* CONFIG_DWC_OTG_REG_LE */
+ /* Device Configuration Register. Offset: 800h */
+ u32 dcfg;
+#define DWC_DCFG_FRAME_INTERVAL_80 0
+#define DWC_DCFG_FRAME_INTERVAL_85 1
+#define DWC_DCFG_FRAME_INTERVAL_90 2
+#define DWC_DCFG_FRAME_INTERVAL_95 3
+#define DWC_DCFG_FRAME_INTERVAL_MASK 3
+#define PERFRINT_SHIFT 11
+#define DEVSPDMSK (0x3 << 0)
+#define DEVADDR_SHIFT 4
+#define DEVADDRMSK (0x7F << DEVADDR_SHIFT)
+#define NZSTSOUTHSHK (1 << 2)
+ /* Device Control Register. Offset: 804h */
+ u32 dctl;
+#define RMTWKUPSIG (1 << 0)
+#define SFTDISCON (1 << 1)
+#define CGNPINNAK (1 << 7)
+ /* Device Status Register (Read Only). Offset: 808h */
+ u32 dsts;
+#define ENUMSPD_SHIFT 1
+#define ENUMSPDMSK (3 << ENUMSPD_SHIFT)
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3
+ /* Reserved. Offset: 80Ch */
+ u32 unused;
+ /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */
+ u32 diepmsk;
+#define TIMEOUTMSK (1 << 3)
+#define INTKNTXFEMP (1 << 4)
+#define INTKNEPMISMSK (1 << 5)
+#define INEPNAKEFFMSK (1 << 6)
+#define TXFIFOUNDRN (1 << 8)
+ /* Device OUT Endpoint Common Interrupt Mask Register. Offset: 814h */
+ u32 doepmsk;
+#define XFERCOMPLMSK (1 << 0)
+#define EPDISABLEDMSK (1 << 1)
+#define AHBERRMSK (1 << 2)
+#define SETUPMSK (1 << 3)
+#define INTKNTXFEMPMSK (1 << 4)
+ /* Device All Endpoints Interrupt Register. Offset: 818h */
+ u32 daint;
+ /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */
+ u32 daintmsk;
+#define DAINTMASK_IN_SHIFT 0
+#define DAINTMASK_OUT_SHIFT 16
+ /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */
+ u32 dtknqr1;
+#define EPTK0_5_SHIFT 8
+#define EPTK0_5MSK ((u32)0xFFFFFF << EPTK0_5_SHIFT)
+#define INTKNWPTR_SHIFT 0
+#define INTKNWPTRMSK ((u32)0x1F << INTKNWPTR_SHIFT)
+ /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */
+ u32 dtknqr2;
+ /* Device VBUS discharge Register. Offset: 828h */
+ u32 dvbusdis;
+ /* Device VBUS Pulse Register. Offset: 82Ch */
+ u32 dvbuspulse;
+ /* Device IN Token Queue Read Register-3 (Read Only). Offset: 830h */
+ u32 dtknqr3_dthrctl;
+ /* Device IN Token Queue Read Register-4 (Read Only). Offset: 834h */
+ u32 dtknqr4_fifoemptymsk;
+};
+/*
+ * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_in_ep_regs {
+ /*
+ * Device IN Endpoint Control Register.
+ * Offset:900h + (ep_num * 20h) + 00h
+ */
+ u32 diepctl;
+#define EPENA ((u32)1 << 31)
+#define EPDIS (1 << 30)
+#define SNAK (1 << 27)
+#define CNAK (1 << 26)
+#define SSTALL (1 << 21)
+#define MPS_SHIFT 0
+#define MPSMSK0 (3 << MPS_SHIFT)
+#define DWC_DEP0CTL_MPS_64 0
+#define DWC_DEP0CTL_MPS_32 1
+#define DWC_DEP0CTL_MPS_16 2
+#define DWC_DEP0CTL_MPS_8 3
+#define DIEPCTL_MPSMSK (0x7FF << MPS_SHIFT)
+ /* Reserved. Offset:900h + (ep_num * 20h) + 04h */
+ u32 reserved04;
+ /*
+ * Device IN Endpoint Interrupt Register.
+ * Offset:900h + (ep_num * 20h) + 08h
+ */
+ u32 diepint;
+#define TXFEMP (1 << 7)
+#define INTKNTXFEMP (1 << 4)
+#define XFERCOMPL (1 << 0)
+ /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */
+ u32 reserved0C;
+ /* Device IN Endpoint Transfer Size Register.
+ * Offset:900h + (ep_num * 20h) + 10h
+ */
+ u32 dieptsiz;
+#define PKTCNT_SHIFT 19
+ /*
+ * Device IN Endpoint DMA Address Register.
+ * Offset:900h + (ep_num * 20h) + 14h
+ */
+ u32 diepdma;
+ /* Reserved.
+ * Offset:900h + (ep_num * 20h) + 18h - 900h + (ep_num * 20h) + 1Ch
+ */
+ u32 dtxfsts;
+ /*
+ * Reserved.
+ * Offset:900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch
+ */
+ u32 reserved18;
+};
+
+/*
+ * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+struct device_out_ep_regs {
+ /*
+ * Device OUT Endpoint Control Register.
+ * Offset:B00h + (ep_num * 20h) + 00h
+ */
+ u32 doepctl;
+#define DOEPCTL_MPSMSK 0x7FF
+#define USBACTEP (1 << 15)
+#define EPTYPE_SHIFT 18
+#define EPTYPEMSK (0x3 << EPTYPE_SHIFT)
+#define EPTYPE_BULK 0x2
+#define EPTYPE_INT 0x3
+#define DATA0PID (1 << 28)
+#define DATA1PID (1 << 29)
+#define DPIDMSK (1 << 16)
+ /*
+ * Device OUT Endpoint Frame number Register.
+ * Offset: B00h + (ep_num * 20h) + 04h
+ */
+ u32 doepfn;
+ /*
+ * Device OUT Endpoint Interrupt Register.
+ * Offset:B00h + (ep_num * 20h) + 08h
+ */
+ u32 doepint;
+#define XFERCOMPL (1 << 0)
+#define EPDISBLD (1 << 1)
+#define AHBERR (1 << 2)
+#define SETUP (1 << 3)
+ /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */
+ u32 reserved0C;
+ /*
+ * Device OUT Endpoint Transfer Size Register.
+ * Offset: B00h + (ep_num * 20h) + 10h
+ */
+ u32 doeptsiz;
+#define XFERSIZE_SHIFT 0
+#define XFERSIZEMSK 0x3F
+#define PKTCNT_SHIFT 19
+#define PKTCNT (3 << 19)
+#define SUPCNT_SHIFT 29
+#define SUPCNTMSK (3 << SUPCNT_SHIFT)
+ /*
+ * Device OUT Endpoint DMA Address Register.
+ * Offset:B00h + (ep_num * 20h) + 14h
+ */
+ u32 doepdma;
+ /*
+ * Reserved.
+ * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch
+ */
+ u32 unused[2];
+};
+#define MAX_EPS_CHANNELS 4
+
+/*
+ * The dwc_ep structure represents the state of a single endpoint when acting in
+ * device mode. It contains the data items needed for an endpoint to be
+ * activated and transfer packets.
+ */
+struct dwc_ep {
+ /* EP number used for register address lookup */
+ u8 num;
+ /* EP direction 0 = OUT */
+#if 0
+ u8 is_in;
+#endif
+ /* pointer to the transfer buffer */
+ u8 *xfer_buff;
+ /* Number of bytes to transfer */
+ u32 xfer_len;
+};
+
+/*
+ * DWC_otg PCD Structure.
+ * This structure encapsulates the data for the dwc_otg PCD.
+ */
+struct dwc_pcd {
+#if 0
+ /* USB gadget */
+ /* Current configuration */
+ u8 configuration;
+ /* Current interface */
+ u8 interface;
+ /* Current alternate settinng */
+ u8 alternate;
+ /* Current Address */
+ u16 address;
+ /* device state */
+/* usb_device_state_t device_state; */ /* current USB Device state */
+ /*
+ * SETUP packet for EP0. This structure is allocated as a DMA buffer on
+ * PCD initialization with enough space for up to 3 setup packets.
+ */
+#endif
+ struct usb_device_request *req;
+ /* Array of EPs. */
+ struct dwc_ep ep0;
+ /* Array of IN EPs. */
+ struct dwc_ep in_ep[MAX_EPS_CHANNELS];
+ /* Array of OUT EPs. */
+ struct dwc_ep out_ep[MAX_EPS_CHANNELS];
+};
+
+/*
+ * The device_if structure contains information needed to manage the DWC_otg
+ * controller acting in device mode. It represents the programming view of the
+ * device-specific aspects of the controller.
+ */
+struct device_if {
+ struct core_global_regs *core_global_regs;
+ /* Common configuration information */
+
+ /* Device Global Registers starting at offset 800h */
+ struct device_global_regs *dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
+
+ /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */
+ struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_IN_EP_REG_OFFSET 0x900
+#define DWC_EP_REG_OFFSET 0x20
+
+ /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+ struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
+
+ /* Push/pop addresses for endpoints or host channels.*/
+ u32 *data_fifo[MAX_EPS_CHANNELS];
+#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
+#define DWC_OTG_DATA_FIFO_SIZE 0x1000
+
+ struct dwc_pcd pcd;
+ int speed;
+};
+
+
+/* Function declarations */
+
+void phy_init(void);
+void udc_irq(void);
+
+void udc_set_nak(int epid);
+void udc_unset_nak(int epid);
+int udc_endpoint_write(struct usb_endpoint_instance *endpoint);
+int udc_init(void);
+/* void udc_enable(struct usb_device_instance *device);*/
+void udc_connect(void);
+void udc_disconnect(void);
+void udc_startup_events(struct usb_device_instance *device);
+void udc_setup_ep(struct usb_device_instance *device, unsigned int ep,
+ struct usb_endpoint_instance *endpoint);
+void udc_set_configuration_controller(u32);
+void udc_set_address_controller(u32);
+
+#endif /* __DW_UDC_H */