From patchwork Fri Nov 2 17:39:54 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vipin Kumar X-Patchwork-Id: 196735 X-Patchwork-Delegate: albert.aribaud@free.fr Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 93F2D2C00D0 for ; Sat, 3 Nov 2012 05:24:23 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 884814B251; Fri, 2 Nov 2012 19:20:30 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id PR5u1+sJ6Yqm; Fri, 2 Nov 2012 19:20:30 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9428B4AFBD; Fri, 2 Nov 2012 19:17:48 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 875A34AF96 for ; Fri, 2 Nov 2012 19:17:39 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id nMSkxMEJXn-1 for ; Fri, 2 Nov 2012 19:17:35 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from eu1sys200aog103.obsmtp.com (eu1sys200aog103.obsmtp.com [207.126.144.115]) by theia.denx.de (Postfix) with ESMTPS id 12A864AED5 for ; Fri, 2 Nov 2012 19:16:48 +0100 (CET) Received: from beta.dmz-ap.st.com ([138.198.100.35]) (using TLSv1) by eu1sys200aob103.postini.com ([207.126.147.11]) with SMTP ID DSNKUJQOD0rrwcjY9+kKZP59sHOpA+AZ8eH/@postini.com; Fri, 02 Nov 2012 18:16:50 UTC Received: from zeta.dmz-ap.st.com (ns6.st.com [138.198.234.13]) by beta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 3FDBC191; Fri, 2 Nov 2012 17:36:20 +0000 (GMT) Received: from Webmail-ap.st.com (eapex1hubcas2.st.com [10.80.176.10]) by zeta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 62EE2107D; Fri, 2 Nov 2012 17:44:33 +0000 (GMT) Received: from localhost (10.251.136.62) by Webmail-ap.st.com (10.80.176.7) with Microsoft SMTP Server (TLS) id 8.3.245.1; Sat, 3 Nov 2012 01:44:27 +0800 From: Vipin Kumar To: Date: Fri, 2 Nov 2012 23:09:54 +0530 Message-ID: <6f1e7146c2854afc13ea35624fa8aed7d81c3ff8.1351877124.git.vipin.kumar@st.com> X-Mailer: git-send-email 1.7.10.rc2.10.gb47606 In-Reply-To: <1c8cd3ce05a1064788a96bb4b49f9b82d9caf736.1351877124.git.vipin.kumar@st.com> References: <1c8cd3ce05a1064788a96bb4b49f9b82d9caf736.1351877124.git.vipin.kumar@st.com> MIME-Version: 1.0 Cc: Pratyush Anand , sr@denx.de, spear-devel@list.st.com Subject: [U-Boot] [Drivers PATCH 10/19] usb/gadget/designware_otg: Add support for designware otg X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Pratyush Anand Driver for designware otg device only implements device functionality and is meant to be used with usbtty interface. This driver will work mainly for Control and Bulk endpoints. Periodic transfer has not been verified using these drivers. Signed-off-by: Pratyush Anand Signed-off-by: Vipin Kumar --- drivers/serial/usbtty.h | 2 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/designware_otg.c | 1063 +++++++++++++++++++++++++++++++++++ include/usb/designware_otg.h | 500 ++++++++++++++++ 4 files changed, 1566 insertions(+) create mode 100644 drivers/usb/gadget/designware_otg.c create mode 100644 include/usb/designware_otg.h diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index eb670da..bd3bcbc 100644 --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -35,6 +35,8 @@ #include #elif defined(CONFIG_DW_UDC) #include +#elif defined(CONFIG_DW_OTG) +#include #endif #include diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 040eaba..30ab931 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -43,6 +43,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 diff --git a/drivers/usb/gadget/designware_otg.c b/drivers/usb/gadget/designware_otg.c new file mode 100644 index 0000000..57f6f15 --- /dev/null +++ b/drivers/usb/gadget/designware_otg.c @@ -0,0 +1,1063 @@ +/* + * 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. + * + * 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 +#include +#include +#include "ep0.h" +#include +#include + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ + +/* Some kind of debugging output... */ +#ifndef DEBUG_DWUSBTTY +#define UDCDBG(str) +#define UDCDBGA(fmt, args...) +#else +#define UDCDBG(str) serial_printf(str "\n") +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) +#endif + +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 + +static void udc_set_addr_ctrl(u32 address); +static void udc_set_cfg_ctrl(u32 config); + +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. + */ +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; + 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*/ + temp = readl(&in_ep_regs->diepctl); + temp |= (EPENA | CNAK); + writel(temp, &in_ep_regs->diepctl); + + /* clear TX Fifo Empty intr*/ + writel(NPTXFEMPTY, &core_global_regs->gintsts); + + temp = readl(&core_global_regs->gintmsk); + temp |= NPTXFEMPTY; + writel(temp, &core_global_regs->gintmsk); + + while (!(readl(&core_global_regs->gintsts) & NPTXFEMPTY)) + ; + + /* 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*/ + while (!(readl(&in_ep_regs->diepint) & XFERCOMPL)) + ; + + writel(XFERCOMPL, &in_ep_regs->diepint); + + temp = readl(&core_global_regs->gintmsk); + temp &= ~NPTXFEMPTY; + writel(temp, &core_global_regs->gintmsk); +} + +/* 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; + + } else if (urb && (urb->actual_length == 0)) { + /* udc_set_nak(ep); */ + } + } +} + +/* This function returns pointer to out ep struct with number num */ +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; + } else { + 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 */ +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; + } else { + 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 gintmsk; + u32 status; + struct dwc_ep *ep; + u32 bcnt; + + /* Disable the Rx Status Queue Level interrupt */ + gintmsk = readl(&global_regs->gintmsk); + gintmsk &= ~RXSTSQLVL; + writel(gintmsk, &global_regs->gintmsk); + + /* 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 */ + gintmsk = readl(&global_regs->gintmsk); + gintmsk |= RXSTSQLVL; + writel(gintmsk, &global_regs->gintmsk); + + /* Clear interrupt */ + writel(RXSTSQLVL, &global_regs->gintsts); + + 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]; + u32 doepctl, doeptsiz; + doeptsiz = 0; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl |= (CNAK | EPENA); + writel(doepctl, &out_regs->doepctl); +} + +static void udc_set_stall(int epid, int dir) +{ + if (dir) + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SSTALL, + &dev_if->in_ep_regs[epid]->diepctl); + else + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SSTALL, + &dev_if->out_ep_regs[epid]->doepctl); +} + +/* + * 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 + */ +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; + } + if (ep0_urb->device->address) + udc_set_addr_ctrl(ep0_urb->device->address); + + if (ep0_urb->device->configuration) + udc_set_cfg_ctrl(ep0_urb->device->configuration); + + 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 */ +static void udc_set_addr_ctrl(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; + u32 doepctl, doeptsiz, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_OUT_ENDPOINT + DAINTMASK_OUT_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl &= ~DOEPCTL_MPSMSK; + doepctl &= ~EPTYPEMSK; + doepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE | + CNAK | EPENA | USBACTEP | DATA0PID + | (EPTYPE_BULK << EPTYPE_SHIFT)); + writel(doepctl, &out_regs->doepctl); +} + +/* 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; + u32 diepctl, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_IN_ENDPOINT + DAINTMASK_IN_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + + diepctl = readl(&in_regs->diepctl); + diepctl &= ~DIEPCTL_MPSMSK; + diepctl &= ~EPTYPEMSK; + diepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE + | USBACTEP | DATA0PID + | (EPTYPE_BULK << EPTYPE_SHIFT)); + writel(diepctl, &in_regs->diepctl); +} + +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; + u32 diepctl, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_INT_ENDPOINT + DAINTMASK_IN_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + + diepctl = readl(&in_regs->diepctl); + diepctl &= ~DIEPCTL_MPSMSK; + diepctl &= ~EPTYPEMSK; + diepctl |= (UDC_INT_PACKET_SIZE + | USBACTEP | DATA0PID + | (EPTYPE_INT << EPTYPE_SHIFT)); + writel(diepctl, &in_regs->diepctl); +} + +/* should be called after set configuration is received */ +static void udc_set_cfg_ctrl(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]; + u32 doepctl, doeptsiz; + + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl |= CNAK | EPENA; + writel(doepctl, &out_regs->doepctl); +} + +/* 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 *dwc_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) { + dwc_ep = get_out_ep(epnum); + doepint = dwc_otg_read_doep_intr(dwc_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 *dwc_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) { + dwc_ep = get_in_ep(epnum); + diepint = dwc_otg_read_diep_intr(dwc_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(dwc_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; + int count = 0; + + val = readl(&global_regs->grstctl); + val |= TXFFLSH; + val &= ~TXFNUM; + val |= (num << TXFNUM_SHIFT); + writel(val, &global_regs->grstctl); + + do { + val = readl(&global_regs->grstctl); + if (++count > 10000) + break; + udelay(1); + } while (val & TXFFLSH); + + /* 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 count = 0; + u32 val = 0; + + val = readl(&global_regs->grstctl); + val |= RXFFLSH; + writel(val, &global_regs->grstctl); + + do { + val = readl(&global_regs->grstctl); + if (++count > 10000) + break; + udelay(1); + } while (val & RXFFLSH); + + /* 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; + u32 gintmsk; + 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++) { + temp = readl(&out_ep_regs[i]->doepctl); + temp |= SNAK; + writel(temp, &out_ep_regs[i]->doepctl); + } + + /* Flush the NP Tx FIFO */ + dwc_otg_flush_tx_fifo(DWC_GRSTCTL_TXFNUM_ALL); + dwc_otg_flush_rx_fifo(); + writel((1 << (DAINTMASK_IN_SHIFT + 0)) + | (1 << (DAINTMASK_OUT_SHIFT + 0)), + &dev_global_regs->daintmsk); + + writel(SETUPMSK | XFERCOMPLMSK | AHBERRMSK | EPDISABLEDMSK, + &dev_global_regs->doepmsk); + + writel(INTKNTXFEMP, &dev_global_regs->diepmsk); + + gintmsk = readl(&core_global_regs->gintmsk); + gintmsk |= GOUTNAKEFF; + writel(gintmsk, &core_global_regs->gintmsk); + + /* program fifo size for ep0 */ + + writel(0x200, &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(0x2000000, &core_global_regs->gnptxfsiz); + + /* Reset Device Address */ + temp = readl(&dev_global_regs->dcfg); + temp &= ~DEVADDRMSK; + writel(temp, &dev_global_regs->dcfg); + + /* setup EP0 to receive SETUP packets */ + ep0_out_start(); + + /* Clear interrupt */ + writel(USBRESET, &core_global_regs->gintsts); + + UDCDBG("device reset in progess"); + usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + + return 1; +} + +/* + * 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) +{ + u32 temp; + 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 */ + temp = readl(&in_ep_regs->diepctl); + temp &= ~MPSMSK0; + temp |= DWC_DEP0CTL_MPS_64; + writel(temp, &in_ep_regs->diepctl); + + temp = readl(&out_ep_regs->doepctl); + /* Enable OUT EP for receive */ + temp |= EPENA; + writel(temp, &out_ep_regs->doepctl); +} + +/* + * 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) +{ + u32 gusbcfg; + struct core_global_regs *global_regs = dev_if->core_global_regs; + dwc_otg_ep0_activate(); + + gusbcfg = readl(&global_regs->gusbcfg); + gusbcfg &= ~USBTRDTIMMSK; + gusbcfg |= PHYIF_16BIT; + gusbcfg |= (9 << USBTRDTIM_SHIFT); + writel(gusbcfg, &global_regs->gusbcfg); + /* Clear interrupt */ + writel(ENUMDONE, &global_regs->gintsts); + + 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; + u32 ahbcfg, gintmsk, usbcfg; + /* Step 1: Program the GAHBCFG Register. */ + ahbcfg = DWC_NPTXEMPTYLVL_EMPTY | DWC_PTXEMPTYLVL_EMPTY; + writel(ahbcfg, &global_regs->gahbcfg); + + /* Step 2: write usbcfg regs*/ + usbcfg = readl(&global_regs->gusbcfg); + usbcfg |= SRPCAP | HNPCAP; + writel(usbcfg, &global_regs->gusbcfg); + + /* step3: write int_msk reg*/ + gintmsk = USBRESET | ENUMDONE | RXSTSQLVL | OUTEPINTR | INEPINTR; + writel(gintmsk, &global_regs->gintmsk); +} + +/* 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(); + } + +} + +void udc_set_nak(int epid) +{ + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SNAK, + &dev_if->out_ep_regs[epid]->doepctl); + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SNAK, + &dev_if->in_ep_regs[epid]->diepctl); +} + +void udc_unset_nak(int epid) +{ + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | CNAK, + &dev_if->out_ep_regs[epid]->doepctl); + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | CNAK, + &dev_if->in_ep_regs[epid]->diepctl); +} + +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; + + UDCDBGA("enable device %p, status %d", device, device->status); + + /* 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 { + serial_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; + u32 dcfg; + + /* remove soft disconnect */ + dcfg = readl(&dev_regs->dctl); + dcfg &= ~SFTDISCON; + writel(dcfg, &dev_regs->dctl); +} + +void udc_disconnect(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dcfg; + + /* soft disconnect */ + dcfg = readl(&dev_regs->dctl); + dcfg |= SFTDISCON; + writel(dcfg, &dev_regs->dctl); + 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) +{ +#if defined(CONFIG_DW_OTG_PHYINIT) + udc_phy_init(); +#endif + udc_disconnect(); + usbotg_init(); + return 0; +} diff --git a/include/usb/designware_otg.h b/include/usb/designware_otg.h new file mode 100644 index 0000000..98d016e --- /dev/null +++ b/include/usb/designware_otg.h @@ -0,0 +1,500 @@ +/* + * (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 + +/* 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) + /* 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 + /* + * 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; + /* 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 { + 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_disable(void); +void udc_connect(void); +void udc_disconnect(void); +#if defined(CONFIG_DW_OTG_PHYINIT) +void udc_phy_init(void); +#endif +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); + +#endif /* __DW_OTG_H */