All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marek Vasut <marex@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH V2] USB:gadget:designware USB OTG implementation
Date: Wed, 4 Apr 2012 10:23:07 +0200	[thread overview]
Message-ID: <201204041023.07386.marex@denx.de> (raw)
In-Reply-To: <27065c311969ab4ce9d10fba7921ed9f47970d6e.1333462753.git.amit.virdi@st.com>

Dear Amit Virdi,

> From: Pratyush Anand <pratyush.anand@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@st.com>
> Signed-off-by: Amit Virdi <amit.virdi@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 ;-)

  reply	other threads:[~2012-04-04  8:23 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-03 14:20 [U-Boot] [PATCH V2] USB:gadget:designware USB OTG implementation Amit Virdi
2012-04-04  8:23 ` Marek Vasut [this message]
2012-04-09  8:38   ` Amit Virdi
2012-04-09  8:49     ` Marek Vasut
2012-10-16  6:24     ` Marek Vasut
2012-10-16  6:33       ` Amit Virdi
2012-10-16  6:35         ` Marek Vasut

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=201204041023.07386.marex@denx.de \
    --to=marex@denx.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.