Dear Amit Virdi,

> From: Pratyush Anand <pratyush.an...@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.an...@st.com>
> Signed-off-by: Amit Virdi <amit.vi...@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.an...@st.com.
> + *
> + * (C) Copyright 2012
> + * Amit Virdi, ST Micoelectronics, amit.vi...@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 ;-)

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to