[U-Boot] [PATCH V2] USB:gadget:designware USB OTG implementation
Marek Vasut
marex at denx.de
Wed Apr 4 10:23:07 CEST 2012
Dear Amit Virdi,
> From: Pratyush Anand <pratyush.anand at st.com>
>
> 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 <pratyush.anand at st.com>
> Signed-off-by: Amit Virdi <amit.virdi at st.com>
> ---
> drivers/serial/usbtty.h | 2 +
> drivers/usb/gadget/Makefile | 1 +
> drivers/usb/gadget/designware_otg.c | 990
> +++++++++++++++++++++++++++++++++++ include/usb/designware_otg.h |
> 523 ++++++++++++++++++
> 4 files changed, 1516 insertions(+), 0 deletions(-)
> 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 <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>
> diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
> index 87d1918..ede367e 100644
> --- a/drivers/usb/gadget/Makefile
> +++ b/drivers/usb/gadget/Makefile
> @@ -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
> diff --git a/drivers/usb/gadget/designware_otg.c
> b/drivers/usb/gadget/designware_otg.c new file mode 100644
> index 0000000..5af6940
> --- /dev/null
> +++ b/drivers/usb/gadget/designware_otg.c
> @@ -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 at st.com.
> + *
> + * (C) Copyright 2012
> + * Amit Virdi, ST Micoelectronics, amit.virdi at 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);
Thinking of this, will this really handle unaligned access of length for example
5 ?
> + }
> +}
> +
> +/* 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) {
if (!endpoint)
return;
... code ...
> + 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*/
missing space before ending comment
> + 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;
DTTO, will this handle unaligned xfer of size > 4 properly ?
> + }
> +
> + 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) {
i++ ... ++i has no meaning here (not even a compiler hint).
> + 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
> + */
Otherwise, I think you did a pretty decent job, one more round and I'm queueing
this ;-)
More information about the U-Boot
mailing list