[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