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