Message ID | 1331121424-19508-1-git-send-email-amit.virdi@st.com |
---|---|
State | Superseded |
Delegated to: | Marek Vasut |
Headers | show |
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 | 1064 > +++++++++++++++++++++++++++++++++++ include/usb/designware_otg.h | > 527 +++++++++++++++++ > 4 files changed, 1594 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 60347d7..6731c38 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..b5dd898 > --- /dev/null > +++ b/drivers/usb/gadget/designware_otg.c > @@ -0,0 +1,1064 @@ > +/* > + * Based on drivers/usb/gadget/designware_otg.c > + * Synopsys DW OTG Device bus interface driver > + * > + * (C) Copyright 2011 > + * Pratyush Anand, ST Micoelectronics, pratyush.anand@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 > + */ > +/* temp def: will be removed TBD*/ temp remove ;-) > +#undef APG_BOARD 1 > + > +#ifndef APG_BOARD dead code? > +#include <common.h> > +#include <asm/io.h> > +#include <usbdevice.h> > +#include "ep0.h" > +#include <usb/designware_otg.h> > +#include <asm/arch/hardware.h> > +#else > +#include "types.h" > +#include <designware_otg.h> > +#include <usbdevice.h> > +#endif > + > +#define UDC_INIT_MDELAY 80 /* Device settle delay */ > + > +/* Some kind of debugging output... */ > +#ifndef DEBUG_DWUSBTTY > +#define UDCDBG(str) > +#define UDCDBGA(fmt, args...) > +#else > +#define UDCDBG(str) serial_printf(str "\n") > +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) > +#endif debug() > + > +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 > + > +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. > + */ > +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)) { rename _bytes to bytes, fix globally > + unaligned = readl(fifo); > + memcpy(data_buff, &unaligned, _bytes); > + } else { > + for (i = 0; i < word_count; i++, data_buff++) > + *data_buff = readl(fifo); > + } > +} > + > +/* 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) { > + 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; > + 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*/ > + temp = readl(&in_ep_regs->diepctl); > + temp |= (EPENA | CNAK); > + writel(temp, &in_ep_regs->diepctl); > + > + /* clear TX Fifo Empty intr*/ > + writel(NPTXFEMPTY, &core_global_regs->gintsts); > + > + temp = readl(&core_global_regs->gintmsk); > + temp |= NPTXFEMPTY; > + writel(temp, &core_global_regs->gintmsk); > + > + while (!(readl(&core_global_regs->gintsts) & NPTXFEMPTY)) > + ; No endless loops please > + > + /* 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; > + } > + > + writel(NPTXFEMPTY, &core_global_regs->gintsts); > + > + /* check for transfer completion*/ > + while (!(readl(&in_ep_regs->diepint) & XFERCOMPL)) > + ; dtto > + > + writel(XFERCOMPL, &in_ep_regs->diepint); > + > + temp = readl(&core_global_regs->gintmsk); > + temp &= ~NPTXFEMPTY; > + writel(temp, &core_global_regs->gintmsk); Use clrsetbits_le32() and friends in cases like that > +} > + > +/* 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; > + > + } else if (urb && (urb->actual_length == 0)) { > + /* udc_set_nak(ep); */ remove deadcode please > + } > + } > +} > + > +/* This function returns pointer to out ep struct with number num */ > +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; You don't need the else branch below, it'll make code more readable: if (num == 0) return; ... code ... > + } else { > + for (i = 0; i < num_out_eps; ++i) { > + 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 */ > +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; > + } else { > + 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 > + */ > +static int dwc_otg_pcd_handle_rx_status_q_level_intr(void) > +{ > + struct core_global_regs *global_regs = dev_if->core_global_regs; > + struct dwc_pcd *pcd = &dev_if->pcd; > + u32 gintmsk; > + u32 status; > + struct dwc_ep *ep; > + u32 bcnt; > + > + /* Disable the Rx Status Queue Level interrupt */ > + gintmsk = readl(&global_regs->gintmsk); > + gintmsk &= ~RXSTSQLVL; > + writel(gintmsk, &global_regs->gintmsk); > + > + /* Get the Status from the top of the FIFO */ > + status = readl(&global_regs->grxstsp); > + /* Get pointer to EP structure */ > + ep = get_out_ep((status & EPNUMMSK) >> EPNUM_SHIFT); > + bcnt = (status & BCNTMSK) >> BCNT_SHIFT; > + > + switch ((status & PKTSTSMSK) >> PKTSTS_SHIFT) { > + case DWC_DSTS_GOUT_NAK: > + break; > + case DWC_STS_DATA_UPDT: > + if (bcnt) > + dw_udc_epn_rx(ep, bcnt); > + break; > + case DWC_STS_XFER_COMP: > + break; > + case DWC_DSTS_SETUP_COMP: > + break; > + case DWC_DSTS_SETUP_UPDT: > + dwc_otg_read_setup_packet((u32 *)pcd->req); > + break; > + default: > + break; > + } > + > + /* Enable the Rx Status Queue Level interrupt */ > + gintmsk = readl(&global_regs->gintmsk); > + gintmsk |= RXSTSQLVL; > + writel(gintmsk, &global_regs->gintmsk); > + > + /* Clear interrupt */ > + writel(RXSTSQLVL, &global_regs->gintsts); > + > + return 1; > +} > + > +/* > + * This function starts the Zero-Length Packet for the IN status phase > + * of a 2 stage control transfer. > + */ > +static void do_setup_in_status_phase(struct device_if *dev_if) > +{ > + struct device_out_ep_regs *out_regs = > + dev_if->out_ep_regs[0]; > + u32 doepctl, doeptsiz; > + doeptsiz = 0; > + doeptsiz |= (1 << PKTCNT_SHIFT); > + writel(doeptsiz, &out_regs->doeptsiz); > + doepctl = readl(&out_regs->doepctl); > + doepctl |= (CNAK | EPENA); > + writel(doepctl, &out_regs->doepctl); clrsetbits_le32 ... fix globally please. > +} > + > +static void udc_set_stall(int epid, int dir) > +{ if (dir) reg = ... else reg = ... writel(readl(reg) | ..., reg); might be more readable ;-) > + if (dir) > + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SSTALL, > + &dev_if->in_ep_regs[epid]->diepctl); > + else > + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SSTALL, > + &dev_if->out_ep_regs[epid]->doepctl); > +} > +/* > + * This function handles EP0 Control transfers. > + * > + * The state of the control tranfers are tracked in ep0state > + * > + * A flag set indicates that it is not the first packet, so do not > + * process setup data now. it has alreday been processed, just send the > + * next data packet > + */ > +void handle_ep0(int in_flag) > +{ > + struct dwc_pcd *pcd = &dev_if->pcd; > + struct dwc_ep *ep0 = &pcd->ep0; > + struct usb_device_request *ctrl = pcd->req; > + > + /* handle inepint, only when more than 64 bytes to transfer*/ > + if (in_flag & !ep0_urb->actual_length) > + return; > + > + if (!ep0_urb->actual_length) { > + if (ep0_recv_setup(ep0_urb)) { > + udc_set_stall(0, ctrl->bmRequestType & USB_DIR_IN); > + return; > + } > + ep0->xfer_buff = (u8 *)ep0_urb->buffer; > + } else > + ep0->xfer_buff += EP0_MAX_PACKET_SIZE; > + > + if (ep0_urb->actual_length <= EP0_MAX_PACKET_SIZE) { > + ep0->xfer_len = ep0_urb->actual_length; > + ep0_urb->actual_length = 0; > + } else { > + ep0->xfer_len = EP0_MAX_PACKET_SIZE; > + ep0_urb->actual_length -= EP0_MAX_PACKET_SIZE; > + } > + > + if (ctrl->bmRequestType & USB_DIR_IN) { > + dwc_otg_ep_write_packet(ep0); > + if (!ep0_urb->actual_length) > + do_setup_in_status_phase(dev_if); > + } else { > + if (!ctrl->wLength) > + dwc_otg_ep_write_packet(ep0); > + else > + udc_set_stall(0, ctrl->bmRequestType & USB_DIR_OUT); > + } > +} > + > +/* > + * This function reads the Device All Endpoints Interrupt register and > + * returns the OUT endpoint interrupt bits. > + */ > +static u32 dwc_otg_read_dev_all_out_ep_intr(void) > +{ > + u32 v; > + > + v = readl(&dev_if->dev_global_regs->daint) & > + readl(&dev_if->dev_global_regs->daintmsk); > + return (v & 0xffff0000) >> 16; simple return v >> 16 doesn't work? > +} > + > +/* > + * This function reads the Device All Endpoints Interrupt register and > + * returns the IN endpoint interrupt bits. > + */ > +static u32 dwc_otg_read_dev_all_in_ep_intr(void) > +{ > + u32 v; > + > + v = readl(&dev_if->dev_global_regs->daint) & > + readl(&dev_if->dev_global_regs->daintmsk); > + return v & 0xffff; > +} > + > +/* This function returns the Device OUT EP Interrupt register */ > +static u32 dwc_otg_read_doep_intr(struct dwc_ep *ep) > +{ > + u32 v; > + > + v = readl(&dev_if->out_ep_regs[ep->num]->doepint) & > + readl(&dev_if->dev_global_regs->doepmsk); > + return v; > +} > + > +/*This function returns the Device IN EP Interrupt register */ > +static u32 dwc_otg_read_diep_intr(struct dwc_ep *ep) > +{ > + u32 v; > + > + v = readl(&dev_if->in_ep_regs[ep->num]->diepint) & > + readl(&dev_if->dev_global_regs->diepmsk); > + return v; > +} > + > +/* > + * This function configures EPO to receive SETUP packets. > + * > + * Program the following fields in the endpoint specific registers for > Control + * OUT EP 0, in order to receive a setup packet: > + * > + * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup > packets) + * > + * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back > setup + * packets) > + * > + * In DMA mode, DOEPDMA0 Register with a memory address to store any setup > + * packets received > + */ > +static void ep0_out_start(void) > +{ > + u32 temp; > + > + /* program transfer size*/ > + temp = 8 * 3; > + /* program packet count*/ > + temp |= PKTCNT; > + /* program setup packet count */ > + temp |= (3 << SUPCNT_SHIFT); > + writel(temp, &dev_if->out_ep_regs[0]->doeptsiz); > +} > + > +/* should be called after set address is received */ > +void udc_set_address_controller(u32 address) > +{ > + u32 dcfg; > + > + dcfg = readl(&dev_if->dev_global_regs->dcfg); > + dcfg &= ~DEVADDRMSK; > + dcfg |= address << DEVADDR_SHIFT; > + writel(dcfg, &dev_if->dev_global_regs->dcfg); > + > + usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0); > +} > + > +/* should be called after set configuration is received */ > +static void dwc_otg_bulk_out_activate(void) > +{ > + struct device_out_ep_regs *out_regs = > + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; > + struct device_global_regs *dev_global_regs > + = dev_if->dev_global_regs; > + u32 doepctl, doeptsiz, daint; > + > + daint = readl(&dev_global_regs->daintmsk); > + daint |= 1 << (UDC_OUT_ENDPOINT + DAINTMASK_OUT_SHIFT); > + writel(daint, &dev_global_regs->daintmsk); > + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; > + doeptsiz |= (1 << PKTCNT_SHIFT); > + writel(doeptsiz, &out_regs->doeptsiz); > + doepctl = readl(&out_regs->doepctl); > + doepctl &= ~DOEPCTL_MPSMSK; > + doepctl &= ~EPTYPEMSK; > + doepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE | > + CNAK | EPENA | USBACTEP | DATA0PID > + | (EPTYPE_BULK << EPTYPE_SHIFT)); > + writel(doepctl, &out_regs->doepctl); > +} > + > +/* should be called after set configuration is received */ > +static void dwc_otg_bulk_in_activate(void) > +{ > + struct device_in_ep_regs *in_regs = > + dev_if->in_ep_regs[UDC_IN_ENDPOINT]; > + struct device_global_regs *dev_global_regs > + = dev_if->dev_global_regs; > + u32 diepctl, daint; > + > + daint = readl(&dev_global_regs->daintmsk); > + daint |= 1 << (UDC_IN_ENDPOINT + DAINTMASK_IN_SHIFT); > + writel(daint, &dev_global_regs->daintmsk); > + > + diepctl = readl(&in_regs->diepctl); > + diepctl &= ~DIEPCTL_MPSMSK; > + diepctl &= ~EPTYPEMSK; > + diepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE > + | USBACTEP | DATA0PID > + | (EPTYPE_BULK << EPTYPE_SHIFT)); > + writel(diepctl, &in_regs->diepctl); > +} > + > +static void dwc_otg_int_in_activate(void) > +{ > + struct device_in_ep_regs *in_regs = > + dev_if->in_ep_regs[UDC_INT_ENDPOINT]; > + struct device_global_regs *dev_global_regs > + = dev_if->dev_global_regs; > + u32 diepctl, daint; > + > + daint = readl(&dev_global_regs->daintmsk); > + daint |= 1 << (UDC_INT_ENDPOINT + DAINTMASK_IN_SHIFT); > + writel(daint, &dev_global_regs->daintmsk); > + > + diepctl = readl(&in_regs->diepctl); > + diepctl &= ~DIEPCTL_MPSMSK; > + diepctl &= ~EPTYPEMSK; > + diepctl |= (UDC_INT_PACKET_SIZE > + | USBACTEP | DATA0PID > + | (EPTYPE_INT << EPTYPE_SHIFT)); > + writel(diepctl, &in_regs->diepctl); lot of clrsetbits_le32 will appear in these parts ;-) > +} > + > +/* should be called after set configuration is received */ > +void udc_set_configuration_controller(u32 config) > +{ > + dwc_otg_bulk_out_activate(); > + dwc_otg_bulk_in_activate(); > + dwc_otg_int_in_activate(); > + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); > +} > + > +/* should be called to receive next packet */ > +static void dwc_otg_bulk_out_enable(void) > +{ > + struct device_out_ep_regs *out_regs = > + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; > + u32 doepctl, doeptsiz; > + > + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; > + doeptsiz |= (1 << PKTCNT_SHIFT); > + writel(doeptsiz, &out_regs->doeptsiz); > + doepctl = readl(&out_regs->doepctl); > + doepctl |= CNAK | EPENA; > + writel(doepctl, &out_regs->doepctl); > +} > + > +/* This interrupt indicates that an OUT EP has a pending Interrupt. */ > + > +static int dwc_otg_pcd_handle_out_ep_intr(void) > +{ > + u32 ep_intr; > + u32 doepint; > + u32 epnum = 0; > + struct dwc_ep *dwc_ep; > + struct device_out_ep_regs **out_ep_regs > + = dev_if->out_ep_regs; > + > + /* Read in the device interrupt bits */ > + ep_intr = dwc_otg_read_dev_all_out_ep_intr(); > + while (ep_intr) { > + if (ep_intr & 0x1) { > + dwc_ep = get_out_ep(epnum); do you even use this variable? Don't you get gcc4.6 warnings ? > + doepint = dwc_otg_read_doep_intr(dwc_ep); > + > + /* Transfer complete */ > + if (doepint & XFERCOMPL) { > + /* Clear xfercompl */ > + writel(XFERCOMPL, &out_ep_regs[epnum]->doepint); > + if (!epnum) > + ep0_out_start(); > + else if (epnum == UDC_OUT_ENDPOINT) > + dwc_otg_bulk_out_enable(); > + } > + /* Setup Phase Done (control EPs) */ > + if (doepint & SETUP) { > + writel(SETUP, &out_ep_regs[epnum]->doepint); > + handle_ep0(0); > + } > + } > + epnum++; > + ep_intr >>= 1; > + } > + return 1; > +} > + > +/* This interrupt indicates that an IN EP has a pending Interrupt. */ > + > +static int dwc_otg_pcd_handle_in_ep_intr(void) > +{ > + u32 ep_intr; > + u32 diepint; > + u32 epnum = 0; > + struct dwc_ep *dwc_ep; > + struct device_in_ep_regs **in_ep_regs > + = dev_if->in_ep_regs; > + > + /* Read in the device interrupt bits */ > + ep_intr = dwc_otg_read_dev_all_in_ep_intr(); > + while (ep_intr) { > + if (ep_intr & 0x1) { if (!...) continue; ... code ... Lessers the depth of code. > + dwc_ep = get_in_ep(epnum); > + diepint = dwc_otg_read_diep_intr(dwc_ep); > + > + /* IN token received when txfifo empty */ > + if (diepint & INTKNTXFEMP) { > + /* Clear xfercompl */ > + writel(INTKNTXFEMP, > + &in_ep_regs[epnum]->diepint); > + if (!epnum) > + handle_ep0(1); > + else if (epnum == UDC_IN_ENDPOINT) > + dw_udc_epn_tx(dwc_ep); > + } > + } > + epnum++; > + ep_intr >>= 1; > + } > + return 1; > +} > + > +static void dwc_otg_flush_tx_fifo(const int num) > +{ > + struct core_global_regs *global_regs = dev_if->core_global_regs; > + u32 val = 0; > + int count = 0; > + > + val = readl(&global_regs->grstctl); > + val |= TXFFLSH; > + val &= ~TXFNUM; > + val |= (num << TXFNUM_SHIFT); > + writel(val, &global_regs->grstctl); > + > + do { > + val = readl(&global_regs->grstctl); > + if (++count > 10000) > + break; > + udelay(1); > + } while (val & TXFFLSH); > + > + /* Wait for 3 PHY Clocks */ > + udelay(1); > +} > + > +static void dwc_otg_flush_rx_fifo(void) > +{ > + struct core_global_regs *global_regs = dev_if->core_global_regs; > + int count = 0; > + u32 val = 0; > + > + val = readl(&global_regs->grstctl); > + val |= RXFFLSH; > + writel(val, &global_regs->grstctl); > + > + do { > + val = readl(&global_regs->grstctl); > + if (++count > 10000) > + break; > + udelay(1); WATCHDOG_RESET() instead of udelay() also, call the variable count "timeout" and make it count down, it'll be more readable (nitpick) > + } while (val & RXFFLSH); > + > + /* Wait for 3 PHY Clocks */ > + udelay(1); > +} > + > +/* > + * This interrupt occurs when a USB Reset is detected. When the USB Reset > + * Interrupt occurs the device state is set to DEFAULT and the EP0 state > is set + * to IDLE. > + * > + */ > +static int dwc_otg_pcd_handle_usb_reset_intr(void) > +{ > + u32 temp; > + u32 i; > + u32 gintmsk; > + struct device_out_ep_regs **out_ep_regs > + = dev_if->out_ep_regs; > + struct device_global_regs *dev_global_regs > + = dev_if->dev_global_regs; > + struct core_global_regs *core_global_regs > + = dev_if->core_global_regs; > + /* Set NAK for all OUT EPs */ > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + temp = readl(&out_ep_regs[i]->doepctl); > + temp |= SNAK; > + writel(temp, &out_ep_regs[i]->doepctl); > + } > + > + /* Flush the NP Tx FIFO */ > + dwc_otg_flush_tx_fifo(DWC_GRSTCTL_TXFNUM_ALL); > + dwc_otg_flush_rx_fifo(); > + writel((1 << (DAINTMASK_IN_SHIFT + 0)) > + | (1 << (DAINTMASK_OUT_SHIFT + 0)), > + &dev_global_regs->daintmsk); > + > + writel(SETUPMSK | XFERCOMPLMSK | AHBERRMSK | EPDISABLEDMSK, > + &dev_global_regs->doepmsk); > + > + writel(INTKNTXFEMP, &dev_global_regs->diepmsk); > + > + gintmsk = readl(&core_global_regs->gintmsk); > + gintmsk |= GOUTNAKEFF; > + writel(gintmsk, &core_global_regs->gintmsk); > + > + /* program fifo size for ep0 */ > + > + writel(0x200, &core_global_regs->grxfsiz); > + > + temp = readl(&dev_if->in_ep_regs[0]->diepctl); > + temp &= 0xFFC3FFFF; /* TxFNumBF = 0, bits 25:22 */ > + writel(temp, &dev_if->in_ep_regs[0]->diepctl); > + > + temp = readl(&core_global_regs->gnptxfsiz); > + > + writel(0x2000000, &core_global_regs->gnptxfsiz); Magic value? > + > + /* Reset Device Address */ > + temp = readl(&dev_global_regs->dcfg); > + temp &= ~DEVADDRMSK; > + writel(temp, &dev_global_regs->dcfg); > + > + /* setup EP0 to receive SETUP packets */ > + ep0_out_start(); > + > + /* Clear interrupt */ > + writel(USBRESET, &core_global_regs->gintsts); > + > + UDCDBG("device reset in progess"); > + usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0); > + > + return 1; > +} > + > +/* > + * 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) > +{ > + u32 temp; > + 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 */ > + temp = readl(&in_ep_regs->diepctl); > + temp &= ~MPSMSK0; > + temp |= DWC_DEP0CTL_MPS_64; > + writel(temp, &in_ep_regs->diepctl); > + > + temp = readl(&out_ep_regs->doepctl); > + /* Enable OUT EP for receive */ > + temp |= EPENA; > + writel(temp, &out_ep_regs->doepctl); > +} > + > +/* > + * Read the device status register and set the device speed in the > + * data structure. > + * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. > + */ > +static int dwc_otg_pcd_handle_enum_done_intr(void) > +{ > + u32 gusbcfg; > + struct core_global_regs *global_regs = dev_if->core_global_regs; > + dwc_otg_ep0_activate(); > + > + gusbcfg = readl(&global_regs->gusbcfg); > + gusbcfg &= ~USBTRDTIMMSK; > + gusbcfg |= PHYIF_16BIT; > + gusbcfg |= (9 << USBTRDTIM_SHIFT); Magic? > + writel(gusbcfg, &global_regs->gusbcfg); > + /* Clear interrupt */ > + writel(ENUMDONE, &global_regs->gintsts); > + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); > + return 1; > +} > + > +static u32 dwc_otg_read_core_intr(void) > +{ > + return readl(&dev_if->core_global_regs->gintsts) & > + readl(&dev_if->core_global_regs->gintmsk); > +} > + > +static void dwc_otg_init(const void *reg_base) > +{ > + struct dwc_pcd *pcd = &dev_if->pcd; > + u32 offset; > + u32 i; > + > + dev_if->core_global_regs = (struct core_global_regs *) reg_base; > + dev_if->dev_global_regs = (struct device_global_regs *) ((u32)reg_base + > + DWC_DEV_GLOBAL_REG_OFFSET); > + > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + offset = i * DWC_EP_REG_OFFSET; > + > + dev_if->in_ep_regs[i] = (struct device_in_ep_regs *) > + ((u32)reg_base + DWC_DEV_IN_EP_REG_OFFSET + offset); > + > + dev_if->out_ep_regs[i] = (struct device_out_ep_regs *) > + ((u32)reg_base + DWC_DEV_OUT_EP_REG_OFFSET + offset); > + } > + > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + dev_if->data_fifo[i] = > + (u32 *) ((u32)reg_base + DWC_OTG_DATA_FIFO_OFFSET + > + (i * DWC_OTG_DATA_FIFO_SIZE)); > + } > + > + dev_if->speed = 0; /* unknown */ > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + pcd->in_ep[i].num = i; > + pcd->out_ep[i].num = i; > + } > +} > + > +/* > + * This function initializes the DWC_otg controller registers and prepares > the + * core for device mode > + */ > +static void dwc_otg_core_init(void) > +{ > + struct core_global_regs *global_regs = dev_if->core_global_regs; > + u32 ahbcfg, gintmsk, usbcfg; > + /* Step 1: Program the GAHBCFG Register. */ > + ahbcfg = DWC_NPTXEMPTYLVL_EMPTY | DWC_PTXEMPTYLVL_EMPTY; > + writel(ahbcfg, &global_regs->gahbcfg); > + > + /* Step 2: write usbcfg regs*/ > + usbcfg = readl(&global_regs->gusbcfg); > + usbcfg |= SRPCAP | HNPCAP; > + writel(usbcfg, &global_regs->gusbcfg); > + > + /* step3: write int_msk reg*/ > + gintmsk = USBRESET | ENUMDONE | RXSTSQLVL | OUTEPINTR | INEPINTR; > + writel(gintmsk, &global_regs->gintmsk); > +} > + > +/* Switch on the UDC */ > +static void usbotg_init(void) > +{ > + udc_device = NULL; > +#ifdef APG_BOARD > + dwc_otg_init((void *)0x11000000); What the heck ? :-O Dead code ? > +#else > + dwc_otg_init((void *)CONFIG_SYS_USBD_BASE); > +#endif > + > + /* Initialize the DWC_otg core. */ > + dwc_otg_core_init(); > + > +} > + > +void udc_irq(void) > +{ > + u32 status; > + > + status = dwc_otg_read_core_intr(); > + while (status) { > + if (status & USBRESET) > + dwc_otg_pcd_handle_usb_reset_intr(); > + if (status & ENUMDONE) > + dwc_otg_pcd_handle_enum_done_intr(); > + if (status & RXSTSQLVL) > + dwc_otg_pcd_handle_rx_status_q_level_intr(); > + if (status & OUTEPINTR) > + dwc_otg_pcd_handle_out_ep_intr(); > + if (status & INEPINTR) > + dwc_otg_pcd_handle_in_ep_intr(); > + status = dwc_otg_read_core_intr(); > + } > + > +} > + > +void udc_set_nak(int epid) > +{ > + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SNAK, > + &dev_if->out_ep_regs[epid]->doepctl); > + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SNAK, > + &dev_if->in_ep_regs[epid]->diepctl); Shouldn't you put this together with previous such code that operated with exeactly the same registers ? maybe even merge these three functions together. And(!) make everything that you don't need outside static please. > +} > + > +void udc_unset_nak(int epid) > +{ > + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | CNAK, > + &dev_if->out_ep_regs[epid]->doepctl); > + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | CNAK, > + &dev_if->in_ep_regs[epid]->diepctl); > +} > + > +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) > +{ > + udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK); > + return 0; > +} > + > +static void udc_enable(struct usb_device_instance *device) > +{ > + struct dwc_pcd *pcd = &dev_if->pcd; > + > + UDCDBGA("enable device %p, status %d", device, device->status); > + > + /* Save the device structure pointer */ > + udc_device = device; > + > + /* Setup ep0 urb */ > + if (!ep0_urb) { > + ep0_urb = > + usbd_alloc_urb(udc_device, > + udc_device->bus->endpoint_array); > + pcd->req = > + (struct usb_device_request *)&ep0_urb->device_request; > + pcd->ep0.xfer_buff = (u8 *)ep0_urb->buffer; > + } else { > +#ifndef APG_BOARD > + serial_printf("udc_enable: ep0_urb already allocated %p\n", > + ep0_urb); Use printf(), not serial_printf(), globally > +#endif > + } > +} > + > +void udc_connect(void) > +{ > + struct device_global_regs *dev_regs = dev_if->dev_global_regs; > + u32 dcfg; > + > + /* remove soft disconnect */ > + dcfg = readl(&dev_regs->dctl); > + dcfg &= ~SFTDISCON; > + writel(dcfg, &dev_regs->dctl); > +} > + > +void udc_disconnect(void) > +{ > + struct device_global_regs *dev_regs = dev_if->dev_global_regs; > + u32 dcfg; > + > + /* soft disconnect */ > + dcfg = readl(&dev_regs->dctl); > + dcfg |= SFTDISCON; > + writel(dcfg, &dev_regs->dctl); > + udelay(150); > +} > + > +void udc_startup_events(struct usb_device_instance *device) > +{ > + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ > + usbd_device_event_irq(device, DEVICE_INIT, 0); > + > + /* > + * The DEVICE_CREATE event puts the USB device in the state > + * STATE_ATTACHED. > + */ > + usbd_device_event_irq(device, DEVICE_CREATE, 0); > + > + /* > + * Some USB controller driver implementations signal > + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. > + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, > + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. > + * The DW USB client controller has the capability to detect when the > + * USB cable is connected to a powered USB bus, so we will defer the > + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later. > + */ > + > + udc_enable(device); > + > +} > + > +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, > + struct usb_endpoint_instance *endpoint) > +{ > + /* > + * Nothing to do here. Hob of this function has laready been > + * done during init. > + */ > +} > + > +int is_usbd_high_speed(void) > +{ > + struct device_global_regs *dev_regs = dev_if->dev_global_regs; > + u32 dsts; > + > + dsts = readl(&dev_regs->dsts); > + dsts &= ENUMSPDMSK; > + if (dsts == DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ) > + return 1; > + else > + return 0; > +} > + > +int udc_init(void) > +{ > + phy_init(); > + udc_disconnect(); > + usbotg_init(); > + return 0; > +} > diff --git a/include/usb/designware_otg.h b/include/usb/designware_otg.h > new file mode 100644 > index 0000000..d7b686b > --- /dev/null > +++ b/include/usb/designware_otg.h > @@ -0,0 +1,527 @@ > +/* > + * (C) Copyright 2011 > + * Pratyush Anand, ST Micoelectronics, pratyush.anand@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 > + */ > + > +#ifndef __DW_OTG_H > +#define __DW_OTG_H > +/* temp def: will be removed TBD */ > +#undef APH_BOARD 1 > +#ifdef APG_BOARD > +#include "types.h" > +#define CONFIG_USBD_HS > +#define CONFIG_DW_OTG > +#endif > + > +#include "usbdevice.h" > +/* USBTTY definitions */ > +#define EP0_MAX_PACKET_SIZE 64 > +#define UDC_INT_ENDPOINT 1 > +#define UDC_INT_PACKET_SIZE 64 > +#define UDC_OUT_ENDPOINT 2 > +#define UDC_BULK_PACKET_SIZE 512 > +#if defined(CONFIG_USBD_HS) > +#define UDC_BULK_HS_PACKET_SIZE 512 > +#endif > +#define UDC_IN_ENDPOINT 3 > +#define UDC_OUT_PACKET_SIZE 64 > +#define UDC_IN_PACKET_SIZE 64 > + > +/* UDC endpoint definitions */ > +#define UDC_EP0 0 > +#define UDC_EP1 1 > +#define UDC_EP2 2 > +#define UDC_EP3 3 > + > +#define CMD_SIZE 12 > +/* OTG Register Definitions */ > + > +/* > + * The application interfaces with the HS OTG core by reading from and > + * writing to the Control and Status Register (CSR) space through the > + * AHB Slave interface. These registers are 32 bits wide, and the > + * addresses are 32-bit-block aligned. > + * CSRs are classified as follows: > + * - Core Global Registers > + * - Device Mode Registers > + * - Device Global Registers > + * - Device Endpoint Specific Registers > + * - Host Mode Registers > + * - Host Global Registers > + * - Host Port CSRs > + * - Host Channel Specific Registers > + * > + * Only the Core Global registers can be accessed in both Device and > + * Host modes. When the HS OTG core is operating in one mode, either > + * Device or Host, the application must not access registers from the > + * other mode. When the core switches from one mode to another, the > + * registers in the new mode of operation must be reprogrammed as they > + * would be after a power-on reset. > + */ > + > +/* > + * DWC_otg Core registers. The core_global_regs structure defines the > + * size and relative field offsets for the Core Global registers. > + */ > +struct core_global_regs { > + /* OTG Control and Status Register. Offset: 000h */ > + u32 gotgctl; > + /* OTG Interrupt Register. Offset: 004h */ > + u32 gotgint; > + /* Core AHB Configuration Register. Offset: 008h */ > + u32 gahbcfg; > + > +#define DWC_GLBINTRMASK 0x0001 > +#define DWC_DMAENABLE 0x0020 > +#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 > +#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 > +#define DWC_PTXEMPTYLVL_EMPTY 0x0100 > +#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 > + > + /* Core USB Configuration Register. Offset: 00Ch */ > + u32 gusbcfg; > +#define PHYIF_16BIT (1 << 3) > +#define SRPCAP (1 << 8) > +#define HNPCAP (1 << 9) > +#define TERM_SEL_DL_PULSE (1 << 22) > +#define USBTRDTIM_SHIFT 10 > +#define USBTRDTIMMSK (0xF << USBTRDTIM_SHIFT) > + /* Core Reset Register. Offset: 010h */ > + u32 grstctl; > +#define DWC_GRSTCTL_TXFNUM_ALL 0x10 > +#define CSFTRST (1 << 0) > +#define INTKNQFLSH (1 << 3) > +#define RXFFLSH (1 << 4) > +#define TXFFLSH (1 << 5) Fix indent ... here and everywhere please > +#define TXFNUM_SHIFT 6 > +#define TXFNUM (0x1F << TXFNUM_SHIFT) > +#define AHBIDLE ((u32)1 << 31) > + /* Core Interrupt Register. Offset: 014h */ > + u32 gintsts; > +#define RXSTSQLVL (1 << 4) > +#define NPTXFEMPTY (1 << 5) > +#define GOUTNAKEFF (1 << 7) > +#define USBRESET (1 << 12) > +#define ENUMDONE (1 << 13) > +#define INEPINTR (1 << 18) > +#define OUTEPINTR (1 << 19) > + /* Core Interrupt Mask Register. Offset: 018h */ > + u32 gintmsk; > + /* > + * Receive Status Queue Read Register > + * (Read Only) Offset: 01Ch > + */ > + u32 grxstsr; > + /* > + * Receive Status Queue Read & POP Register > + * (Read Only) Offset: 020h > + */ > + u32 grxstsp; > +#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */ > +#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */ > +#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */ > +#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */ > +#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */ > +#define EPNUM_SHIFT 0 > +#define EPNUMMSK (0xF << EPNUM_SHIFT) > +#define BCNT_SHIFT 4 > +#define BCNTMSK (0x7FF << BCNT_SHIFT) > +#define PKTSTS_SHIFT 17 > +#define PKTSTSMSK (0xF << PKTSTS_SHIFT) > + /* Receive FIFO Size Register. Offset: 024h */ > + u32 grxfsiz; > +#define dwc_param_dev_rx_fifo_size_default 1064 > + /* Non Periodic Transmit FIFO Size Register. Offset: 028h */ > + u32 gnptxfsiz; > +#define dwc_param_dev_nperio_tx_fifo_size_default 1024 > + /* > + * Non Periodic Transmit FIFO/Queue Status Register > + * (Read Only). Offset: 02Ch > + */ > + u32 gnptxsts; > +#define NPTXQSPCAVAIL_SHIFT 16 > +#define NPTXQSPCAVAILMSK (0xFF << NPTXQSPCAVAIL_SHIFT) > +#define NPTXFSPCAVAIL_SHIFT 0 > +#define NPTXFSPCAVAILMSK (0xFFFF << NPTXFSPCAVAIL_SHIFT) > + /* I2C Access Register. Offset: 030h */ > + u32 gi2cctl; > + /* PHY Vendor Control Register. Offset: 034h */ > + u32 gpvndctl; > + /* General Purpose Input/Output Register. Offset: 038h */ > + u32 ggpio; > + /* User ID Register. Offset: 03Ch */ > + u32 guid; > + /* Synopsys ID Register (Read Only). Offset: 040h */ > + u32 gsnpsid; > + /* User HW Config1 Register (Read Only). Offset: 044h */ > + u32 ghwcfg1; > + /* User HW Config2 Register (Read Only). Offset: 048h */ > + > + u32 ghwcfg2; > +#define DWC_SLAVE_ONLY_ARCH 0 > +#define DWC_EXT_DMA_ARCH 1 > +#define DWC_INT_DMA_ARCH 2 > + > +#define DWC_MODE_HNP_SRP_CAPABLE 0 > +#define DWC_MODE_SRP_ONLY_CAPABLE 1 > +#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 > +#define DWC_MODE_SRP_CAPABLE_DEVICE 3 > +#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 > +#define DWC_MODE_SRP_CAPABLE_HOST 5 > +#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 > +#define DYNAMIC_FIFO (1 << 19) > +#define NUM_DEV_EP_SHIFT 10 > +#define NUM_DEV_EP (0xF << NUM_DEV_EP_SHIFT) > +#define HSPHYTYPE_SHIFT 6 > +#define HSPHYTYPEMSK (3 << HSPHYTYPE_SHIFT) > +#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 > +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 > +#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 > +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 > +#define TKNQDEPTH_SHIFT 26 > +#define TKNQDEPTHMSK (0x1F << TKNQDEPTH_SHIFT) > + > + /* User HW Config3 Register (Read Only). Offset: 04Ch */ > + u32 ghwcfg3; > +#define DFIFO_DEPTH_SHIFT 16 > +#define DFIFO_DEPTH ((u32)0xFFFF << DFIFO_DEPTH_SHIFT) > + /* User HW Config4 Register (Read Only). Offset: 050h */ > + u32 ghwcfg4; > +#define NUM_DEV_PERIO_IN_EP_SHIFT 0 > +#define NUM_DEV_PERIO_IN_EP (0xF << NUM_DEV_PERIO_IN_EP_SHIFT) > +#define DED_FIFO_EN (1 << 25) > +#define NUM_IN_EPS_SHIFT 26 > +#define NUM_IN_EPS (0xF << NUM_IN_EPS_SHIFT) > +#define UTMI_PHY_DATA_WIDTH_SHIFT 14 > +#define UTMI_PHY_DATA_WIDTH (0x3 << UTMI_PHY_DATA_WIDTH_SHIFT) > + /* Reserved Offset: 054h-0FFh */ > + u32 reserved[43]; > + /* Host Periodic Transmit FIFO Size Register. Offset: 100h */ > + u32 hptxfsiz; > + > + /* > + * Device Periodic Transmit FIFO#n Register, if dedicated fifos are > + * disabled. Otherwise Device Transmit FIFO#n Register. > + * > + * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15) > + */ > + u32 dptxfsiz_dieptxf[15]; > +#define dwc_param_dev_tx_fifo_size_default 256 > +#define dwc_param_dev_perio_tx_fifo_size_default 256 > +}; > + > +/* > + * Device Global Registers. Offsets 800h-BFFh > + * > + * The following structures define the size and relative field offsets for > the + * Device Mode Registers. > + * > + * These registers are visible only in Device mode and must not be > accessed in + * Host mode, as the results are unknown. > + */ > +struct device_global_regs { /* CONFIG_DWC_OTG_REG_LE */ > + /* Device Configuration Register. Offset: 800h */ > + u32 dcfg; > +#define DWC_DCFG_FRAME_INTERVAL_80 0 > +#define DWC_DCFG_FRAME_INTERVAL_85 1 > +#define DWC_DCFG_FRAME_INTERVAL_90 2 > +#define DWC_DCFG_FRAME_INTERVAL_95 3 > +#define DWC_DCFG_FRAME_INTERVAL_MASK 3 > +#define PERFRINT_SHIFT 11 > +#define DEVSPDMSK (0x3 << 0) > +#define DEVADDR_SHIFT 4 > +#define DEVADDRMSK (0x7F << DEVADDR_SHIFT) > +#define NZSTSOUTHSHK (1 << 2) > + /* Device Control Register. Offset: 804h */ > + u32 dctl; > +#define RMTWKUPSIG (1 << 0) > +#define SFTDISCON (1 << 1) > +#define CGNPINNAK (1 << 7) > + /* Device Status Register (Read Only). Offset: 808h */ > + u32 dsts; > +#define ENUMSPD_SHIFT 1 > +#define ENUMSPDMSK (3 << ENUMSPD_SHIFT) > +#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 > +#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 > +#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 > +#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 > + /* Reserved. Offset: 80Ch */ > + u32 unused; > + /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */ > + u32 diepmsk; > +#define TIMEOUTMSK (1 << 3) > +#define INTKNTXFEMP (1 << 4) > +#define INTKNEPMISMSK (1 << 5) > +#define INEPNAKEFFMSK (1 << 6) > +#define TXFIFOUNDRN (1 << 8) > + /* Device OUT Endpoint Common Interrupt Mask Register. Offset: 814h */ > + u32 doepmsk; > +#define XFERCOMPLMSK (1 << 0) > +#define EPDISABLEDMSK (1 << 1) > +#define AHBERRMSK (1 << 2) > +#define SETUPMSK (1 << 3) > +#define INTKNTXFEMPMSK (1 << 4) > + /* Device All Endpoints Interrupt Register. Offset: 818h */ > + u32 daint; > + /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */ > + u32 daintmsk; > +#define DAINTMASK_IN_SHIFT 0 > +#define DAINTMASK_OUT_SHIFT 16 > + /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */ > + u32 dtknqr1; > +#define EPTK0_5_SHIFT 8 > +#define EPTK0_5MSK ((u32)0xFFFFFF << EPTK0_5_SHIFT) > +#define INTKNWPTR_SHIFT 0 > +#define INTKNWPTRMSK ((u32)0x1F << INTKNWPTR_SHIFT) > + /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */ > + u32 dtknqr2; > + /* Device VBUS discharge Register. Offset: 828h */ > + u32 dvbusdis; > + /* Device VBUS Pulse Register. Offset: 82Ch */ > + u32 dvbuspulse; > + /* Device IN Token Queue Read Register-3 (Read Only). Offset: 830h */ > + u32 dtknqr3_dthrctl; > + /* Device IN Token Queue Read Register-4 (Read Only). Offset: 834h */ > + u32 dtknqr4_fifoemptymsk; > +}; > +/* > + * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh > + * > + * There will be one set of endpoint registers per logical endpoint > implemented. + * > + * These registers are visible only in Device mode and must not be > accessed in + * Host mode, as the results are unknown. > + */ > +struct device_in_ep_regs { > + /* > + * Device IN Endpoint Control Register. > + * Offset:900h + (ep_num * 20h) + 00h > + */ > + u32 diepctl; > +#define EPENA ((u32)1 << 31) > +#define EPDIS (1 << 30) > +#define SNAK (1 << 27) > +#define CNAK (1 << 26) > +#define SSTALL (1 << 21) > +#define MPS_SHIFT 0 > +#define MPSMSK0 (3 << MPS_SHIFT) > +#define DWC_DEP0CTL_MPS_64 0 > +#define DWC_DEP0CTL_MPS_32 1 > +#define DWC_DEP0CTL_MPS_16 2 > +#define DWC_DEP0CTL_MPS_8 3 > +#define DIEPCTL_MPSMSK (0x7FF << MPS_SHIFT) > + /* Reserved. Offset:900h + (ep_num * 20h) + 04h */ > + u32 reserved04; > + /* > + * Device IN Endpoint Interrupt Register. > + * Offset:900h + (ep_num * 20h) + 08h > + */ > + u32 diepint; > +#define TXFEMP (1 << 7) > +#define INTKNTXFEMP (1 << 4) > +#define XFERCOMPL (1 << 0) > + /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ > + u32 reserved0C; > + /* Device IN Endpoint Transfer Size Register. > + * Offset:900h + (ep_num * 20h) + 10h > + */ > + u32 dieptsiz; > +#define PKTCNT_SHIFT 19 > + /* > + * Device IN Endpoint DMA Address Register. > + * Offset:900h + (ep_num * 20h) + 14h > + */ > + u32 diepdma; > + /* Reserved. > + * Offset:900h + (ep_num * 20h) + 18h - 900h + (ep_num * 20h) + 1Ch > + */ > + u32 dtxfsts; > + /* > + * Reserved. > + * Offset:900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch > + */ > + u32 reserved18; > +}; > + > +/* > + * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh > + * > + * There will be one set of endpoint registers per logical endpoint > implemented. + * > + * These registers are visible only in Device mode and must not be > accessed in + * Host mode, as the results are unknown. > + */ > +struct device_out_ep_regs { > + /* > + * Device OUT Endpoint Control Register. > + * Offset:B00h + (ep_num * 20h) + 00h > + */ > + u32 doepctl; > +#define DOEPCTL_MPSMSK 0x7FF > +#define USBACTEP (1 << 15) > +#define EPTYPE_SHIFT 18 > +#define EPTYPEMSK (0x3 << EPTYPE_SHIFT) > +#define EPTYPE_BULK 0x2 > +#define EPTYPE_INT 0x3 > +#define DATA0PID (1 << 28) > +#define DATA1PID (1 << 29) > +#define DPIDMSK (1 << 16) > + /* > + * Device OUT Endpoint Frame number Register. > + * Offset: B00h + (ep_num * 20h) + 04h > + */ > + u32 doepfn; > + /* > + * Device OUT Endpoint Interrupt Register. > + * Offset:B00h + (ep_num * 20h) + 08h > + */ > + u32 doepint; > +#define XFERCOMPL (1 << 0) > +#define EPDISBLD (1 << 1) > +#define AHBERR (1 << 2) > +#define SETUP (1 << 3) > + /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ > + u32 reserved0C; > + /* > + * Device OUT Endpoint Transfer Size Register. > + * Offset: B00h + (ep_num * 20h) + 10h > + */ > + u32 doeptsiz; > +#define XFERSIZE_SHIFT 0 > +#define XFERSIZEMSK 0x3F > +#define PKTCNT_SHIFT 19 > +#define PKTCNT (3 << 19) > +#define SUPCNT_SHIFT 29 > +#define SUPCNTMSK (3 << SUPCNT_SHIFT) > + /* > + * Device OUT Endpoint DMA Address Register. > + * Offset:B00h + (ep_num * 20h) + 14h > + */ > + u32 doepdma; > + /* > + * Reserved. > + * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch > + */ > + u32 unused[2]; > +}; > +#define MAX_EPS_CHANNELS 4 > + > +/* > + * The dwc_ep structure represents the state of a single endpoint when > acting in + * device mode. It contains the data items needed for an > endpoint to be + * activated and transfer packets. > + */ > +struct dwc_ep { > + /* EP number used for register address lookup */ > + u8 num; > + /* EP direction 0 = OUT */ > +#if 0 > + u8 is_in; > +#endif > + /* pointer to the transfer buffer */ > + u8 *xfer_buff; > + /* Number of bytes to transfer */ > + u32 xfer_len; > +}; > + > +/* > + * DWC_otg PCD Structure. > + * This structure encapsulates the data for the dwc_otg PCD. > + */ > +struct dwc_pcd { > +#if 0 > + /* USB gadget */ > + /* Current configuration */ > + u8 configuration; > + /* Current interface */ > + u8 interface; > + /* Current alternate settinng */ > + u8 alternate; indent > + /* Current Address */ > + u16 address; > + /* device state */ > +/* usb_device_state_t device_state; */ /* current USB Device state */ > + /* > + * SETUP packet for EP0. This structure is allocated as a DMA buffer on > + * PCD initialization with enough space for up to 3 setup packets. > + */ > +#endif > + struct usb_device_request *req; > + /* Array of EPs. */ > + struct dwc_ep ep0; > + /* Array of IN EPs. */ > + struct dwc_ep in_ep[MAX_EPS_CHANNELS - 1]; > + /* Array of OUT EPs. */ > + struct dwc_ep out_ep[MAX_EPS_CHANNELS - 1]; > +}; > + > +/* > + * The device_if structure contains information needed to manage the > DWC_otg + * controller acting in device mode. It represents the > programming view of the + * device-specific aspects of the controller. > + */ > +struct device_if { > + struct core_global_regs *core_global_regs; > + /* Common configuration information */ > + > + /* Device Global Registers starting at offset 800h */ > + struct device_global_regs *dev_global_regs; > +#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 > + > + /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */ > + struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS]; > +#define DWC_DEV_IN_EP_REG_OFFSET 0x900 > +#define DWC_EP_REG_OFFSET 0x20 > + > + /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ > + struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS]; > +#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 > + > + /* Push/pop addresses for endpoints or host channels.*/ > + u32 *data_fifo[MAX_EPS_CHANNELS]; > +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 > +#define DWC_OTG_DATA_FIFO_SIZE 0x1000 > + > + struct dwc_pcd pcd; > + int speed; > +}; > + > + > +/* Function declarations */ > + > +void phy_init(void); > +void udc_irq(void); > + > +void udc_set_nak(int epid); > +void udc_unset_nak(int epid); > +int udc_endpoint_write(struct usb_endpoint_instance *endpoint); > +int udc_init(void); > +/* void udc_enable(struct usb_device_instance *device);*/ > +void udc_disable(void); > +void udc_connect(void); > +void udc_disconnect(void); > +void udc_startup_events(struct usb_device_instance *device); > +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, > + struct usb_endpoint_instance *endpoint); > +void udc_set_configuration_controller(u32); > +void udc_set_address_controller(u32); > + > +#endif /* __DW_UDC_H */ Thanks for your good work so far, keep it up ! :-)
On 3/7/2012 6:12 PM, Marek Vasut wrote: > 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 | 1064 >> +++++++++++++++++++++++++++++++++++ include/usb/designware_otg.h | >> 527 +++++++++++++++++ >> 4 files changed, 1594 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 60347d7..6731c38 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..b5dd898 >> --- /dev/null >> +++ b/drivers/usb/gadget/designware_otg.c >> @@ -0,0 +1,1064 @@ >> +/* >> + * Based on drivers/usb/gadget/designware_otg.c >> + * Synopsys DW OTG Device bus interface driver >> + * >> + * (C) Copyright 2011 >> + * Pratyush Anand, ST Micoelectronics, pratyush.anand@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 >> + */ >> +/* temp def: will be removed TBD*/ > > temp remove ;-) > will correct.. >> +#undef APG_BOARD 1 >> + >> +#ifndef APG_BOARD > > dead code? > ohh, its there...will correct it. >> +#include<common.h> >> +#include<asm/io.h> >> +#include<usbdevice.h> >> +#include "ep0.h" >> +#include<usb/designware_otg.h> >> +#include<asm/arch/hardware.h> >> +#else >> +#include "types.h" >> +#include<designware_otg.h> >> +#include<usbdevice.h> >> +#endif >> + >> +#define UDC_INIT_MDELAY 80 /* Device settle delay */ >> + >> +/* Some kind of debugging output... */ >> +#ifndef DEBUG_DWUSBTTY >> +#define UDCDBG(str) >> +#define UDCDBGA(fmt, args...) >> +#else >> +#define UDCDBG(str) serial_printf(str "\n") >> +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) >> +#endif > > debug() all the else part will vanish. this was just to test on another environment. will correct it. Sorry. > >> + >> +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 >> + >> +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. >> + */ >> +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)) { > > rename _bytes to bytes, fix globally ok.. > >> + unaligned = readl(fifo); >> + memcpy(data_buff,&unaligned, _bytes); >> + } else { >> + for (i = 0; i< word_count; i++, data_buff++) >> + *data_buff = readl(fifo); >> + } >> +} >> + >> +/* 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) { >> + 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; >> + 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*/ >> + temp = readl(&in_ep_regs->diepctl); >> + temp |= (EPENA | CNAK); >> + writel(temp,&in_ep_regs->diepctl); >> + >> + /* clear TX Fifo Empty intr*/ >> + writel(NPTXFEMPTY,&core_global_regs->gintsts); >> + >> + temp = readl(&core_global_regs->gintmsk); >> + temp |= NPTXFEMPTY; >> + writel(temp,&core_global_regs->gintmsk); >> + >> + while (!(readl(&core_global_regs->gintsts)& NPTXFEMPTY)) >> + ; > > No endless loops please ok..will provide timeout. > >> + >> + /* 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; >> + } >> + >> + writel(NPTXFEMPTY,&core_global_regs->gintsts); >> + >> + /* check for transfer completion*/ >> + while (!(readl(&in_ep_regs->diepint)& XFERCOMPL)) >> + ; > > dtto ok.. > >> + >> + writel(XFERCOMPL,&in_ep_regs->diepint); >> + >> + temp = readl(&core_global_regs->gintmsk); >> + temp&= ~NPTXFEMPTY; >> + writel(temp,&core_global_regs->gintmsk); > > Use clrsetbits_le32() and friends in cases like that ok.. > >> +} >> + >> +/* 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; >> + >> + } else if (urb&& (urb->actual_length == 0)) { >> + /* udc_set_nak(ep); */ > > remove deadcode please ok.. > >> + } >> + } >> +} >> + >> +/* This function returns pointer to out ep struct with number num */ >> +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; > > You don't need the else branch below, it'll make code more readable: ok.. > > if (num == 0) > return; > > ... code ... > >> + } else { >> + for (i = 0; i< num_out_eps; ++i) { >> + 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 */ >> +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; >> + } else { >> + 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 >> + */ >> +static int dwc_otg_pcd_handle_rx_status_q_level_intr(void) >> +{ >> + struct core_global_regs *global_regs = dev_if->core_global_regs; >> + struct dwc_pcd *pcd =&dev_if->pcd; >> + u32 gintmsk; >> + u32 status; >> + struct dwc_ep *ep; >> + u32 bcnt; >> + >> + /* Disable the Rx Status Queue Level interrupt */ >> + gintmsk = readl(&global_regs->gintmsk); >> + gintmsk&= ~RXSTSQLVL; >> + writel(gintmsk,&global_regs->gintmsk); >> + >> + /* Get the Status from the top of the FIFO */ >> + status = readl(&global_regs->grxstsp); >> + /* Get pointer to EP structure */ >> + ep = get_out_ep((status& EPNUMMSK)>> EPNUM_SHIFT); >> + bcnt = (status& BCNTMSK)>> BCNT_SHIFT; >> + >> + switch ((status& PKTSTSMSK)>> PKTSTS_SHIFT) { >> + case DWC_DSTS_GOUT_NAK: >> + break; >> + case DWC_STS_DATA_UPDT: >> + if (bcnt) >> + dw_udc_epn_rx(ep, bcnt); >> + break; >> + case DWC_STS_XFER_COMP: >> + break; >> + case DWC_DSTS_SETUP_COMP: >> + break; >> + case DWC_DSTS_SETUP_UPDT: >> + dwc_otg_read_setup_packet((u32 *)pcd->req); >> + break; >> + default: >> + break; >> + } >> + >> + /* Enable the Rx Status Queue Level interrupt */ >> + gintmsk = readl(&global_regs->gintmsk); >> + gintmsk |= RXSTSQLVL; >> + writel(gintmsk,&global_regs->gintmsk); >> + >> + /* Clear interrupt */ >> + writel(RXSTSQLVL,&global_regs->gintsts); >> + >> + return 1; >> +} >> + >> +/* >> + * This function starts the Zero-Length Packet for the IN status phase >> + * of a 2 stage control transfer. >> + */ >> +static void do_setup_in_status_phase(struct device_if *dev_if) >> +{ >> + struct device_out_ep_regs *out_regs = >> + dev_if->out_ep_regs[0]; >> + u32 doepctl, doeptsiz; >> + doeptsiz = 0; >> + doeptsiz |= (1<< PKTCNT_SHIFT); >> + writel(doeptsiz,&out_regs->doeptsiz); >> + doepctl = readl(&out_regs->doepctl); >> + doepctl |= (CNAK | EPENA); >> + writel(doepctl,&out_regs->doepctl); > > clrsetbits_le32 ... fix globally please. > >> +} >> + >> +static void udc_set_stall(int epid, int dir) >> +{ > > if (dir) > reg = ... > else > reg = ... > > writel(readl(reg) | ..., reg); > > might be more readable ;-) both are not writing to the same register. if is diepctl and else is doepctl. > >> + if (dir) >> + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SSTALL, >> +&dev_if->in_ep_regs[epid]->diepctl); >> + else >> + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SSTALL, >> +&dev_if->out_ep_regs[epid]->doepctl); >> +} >> +/* >> + * This function handles EP0 Control transfers. >> + * >> + * The state of the control tranfers are tracked in ep0state >> + * >> + * A flag set indicates that it is not the first packet, so do not >> + * process setup data now. it has alreday been processed, just send the >> + * next data packet >> + */ >> +void handle_ep0(int in_flag) >> +{ >> + struct dwc_pcd *pcd =&dev_if->pcd; >> + struct dwc_ep *ep0 =&pcd->ep0; >> + struct usb_device_request *ctrl = pcd->req; >> + >> + /* handle inepint, only when more than 64 bytes to transfer*/ >> + if (in_flag& !ep0_urb->actual_length) >> + return; >> + >> + if (!ep0_urb->actual_length) { >> + if (ep0_recv_setup(ep0_urb)) { >> + udc_set_stall(0, ctrl->bmRequestType& USB_DIR_IN); >> + return; >> + } >> + ep0->xfer_buff = (u8 *)ep0_urb->buffer; >> + } else >> + ep0->xfer_buff += EP0_MAX_PACKET_SIZE; >> + >> + if (ep0_urb->actual_length<= EP0_MAX_PACKET_SIZE) { >> + ep0->xfer_len = ep0_urb->actual_length; >> + ep0_urb->actual_length = 0; >> + } else { >> + ep0->xfer_len = EP0_MAX_PACKET_SIZE; >> + ep0_urb->actual_length -= EP0_MAX_PACKET_SIZE; >> + } >> + >> + if (ctrl->bmRequestType& USB_DIR_IN) { >> + dwc_otg_ep_write_packet(ep0); >> + if (!ep0_urb->actual_length) >> + do_setup_in_status_phase(dev_if); >> + } else { >> + if (!ctrl->wLength) >> + dwc_otg_ep_write_packet(ep0); >> + else >> + udc_set_stall(0, ctrl->bmRequestType& USB_DIR_OUT); >> + } >> +} >> + >> +/* >> + * This function reads the Device All Endpoints Interrupt register and >> + * returns the OUT endpoint interrupt bits. >> + */ >> +static u32 dwc_otg_read_dev_all_out_ep_intr(void) >> +{ >> + u32 v; >> + >> + v = readl(&dev_if->dev_global_regs->daint)& >> + readl(&dev_if->dev_global_regs->daintmsk); >> + return (v& 0xffff0000)>> 16; > > simple return v>> 16 doesn't work? should work :) >> +} >> + >> +/* >> + * This function reads the Device All Endpoints Interrupt register and >> + * returns the IN endpoint interrupt bits. >> + */ >> +static u32 dwc_otg_read_dev_all_in_ep_intr(void) >> +{ >> + u32 v; >> + >> + v = readl(&dev_if->dev_global_regs->daint)& >> + readl(&dev_if->dev_global_regs->daintmsk); >> + return v& 0xffff; >> +} >> + >> +/* This function returns the Device OUT EP Interrupt register */ >> +static u32 dwc_otg_read_doep_intr(struct dwc_ep *ep) >> +{ >> + u32 v; >> + >> + v = readl(&dev_if->out_ep_regs[ep->num]->doepint)& >> + readl(&dev_if->dev_global_regs->doepmsk); >> + return v; >> +} >> + >> +/*This function returns the Device IN EP Interrupt register */ >> +static u32 dwc_otg_read_diep_intr(struct dwc_ep *ep) >> +{ >> + u32 v; >> + >> + v = readl(&dev_if->in_ep_regs[ep->num]->diepint)& >> + readl(&dev_if->dev_global_regs->diepmsk); >> + return v; >> +} >> + >> +/* >> + * This function configures EPO to receive SETUP packets. >> + * >> + * Program the following fields in the endpoint specific registers for >> Control + * OUT EP 0, in order to receive a setup packet: >> + * >> + * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup >> packets) + * >> + * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back >> setup + * packets) >> + * >> + * In DMA mode, DOEPDMA0 Register with a memory address to store any setup >> + * packets received >> + */ >> +static void ep0_out_start(void) >> +{ >> + u32 temp; >> + >> + /* program transfer size*/ >> + temp = 8 * 3; >> + /* program packet count*/ >> + temp |= PKTCNT; >> + /* program setup packet count */ >> + temp |= (3<< SUPCNT_SHIFT); >> + writel(temp,&dev_if->out_ep_regs[0]->doeptsiz); >> +} >> + >> +/* should be called after set address is received */ >> +void udc_set_address_controller(u32 address) >> +{ >> + u32 dcfg; >> + >> + dcfg = readl(&dev_if->dev_global_regs->dcfg); >> + dcfg&= ~DEVADDRMSK; >> + dcfg |= address<< DEVADDR_SHIFT; >> + writel(dcfg,&dev_if->dev_global_regs->dcfg); >> + >> + usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0); >> +} >> + >> +/* should be called after set configuration is received */ >> +static void dwc_otg_bulk_out_activate(void) >> +{ >> + struct device_out_ep_regs *out_regs = >> + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; >> + struct device_global_regs *dev_global_regs >> + = dev_if->dev_global_regs; >> + u32 doepctl, doeptsiz, daint; >> + >> + daint = readl(&dev_global_regs->daintmsk); >> + daint |= 1<< (UDC_OUT_ENDPOINT + DAINTMASK_OUT_SHIFT); >> + writel(daint,&dev_global_regs->daintmsk); >> + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; >> + doeptsiz |= (1<< PKTCNT_SHIFT); >> + writel(doeptsiz,&out_regs->doeptsiz); >> + doepctl = readl(&out_regs->doepctl); >> + doepctl&= ~DOEPCTL_MPSMSK; >> + doepctl&= ~EPTYPEMSK; >> + doepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE | >> + CNAK | EPENA | USBACTEP | DATA0PID >> + | (EPTYPE_BULK<< EPTYPE_SHIFT)); >> + writel(doepctl,&out_regs->doepctl); >> +} >> + >> +/* should be called after set configuration is received */ >> +static void dwc_otg_bulk_in_activate(void) >> +{ >> + struct device_in_ep_regs *in_regs = >> + dev_if->in_ep_regs[UDC_IN_ENDPOINT]; >> + struct device_global_regs *dev_global_regs >> + = dev_if->dev_global_regs; >> + u32 diepctl, daint; >> + >> + daint = readl(&dev_global_regs->daintmsk); >> + daint |= 1<< (UDC_IN_ENDPOINT + DAINTMASK_IN_SHIFT); >> + writel(daint,&dev_global_regs->daintmsk); >> + >> + diepctl = readl(&in_regs->diepctl); >> + diepctl&= ~DIEPCTL_MPSMSK; >> + diepctl&= ~EPTYPEMSK; >> + diepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE >> + | USBACTEP | DATA0PID >> + | (EPTYPE_BULK<< EPTYPE_SHIFT)); >> + writel(diepctl,&in_regs->diepctl); >> +} >> + >> +static void dwc_otg_int_in_activate(void) >> +{ >> + struct device_in_ep_regs *in_regs = >> + dev_if->in_ep_regs[UDC_INT_ENDPOINT]; >> + struct device_global_regs *dev_global_regs >> + = dev_if->dev_global_regs; >> + u32 diepctl, daint; >> + >> + daint = readl(&dev_global_regs->daintmsk); >> + daint |= 1<< (UDC_INT_ENDPOINT + DAINTMASK_IN_SHIFT); >> + writel(daint,&dev_global_regs->daintmsk); >> + >> + diepctl = readl(&in_regs->diepctl); >> + diepctl&= ~DIEPCTL_MPSMSK; >> + diepctl&= ~EPTYPEMSK; >> + diepctl |= (UDC_INT_PACKET_SIZE >> + | USBACTEP | DATA0PID >> + | (EPTYPE_INT<< EPTYPE_SHIFT)); >> + writel(diepctl,&in_regs->diepctl); > > lot of clrsetbits_le32 will appear in these parts ;-) ok. > >> +} >> + >> +/* should be called after set configuration is received */ >> +void udc_set_configuration_controller(u32 config) >> +{ >> + dwc_otg_bulk_out_activate(); >> + dwc_otg_bulk_in_activate(); >> + dwc_otg_int_in_activate(); >> + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); >> +} >> + >> +/* should be called to receive next packet */ >> +static void dwc_otg_bulk_out_enable(void) >> +{ >> + struct device_out_ep_regs *out_regs = >> + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; >> + u32 doepctl, doeptsiz; >> + >> + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; >> + doeptsiz |= (1<< PKTCNT_SHIFT); >> + writel(doeptsiz,&out_regs->doeptsiz); >> + doepctl = readl(&out_regs->doepctl); >> + doepctl |= CNAK | EPENA; >> + writel(doepctl,&out_regs->doepctl); >> +} >> + >> +/* This interrupt indicates that an OUT EP has a pending Interrupt. */ >> + >> +static int dwc_otg_pcd_handle_out_ep_intr(void) >> +{ >> + u32 ep_intr; >> + u32 doepint; >> + u32 epnum = 0; >> + struct dwc_ep *dwc_ep; >> + struct device_out_ep_regs **out_ep_regs >> + = dev_if->out_ep_regs; >> + >> + /* Read in the device interrupt bits */ >> + ep_intr = dwc_otg_read_dev_all_out_ep_intr(); >> + while (ep_intr) { >> + if (ep_intr& 0x1) { >> + dwc_ep = get_out_ep(epnum); > > do you even use this variable? Don't you get gcc4.6 warnings ? did not get warning, will cross check. > >> + doepint = dwc_otg_read_doep_intr(dwc_ep); >> + >> + /* Transfer complete */ >> + if (doepint& XFERCOMPL) { >> + /* Clear xfercompl */ >> + writel(XFERCOMPL,&out_ep_regs[epnum]->doepint); >> + if (!epnum) >> + ep0_out_start(); >> + else if (epnum == UDC_OUT_ENDPOINT) >> + dwc_otg_bulk_out_enable(); >> + } >> + /* Setup Phase Done (control EPs) */ >> + if (doepint& SETUP) { >> + writel(SETUP,&out_ep_regs[epnum]->doepint); >> + handle_ep0(0); >> + } >> + } >> + epnum++; >> + ep_intr>>= 1; >> + } >> + return 1; >> +} >> + >> +/* This interrupt indicates that an IN EP has a pending Interrupt. */ >> + >> +static int dwc_otg_pcd_handle_in_ep_intr(void) >> +{ >> + u32 ep_intr; >> + u32 diepint; >> + u32 epnum = 0; >> + struct dwc_ep *dwc_ep; >> + struct device_in_ep_regs **in_ep_regs >> + = dev_if->in_ep_regs; >> + >> + /* Read in the device interrupt bits */ >> + ep_intr = dwc_otg_read_dev_all_in_ep_intr(); >> + while (ep_intr) { >> + if (ep_intr& 0x1) { > > if (!...) > continue; > > ... code ... > > Lessers the depth of code. ok.. > >> + dwc_ep = get_in_ep(epnum); >> + diepint = dwc_otg_read_diep_intr(dwc_ep); >> + >> + /* IN token received when txfifo empty */ >> + if (diepint& INTKNTXFEMP) { >> + /* Clear xfercompl */ >> + writel(INTKNTXFEMP, >> +&in_ep_regs[epnum]->diepint); >> + if (!epnum) >> + handle_ep0(1); >> + else if (epnum == UDC_IN_ENDPOINT) >> + dw_udc_epn_tx(dwc_ep); >> + } >> + } >> + epnum++; >> + ep_intr>>= 1; >> + } >> + return 1; >> +} >> + >> +static void dwc_otg_flush_tx_fifo(const int num) >> +{ >> + struct core_global_regs *global_regs = dev_if->core_global_regs; >> + u32 val = 0; >> + int count = 0; >> + >> + val = readl(&global_regs->grstctl); >> + val |= TXFFLSH; >> + val&= ~TXFNUM; >> + val |= (num<< TXFNUM_SHIFT); >> + writel(val,&global_regs->grstctl); >> + >> + do { >> + val = readl(&global_regs->grstctl); >> + if (++count> 10000) >> + break; >> + udelay(1); >> + } while (val& TXFFLSH); >> + >> + /* Wait for 3 PHY Clocks */ >> + udelay(1); >> +} >> + >> +static void dwc_otg_flush_rx_fifo(void) >> +{ >> + struct core_global_regs *global_regs = dev_if->core_global_regs; >> + int count = 0; >> + u32 val = 0; >> + >> + val = readl(&global_regs->grstctl); >> + val |= RXFFLSH; >> + writel(val,&global_regs->grstctl); >> + >> + do { >> + val = readl(&global_regs->grstctl); >> + if (++count> 10000) >> + break; >> + udelay(1); > > WATCHDOG_RESET() instead of udelay() > also, call the variable count "timeout" and make it count down, it'll be more > readable (nitpick) > >> + } while (val& RXFFLSH); >> + >> + /* Wait for 3 PHY Clocks */ >> + udelay(1); >> +} >> + >> +/* >> + * This interrupt occurs when a USB Reset is detected. When the USB Reset >> + * Interrupt occurs the device state is set to DEFAULT and the EP0 state >> is set + * to IDLE. >> + * >> + */ >> +static int dwc_otg_pcd_handle_usb_reset_intr(void) >> +{ >> + u32 temp; >> + u32 i; >> + u32 gintmsk; >> + struct device_out_ep_regs **out_ep_regs >> + = dev_if->out_ep_regs; >> + struct device_global_regs *dev_global_regs >> + = dev_if->dev_global_regs; >> + struct core_global_regs *core_global_regs >> + = dev_if->core_global_regs; >> + /* Set NAK for all OUT EPs */ >> + for (i = 0; i< MAX_EPS_CHANNELS; i++) { >> + temp = readl(&out_ep_regs[i]->doepctl); >> + temp |= SNAK; >> + writel(temp,&out_ep_regs[i]->doepctl); >> + } >> + >> + /* Flush the NP Tx FIFO */ >> + dwc_otg_flush_tx_fifo(DWC_GRSTCTL_TXFNUM_ALL); >> + dwc_otg_flush_rx_fifo(); >> + writel((1<< (DAINTMASK_IN_SHIFT + 0)) >> + | (1<< (DAINTMASK_OUT_SHIFT + 0)), >> +&dev_global_regs->daintmsk); >> + >> + writel(SETUPMSK | XFERCOMPLMSK | AHBERRMSK | EPDISABLEDMSK, >> +&dev_global_regs->doepmsk); >> + >> + writel(INTKNTXFEMP,&dev_global_regs->diepmsk); >> + >> + gintmsk = readl(&core_global_regs->gintmsk); >> + gintmsk |= GOUTNAKEFF; >> + writel(gintmsk,&core_global_regs->gintmsk); >> + >> + /* program fifo size for ep0 */ >> + >> + writel(0x200,&core_global_regs->grxfsiz); >> + >> + temp = readl(&dev_if->in_ep_regs[0]->diepctl); >> + temp&= 0xFFC3FFFF; /* TxFNumBF = 0, bits 25:22 */ >> + writel(temp,&dev_if->in_ep_regs[0]->diepctl); >> + >> + temp = readl(&core_global_regs->gnptxfsiz); >> + >> + writel(0x2000000,&core_global_regs->gnptxfsiz); > > Magic value? Ok, will define it properly. > >> + >> + /* Reset Device Address */ >> + temp = readl(&dev_global_regs->dcfg); >> + temp&= ~DEVADDRMSK; >> + writel(temp,&dev_global_regs->dcfg); >> + >> + /* setup EP0 to receive SETUP packets */ >> + ep0_out_start(); >> + >> + /* Clear interrupt */ >> + writel(USBRESET,&core_global_regs->gintsts); >> + >> + UDCDBG("device reset in progess"); >> + usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0); >> + >> + return 1; >> +} >> + >> +/* >> + * 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) >> +{ >> + u32 temp; >> + 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 */ >> + temp = readl(&in_ep_regs->diepctl); >> + temp&= ~MPSMSK0; >> + temp |= DWC_DEP0CTL_MPS_64; >> + writel(temp,&in_ep_regs->diepctl); >> + >> + temp = readl(&out_ep_regs->doepctl); >> + /* Enable OUT EP for receive */ >> + temp |= EPENA; >> + writel(temp,&out_ep_regs->doepctl); >> +} >> + >> +/* >> + * Read the device status register and set the device speed in the >> + * data structure. >> + * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. >> + */ >> +static int dwc_otg_pcd_handle_enum_done_intr(void) >> +{ >> + u32 gusbcfg; >> + struct core_global_regs *global_regs = dev_if->core_global_regs; >> + dwc_otg_ep0_activate(); >> + >> + gusbcfg = readl(&global_regs->gusbcfg); >> + gusbcfg&= ~USBTRDTIMMSK; >> + gusbcfg |= PHYIF_16BIT; >> + gusbcfg |= (9<< USBTRDTIM_SHIFT); > > Magic? will define this value. > >> + writel(gusbcfg,&global_regs->gusbcfg); >> + /* Clear interrupt */ >> + writel(ENUMDONE,&global_regs->gintsts); >> + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); >> + return 1; >> +} >> + >> +static u32 dwc_otg_read_core_intr(void) >> +{ >> + return readl(&dev_if->core_global_regs->gintsts)& >> + readl(&dev_if->core_global_regs->gintmsk); >> +} >> + >> +static void dwc_otg_init(const void *reg_base) >> +{ >> + struct dwc_pcd *pcd =&dev_if->pcd; >> + u32 offset; >> + u32 i; >> + >> + dev_if->core_global_regs = (struct core_global_regs *) reg_base; >> + dev_if->dev_global_regs = (struct device_global_regs *) ((u32)reg_base + >> + DWC_DEV_GLOBAL_REG_OFFSET); >> + >> + for (i = 0; i< MAX_EPS_CHANNELS; i++) { >> + offset = i * DWC_EP_REG_OFFSET; >> + >> + dev_if->in_ep_regs[i] = (struct device_in_ep_regs *) >> + ((u32)reg_base + DWC_DEV_IN_EP_REG_OFFSET + offset); >> + >> + dev_if->out_ep_regs[i] = (struct device_out_ep_regs *) >> + ((u32)reg_base + DWC_DEV_OUT_EP_REG_OFFSET + offset); >> + } >> + >> + for (i = 0; i< MAX_EPS_CHANNELS; i++) { >> + dev_if->data_fifo[i] = >> + (u32 *) ((u32)reg_base + DWC_OTG_DATA_FIFO_OFFSET + >> + (i * DWC_OTG_DATA_FIFO_SIZE)); >> + } >> + >> + dev_if->speed = 0; /* unknown */ >> + for (i = 0; i< MAX_EPS_CHANNELS; i++) { >> + pcd->in_ep[i].num = i; >> + pcd->out_ep[i].num = i; >> + } >> +} >> + >> +/* >> + * This function initializes the DWC_otg controller registers and prepares >> the + * core for device mode >> + */ >> +static void dwc_otg_core_init(void) >> +{ >> + struct core_global_regs *global_regs = dev_if->core_global_regs; >> + u32 ahbcfg, gintmsk, usbcfg; >> + /* Step 1: Program the GAHBCFG Register. */ >> + ahbcfg = DWC_NPTXEMPTYLVL_EMPTY | DWC_PTXEMPTYLVL_EMPTY; >> + writel(ahbcfg,&global_regs->gahbcfg); >> + >> + /* Step 2: write usbcfg regs*/ >> + usbcfg = readl(&global_regs->gusbcfg); >> + usbcfg |= SRPCAP | HNPCAP; >> + writel(usbcfg,&global_regs->gusbcfg); >> + >> + /* step3: write int_msk reg*/ >> + gintmsk = USBRESET | ENUMDONE | RXSTSQLVL | OUTEPINTR | INEPINTR; >> + writel(gintmsk,&global_regs->gintmsk); >> +} >> + >> +/* Switch on the UDC */ >> +static void usbotg_init(void) >> +{ >> + udc_device = NULL; >> +#ifdef APG_BOARD >> + dwc_otg_init((void *)0x11000000); > > What the heck ? :-O > > Dead code ? Sorry, ifdef APG_BOARD was completely local. will correct all these. > >> +#else >> + dwc_otg_init((void *)CONFIG_SYS_USBD_BASE); >> +#endif >> + >> + /* Initialize the DWC_otg core. */ >> + dwc_otg_core_init(); >> + >> +} >> + >> +void udc_irq(void) >> +{ >> + u32 status; >> + >> + status = dwc_otg_read_core_intr(); >> + while (status) { >> + if (status& USBRESET) >> + dwc_otg_pcd_handle_usb_reset_intr(); >> + if (status& ENUMDONE) >> + dwc_otg_pcd_handle_enum_done_intr(); >> + if (status& RXSTSQLVL) >> + dwc_otg_pcd_handle_rx_status_q_level_intr(); >> + if (status& OUTEPINTR) >> + dwc_otg_pcd_handle_out_ep_intr(); >> + if (status& INEPINTR) >> + dwc_otg_pcd_handle_in_ep_intr(); >> + status = dwc_otg_read_core_intr(); >> + } >> + >> +} >> + >> +void udc_set_nak(int epid) >> +{ >> + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SNAK, >> +&dev_if->out_ep_regs[epid]->doepctl); >> + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SNAK, >> +&dev_if->in_ep_regs[epid]->diepctl); > > Shouldn't you put this together with previous such code that operated with > exeactly the same registers ? maybe even merge these three functions together. > > And(!) make everything that you don't need outside static please. ok.. > >> +} >> + >> +void udc_unset_nak(int epid) >> +{ >> + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | CNAK, >> +&dev_if->out_ep_regs[epid]->doepctl); >> + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | CNAK, >> +&dev_if->in_ep_regs[epid]->diepctl); >> +} >> + >> +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) >> +{ >> + udc_unset_nak(endpoint->endpoint_address& USB_ENDPOINT_NUMBER_MASK); >> + return 0; >> +} >> + >> +static void udc_enable(struct usb_device_instance *device) >> +{ >> + struct dwc_pcd *pcd =&dev_if->pcd; >> + >> + UDCDBGA("enable device %p, status %d", device, device->status); >> + >> + /* Save the device structure pointer */ >> + udc_device = device; >> + >> + /* Setup ep0 urb */ >> + if (!ep0_urb) { >> + ep0_urb = >> + usbd_alloc_urb(udc_device, >> + udc_device->bus->endpoint_array); >> + pcd->req = >> + (struct usb_device_request *)&ep0_urb->device_request; >> + pcd->ep0.xfer_buff = (u8 *)ep0_urb->buffer; >> + } else { >> +#ifndef APG_BOARD >> + serial_printf("udc_enable: ep0_urb already allocated %p\n", >> + ep0_urb); > > Use printf(), not serial_printf(), globally ok.. > >> +#endif >> + } >> +} >> + >> +void udc_connect(void) >> +{ >> + struct device_global_regs *dev_regs = dev_if->dev_global_regs; >> + u32 dcfg; >> + >> + /* remove soft disconnect */ >> + dcfg = readl(&dev_regs->dctl); >> + dcfg&= ~SFTDISCON; >> + writel(dcfg,&dev_regs->dctl); >> +} >> + >> +void udc_disconnect(void) >> +{ >> + struct device_global_regs *dev_regs = dev_if->dev_global_regs; >> + u32 dcfg; >> + >> + /* soft disconnect */ >> + dcfg = readl(&dev_regs->dctl); >> + dcfg |= SFTDISCON; >> + writel(dcfg,&dev_regs->dctl); >> + udelay(150); >> +} >> + >> +void udc_startup_events(struct usb_device_instance *device) >> +{ >> + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ >> + usbd_device_event_irq(device, DEVICE_INIT, 0); >> + >> + /* >> + * The DEVICE_CREATE event puts the USB device in the state >> + * STATE_ATTACHED. >> + */ >> + usbd_device_event_irq(device, DEVICE_CREATE, 0); >> + >> + /* >> + * Some USB controller driver implementations signal >> + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. >> + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, >> + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. >> + * The DW USB client controller has the capability to detect when the >> + * USB cable is connected to a powered USB bus, so we will defer the >> + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later. >> + */ >> + >> + udc_enable(device); >> + >> +} >> + >> +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, >> + struct usb_endpoint_instance *endpoint) >> +{ >> + /* >> + * Nothing to do here. Hob of this function has laready been >> + * done during init. >> + */ >> +} >> + >> +int is_usbd_high_speed(void) >> +{ >> + struct device_global_regs *dev_regs = dev_if->dev_global_regs; >> + u32 dsts; >> + >> + dsts = readl(&dev_regs->dsts); >> + dsts&= ENUMSPDMSK; >> + if (dsts == DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ) >> + return 1; >> + else >> + return 0; >> +} >> + >> +int udc_init(void) >> +{ >> + phy_init(); >> + udc_disconnect(); >> + usbotg_init(); >> + return 0; >> +} >> diff --git a/include/usb/designware_otg.h b/include/usb/designware_otg.h >> new file mode 100644 >> index 0000000..d7b686b >> --- /dev/null >> +++ b/include/usb/designware_otg.h >> @@ -0,0 +1,527 @@ >> +/* >> + * (C) Copyright 2011 >> + * Pratyush Anand, ST Micoelectronics, pratyush.anand@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 >> + */ >> + >> +#ifndef __DW_OTG_H >> +#define __DW_OTG_H >> +/* temp def: will be removed TBD */ >> +#undef APH_BOARD 1 >> +#ifdef APG_BOARD >> +#include "types.h" >> +#define CONFIG_USBD_HS >> +#define CONFIG_DW_OTG >> +#endif >> + >> +#include "usbdevice.h" >> +/* USBTTY definitions */ >> +#define EP0_MAX_PACKET_SIZE 64 >> +#define UDC_INT_ENDPOINT 1 >> +#define UDC_INT_PACKET_SIZE 64 >> +#define UDC_OUT_ENDPOINT 2 >> +#define UDC_BULK_PACKET_SIZE 512 >> +#if defined(CONFIG_USBD_HS) >> +#define UDC_BULK_HS_PACKET_SIZE 512 >> +#endif >> +#define UDC_IN_ENDPOINT 3 >> +#define UDC_OUT_PACKET_SIZE 64 >> +#define UDC_IN_PACKET_SIZE 64 >> + >> +/* UDC endpoint definitions */ >> +#define UDC_EP0 0 >> +#define UDC_EP1 1 >> +#define UDC_EP2 2 >> +#define UDC_EP3 3 >> + >> +#define CMD_SIZE 12 >> +/* OTG Register Definitions */ >> + >> +/* >> + * The application interfaces with the HS OTG core by reading from and >> + * writing to the Control and Status Register (CSR) space through the >> + * AHB Slave interface. These registers are 32 bits wide, and the >> + * addresses are 32-bit-block aligned. >> + * CSRs are classified as follows: >> + * - Core Global Registers >> + * - Device Mode Registers >> + * - Device Global Registers >> + * - Device Endpoint Specific Registers >> + * - Host Mode Registers >> + * - Host Global Registers >> + * - Host Port CSRs >> + * - Host Channel Specific Registers >> + * >> + * Only the Core Global registers can be accessed in both Device and >> + * Host modes. When the HS OTG core is operating in one mode, either >> + * Device or Host, the application must not access registers from the >> + * other mode. When the core switches from one mode to another, the >> + * registers in the new mode of operation must be reprogrammed as they >> + * would be after a power-on reset. >> + */ >> + >> +/* >> + * DWC_otg Core registers. The core_global_regs structure defines the >> + * size and relative field offsets for the Core Global registers. >> + */ >> +struct core_global_regs { >> + /* OTG Control and Status Register. Offset: 000h */ >> + u32 gotgctl; >> + /* OTG Interrupt Register. Offset: 004h */ >> + u32 gotgint; >> + /* Core AHB Configuration Register. Offset: 008h */ >> + u32 gahbcfg; >> + >> +#define DWC_GLBINTRMASK 0x0001 >> +#define DWC_DMAENABLE 0x0020 >> +#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 >> +#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 >> +#define DWC_PTXEMPTYLVL_EMPTY 0x0100 >> +#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 >> + >> + /* Core USB Configuration Register. Offset: 00Ch */ >> + u32 gusbcfg; >> +#define PHYIF_16BIT (1<< 3) >> +#define SRPCAP (1<< 8) >> +#define HNPCAP (1<< 9) >> +#define TERM_SEL_DL_PULSE (1<< 22) >> +#define USBTRDTIM_SHIFT 10 >> +#define USBTRDTIMMSK (0xF<< USBTRDTIM_SHIFT) >> + /* Core Reset Register. Offset: 010h */ >> + u32 grstctl; >> +#define DWC_GRSTCTL_TXFNUM_ALL 0x10 >> +#define CSFTRST (1<< 0) >> +#define INTKNQFLSH (1<< 3) >> +#define RXFFLSH (1<< 4) >> +#define TXFFLSH (1<< 5) > > Fix indent ... here and everywhere please ok.. > >> +#define TXFNUM_SHIFT 6 >> +#define TXFNUM (0x1F<< TXFNUM_SHIFT) >> +#define AHBIDLE ((u32)1<< 31) >> + /* Core Interrupt Register. Offset: 014h */ >> + u32 gintsts; >> +#define RXSTSQLVL (1<< 4) >> +#define NPTXFEMPTY (1<< 5) >> +#define GOUTNAKEFF (1<< 7) >> +#define USBRESET (1<< 12) >> +#define ENUMDONE (1<< 13) >> +#define INEPINTR (1<< 18) >> +#define OUTEPINTR (1<< 19) >> + /* Core Interrupt Mask Register. Offset: 018h */ >> + u32 gintmsk; >> + /* >> + * Receive Status Queue Read Register >> + * (Read Only) Offset: 01Ch >> + */ >> + u32 grxstsr; >> + /* >> + * Receive Status Queue Read& POP Register >> + * (Read Only) Offset: 020h >> + */ >> + u32 grxstsp; >> +#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */ >> +#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */ >> +#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */ >> +#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */ >> +#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */ >> +#define EPNUM_SHIFT 0 >> +#define EPNUMMSK (0xF<< EPNUM_SHIFT) >> +#define BCNT_SHIFT 4 >> +#define BCNTMSK (0x7FF<< > BCNT_SHIFT) >> +#define PKTSTS_SHIFT 17 >> +#define PKTSTSMSK (0xF<< PKTSTS_SHIFT) >> + /* Receive FIFO Size Register. Offset: 024h */ >> + u32 grxfsiz; >> +#define dwc_param_dev_rx_fifo_size_default 1064 >> + /* Non Periodic Transmit FIFO Size Register. Offset: 028h */ >> + u32 gnptxfsiz; >> +#define dwc_param_dev_nperio_tx_fifo_size_default 1024 >> + /* >> + * Non Periodic Transmit FIFO/Queue Status Register >> + * (Read Only). Offset: 02Ch >> + */ >> + u32 gnptxsts; >> +#define NPTXQSPCAVAIL_SHIFT 16 >> +#define NPTXQSPCAVAILMSK (0xFF<< NPTXQSPCAVAIL_SHIFT) >> +#define NPTXFSPCAVAIL_SHIFT 0 >> +#define NPTXFSPCAVAILMSK (0xFFFF<< NPTXFSPCAVAIL_SHIFT) >> + /* I2C Access Register. Offset: 030h */ >> + u32 gi2cctl; >> + /* PHY Vendor Control Register. Offset: 034h */ >> + u32 gpvndctl; >> + /* General Purpose Input/Output Register. Offset: 038h */ >> + u32 ggpio; >> + /* User ID Register. Offset: 03Ch */ >> + u32 guid; >> + /* Synopsys ID Register (Read Only). Offset: 040h */ >> + u32 gsnpsid; >> + /* User HW Config1 Register (Read Only). Offset: 044h */ >> + u32 ghwcfg1; >> + /* User HW Config2 Register (Read Only). Offset: 048h */ >> + >> + u32 ghwcfg2; >> +#define DWC_SLAVE_ONLY_ARCH 0 >> +#define DWC_EXT_DMA_ARCH 1 >> +#define DWC_INT_DMA_ARCH 2 >> + >> +#define DWC_MODE_HNP_SRP_CAPABLE 0 >> +#define DWC_MODE_SRP_ONLY_CAPABLE 1 >> +#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 >> +#define DWC_MODE_SRP_CAPABLE_DEVICE 3 >> +#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 >> +#define DWC_MODE_SRP_CAPABLE_HOST 5 >> +#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 >> +#define DYNAMIC_FIFO (1<< 19) >> +#define NUM_DEV_EP_SHIFT 10 >> +#define NUM_DEV_EP (0xF<< NUM_DEV_EP_SHIFT) >> +#define HSPHYTYPE_SHIFT 6 >> +#define HSPHYTYPEMSK (3<< HSPHYTYPE_SHIFT) >> +#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 >> +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 >> +#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 >> +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 >> +#define TKNQDEPTH_SHIFT 26 >> +#define TKNQDEPTHMSK (0x1F<< TKNQDEPTH_SHIFT) >> + >> + /* User HW Config3 Register (Read Only). Offset: 04Ch */ >> + u32 ghwcfg3; >> +#define DFIFO_DEPTH_SHIFT 16 >> +#define DFIFO_DEPTH ((u32)0xFFFF<< DFIFO_DEPTH_SHIFT) >> + /* User HW Config4 Register (Read Only). Offset: 050h */ >> + u32 ghwcfg4; >> +#define NUM_DEV_PERIO_IN_EP_SHIFT 0 >> +#define NUM_DEV_PERIO_IN_EP (0xF<< NUM_DEV_PERIO_IN_EP_SHIFT) >> +#define DED_FIFO_EN (1<< 25) >> +#define NUM_IN_EPS_SHIFT 26 >> +#define NUM_IN_EPS (0xF<< NUM_IN_EPS_SHIFT) >> +#define UTMI_PHY_DATA_WIDTH_SHIFT 14 >> +#define UTMI_PHY_DATA_WIDTH (0x3<< UTMI_PHY_DATA_WIDTH_SHIFT) >> + /* Reserved Offset: 054h-0FFh */ >> + u32 reserved[43]; >> + /* Host Periodic Transmit FIFO Size Register. Offset: 100h */ >> + u32 hptxfsiz; >> + >> + /* >> + * Device Periodic Transmit FIFO#n Register, if dedicated fifos are >> + * disabled. Otherwise Device Transmit FIFO#n Register. >> + * >> + * Offset: 104h + (FIFO_Number-1)*04h, 1<= FIFO Number<= 15 (1<=n<=15) >> + */ >> + u32 dptxfsiz_dieptxf[15]; >> +#define dwc_param_dev_tx_fifo_size_default 256 >> +#define dwc_param_dev_perio_tx_fifo_size_default 256 >> +}; >> + >> +/* >> + * Device Global Registers. Offsets 800h-BFFh >> + * >> + * The following structures define the size and relative field offsets for >> the + * Device Mode Registers. >> + * >> + * These registers are visible only in Device mode and must not be >> accessed in + * Host mode, as the results are unknown. >> + */ >> +struct device_global_regs { /* CONFIG_DWC_OTG_REG_LE */ >> + /* Device Configuration Register. Offset: 800h */ >> + u32 dcfg; >> +#define DWC_DCFG_FRAME_INTERVAL_80 0 >> +#define DWC_DCFG_FRAME_INTERVAL_85 1 >> +#define DWC_DCFG_FRAME_INTERVAL_90 2 >> +#define DWC_DCFG_FRAME_INTERVAL_95 3 >> +#define DWC_DCFG_FRAME_INTERVAL_MASK 3 >> +#define PERFRINT_SHIFT 11 >> +#define DEVSPDMSK (0x3<< 0) >> +#define DEVADDR_SHIFT 4 >> +#define DEVADDRMSK (0x7F<< DEVADDR_SHIFT) >> +#define NZSTSOUTHSHK (1<< 2) >> + /* Device Control Register. Offset: 804h */ >> + u32 dctl; >> +#define RMTWKUPSIG (1<< 0) >> +#define SFTDISCON (1<< 1) >> +#define CGNPINNAK (1<< 7) >> + /* Device Status Register (Read Only). Offset: 808h */ >> + u32 dsts; >> +#define ENUMSPD_SHIFT 1 >> +#define ENUMSPDMSK (3<< ENUMSPD_SHIFT) >> +#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 >> +#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 >> +#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 >> +#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 >> + /* Reserved. Offset: 80Ch */ >> + u32 unused; >> + /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */ >> + u32 diepmsk; >> +#define TIMEOUTMSK (1<< 3) >> +#define INTKNTXFEMP (1<< 4) >> +#define INTKNEPMISMSK (1<< 5) >> +#define INEPNAKEFFMSK (1<< 6) >> +#define TXFIFOUNDRN (1<< 8) >> + /* Device OUT Endpoint Common Interrupt Mask Register. Offset: 814h */ >> + u32 doepmsk; >> +#define XFERCOMPLMSK (1<< 0) >> +#define EPDISABLEDMSK (1<< 1) >> +#define AHBERRMSK (1<< 2) >> +#define SETUPMSK (1<< 3) >> +#define INTKNTXFEMPMSK (1<< 4) >> + /* Device All Endpoints Interrupt Register. Offset: 818h */ >> + u32 daint; >> + /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */ >> + u32 daintmsk; >> +#define DAINTMASK_IN_SHIFT 0 >> +#define DAINTMASK_OUT_SHIFT 16 >> + /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */ >> + u32 dtknqr1; >> +#define EPTK0_5_SHIFT 8 >> +#define EPTK0_5MSK ((u32)0xFFFFFF<< EPTK0_5_SHIFT) >> +#define INTKNWPTR_SHIFT 0 >> +#define INTKNWPTRMSK ((u32)0x1F<< INTKNWPTR_SHIFT) >> + /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */ >> + u32 dtknqr2; >> + /* Device VBUS discharge Register. Offset: 828h */ >> + u32 dvbusdis; >> + /* Device VBUS Pulse Register. Offset: 82Ch */ >> + u32 dvbuspulse; >> + /* Device IN Token Queue Read Register-3 (Read Only). Offset: 830h */ >> + u32 dtknqr3_dthrctl; >> + /* Device IN Token Queue Read Register-4 (Read Only). Offset: 834h */ >> + u32 dtknqr4_fifoemptymsk; >> +}; >> +/* >> + * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh >> + * >> + * There will be one set of endpoint registers per logical endpoint >> implemented. + * >> + * These registers are visible only in Device mode and must not be >> accessed in + * Host mode, as the results are unknown. >> + */ >> +struct device_in_ep_regs { >> + /* >> + * Device IN Endpoint Control Register. >> + * Offset:900h + (ep_num * 20h) + 00h >> + */ >> + u32 diepctl; >> +#define EPENA ((u32)1<< 31) >> +#define EPDIS (1<< 30) >> +#define SNAK (1<< 27) >> +#define CNAK (1<< 26) >> +#define SSTALL (1<< 21) >> +#define MPS_SHIFT 0 >> +#define MPSMSK0 (3<< MPS_SHIFT) >> +#define DWC_DEP0CTL_MPS_64 0 >> +#define DWC_DEP0CTL_MPS_32 1 >> +#define DWC_DEP0CTL_MPS_16 2 >> +#define DWC_DEP0CTL_MPS_8 3 >> +#define DIEPCTL_MPSMSK (0x7FF<< MPS_SHIFT) >> + /* Reserved. Offset:900h + (ep_num * 20h) + 04h */ >> + u32 reserved04; >> + /* >> + * Device IN Endpoint Interrupt Register. >> + * Offset:900h + (ep_num * 20h) + 08h >> + */ >> + u32 diepint; >> +#define TXFEMP (1<< 7) >> +#define INTKNTXFEMP (1<< 4) >> +#define XFERCOMPL (1<< 0) >> + /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ >> + u32 reserved0C; >> + /* Device IN Endpoint Transfer Size Register. >> + * Offset:900h + (ep_num * 20h) + 10h >> + */ >> + u32 dieptsiz; >> +#define PKTCNT_SHIFT 19 >> + /* >> + * Device IN Endpoint DMA Address Register. >> + * Offset:900h + (ep_num * 20h) + 14h >> + */ >> + u32 diepdma; >> + /* Reserved. >> + * Offset:900h + (ep_num * 20h) + 18h - 900h + (ep_num * 20h) + 1Ch >> + */ >> + u32 dtxfsts; >> + /* >> + * Reserved. >> + * Offset:900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch >> + */ >> + u32 reserved18; >> +}; >> + >> +/* >> + * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh >> + * >> + * There will be one set of endpoint registers per logical endpoint >> implemented. + * >> + * These registers are visible only in Device mode and must not be >> accessed in + * Host mode, as the results are unknown. >> + */ >> +struct device_out_ep_regs { >> + /* >> + * Device OUT Endpoint Control Register. >> + * Offset:B00h + (ep_num * 20h) + 00h >> + */ >> + u32 doepctl; >> +#define DOEPCTL_MPSMSK 0x7FF >> +#define USBACTEP (1<< 15) >> +#define EPTYPE_SHIFT 18 >> +#define EPTYPEMSK (0x3<< EPTYPE_SHIFT) >> +#define EPTYPE_BULK 0x2 >> +#define EPTYPE_INT 0x3 >> +#define DATA0PID (1<< 28) >> +#define DATA1PID (1<< 29) >> +#define DPIDMSK (1<< 16) >> + /* >> + * Device OUT Endpoint Frame number Register. >> + * Offset: B00h + (ep_num * 20h) + 04h >> + */ >> + u32 doepfn; >> + /* >> + * Device OUT Endpoint Interrupt Register. >> + * Offset:B00h + (ep_num * 20h) + 08h >> + */ >> + u32 doepint; >> +#define XFERCOMPL (1<< 0) >> +#define EPDISBLD (1<< 1) >> +#define AHBERR (1<< 2) >> +#define SETUP (1<< 3) >> + /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ >> + u32 reserved0C; >> + /* >> + * Device OUT Endpoint Transfer Size Register. >> + * Offset: B00h + (ep_num * 20h) + 10h >> + */ >> + u32 doeptsiz; >> +#define XFERSIZE_SHIFT 0 >> +#define XFERSIZEMSK 0x3F >> +#define PKTCNT_SHIFT 19 >> +#define PKTCNT (3<< 19) >> +#define SUPCNT_SHIFT 29 >> +#define SUPCNTMSK (3<< SUPCNT_SHIFT) >> + /* >> + * Device OUT Endpoint DMA Address Register. >> + * Offset:B00h + (ep_num * 20h) + 14h >> + */ >> + u32 doepdma; >> + /* >> + * Reserved. >> + * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch >> + */ >> + u32 unused[2]; >> +}; >> +#define MAX_EPS_CHANNELS 4 >> + >> +/* >> + * The dwc_ep structure represents the state of a single endpoint when >> acting in + * device mode. It contains the data items needed for an >> endpoint to be + * activated and transfer packets. >> + */ >> +struct dwc_ep { >> + /* EP number used for register address lookup */ >> + u8 num; >> + /* EP direction 0 = OUT */ >> +#if 0 >> + u8 is_in; >> +#endif >> + /* pointer to the transfer buffer */ >> + u8 *xfer_buff; >> + /* Number of bytes to transfer */ >> + u32 xfer_len; >> +}; >> + >> +/* >> + * DWC_otg PCD Structure. >> + * This structure encapsulates the data for the dwc_otg PCD. >> + */ >> +struct dwc_pcd { >> +#if 0 >> + /* USB gadget */ >> + /* Current configuration */ >> + u8 configuration; >> + /* Current interface */ >> + u8 interface; >> + /* Current alternate settinng */ >> + u8 alternate; > > indent pk.. > >> + /* Current Address */ >> + u16 address; >> + /* device state */ >> +/* usb_device_state_t device_state; */ /* current USB Device state */ >> + /* >> + * SETUP packet for EP0. This structure is allocated as a DMA buffer on >> + * PCD initialization with enough space for up to 3 setup packets. >> + */ >> +#endif >> + struct usb_device_request *req; >> + /* Array of EPs. */ >> + struct dwc_ep ep0; >> + /* Array of IN EPs. */ >> + struct dwc_ep in_ep[MAX_EPS_CHANNELS - 1]; >> + /* Array of OUT EPs. */ >> + struct dwc_ep out_ep[MAX_EPS_CHANNELS - 1]; >> +}; >> + >> +/* >> + * The device_if structure contains information needed to manage the >> DWC_otg + * controller acting in device mode. It represents the >> programming view of the + * device-specific aspects of the controller. >> + */ >> +struct device_if { >> + struct core_global_regs *core_global_regs; >> + /* Common configuration information */ >> + >> + /* Device Global Registers starting at offset 800h */ >> + struct device_global_regs *dev_global_regs; >> +#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 >> + >> + /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */ >> + struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS]; >> +#define DWC_DEV_IN_EP_REG_OFFSET 0x900 >> +#define DWC_EP_REG_OFFSET 0x20 >> + >> + /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ >> + struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS]; >> +#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 >> + >> + /* Push/pop addresses for endpoints or host channels.*/ >> + u32 *data_fifo[MAX_EPS_CHANNELS]; >> +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 >> +#define DWC_OTG_DATA_FIFO_SIZE 0x1000 >> + >> + struct dwc_pcd pcd; >> + int speed; >> +}; >> + >> + >> +/* Function declarations */ >> + >> +void phy_init(void); >> +void udc_irq(void); >> + >> +void udc_set_nak(int epid); >> +void udc_unset_nak(int epid); >> +int udc_endpoint_write(struct usb_endpoint_instance *endpoint); >> +int udc_init(void); >> +/* void udc_enable(struct usb_device_instance *device);*/ >> +void udc_disable(void); >> +void udc_connect(void); >> +void udc_disconnect(void); >> +void udc_startup_events(struct usb_device_instance *device); >> +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, >> + struct usb_endpoint_instance *endpoint); >> +void udc_set_configuration_controller(u32); >> +void udc_set_address_controller(u32); >> + >> +#endif /* __DW_UDC_H */ > > Thanks for your good work so far, keep it up ! :-) > . >
diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index 60347d7..6731c38 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..b5dd898 --- /dev/null +++ b/drivers/usb/gadget/designware_otg.c @@ -0,0 +1,1064 @@ +/* + * Based on drivers/usb/gadget/designware_otg.c + * Synopsys DW OTG Device bus interface driver + * + * (C) Copyright 2011 + * Pratyush Anand, ST Micoelectronics, pratyush.anand@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 + */ +/* temp def: will be removed TBD*/ +#undef APG_BOARD 1 + +#ifndef APG_BOARD +#include <common.h> +#include <asm/io.h> +#include <usbdevice.h> +#include "ep0.h" +#include <usb/designware_otg.h> +#include <asm/arch/hardware.h> +#else +#include "types.h" +#include <designware_otg.h> +#include <usbdevice.h> +#endif + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ + +/* Some kind of debugging output... */ +#ifndef DEBUG_DWUSBTTY +#define UDCDBG(str) +#define UDCDBGA(fmt, args...) +#else +#define UDCDBG(str) serial_printf(str "\n") +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) +#endif + +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 + +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. + */ +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); + } +} + +/* 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) { + 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; + 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*/ + temp = readl(&in_ep_regs->diepctl); + temp |= (EPENA | CNAK); + writel(temp, &in_ep_regs->diepctl); + + /* clear TX Fifo Empty intr*/ + writel(NPTXFEMPTY, &core_global_regs->gintsts); + + temp = readl(&core_global_regs->gintmsk); + temp |= NPTXFEMPTY; + writel(temp, &core_global_regs->gintmsk); + + while (!(readl(&core_global_regs->gintsts) & NPTXFEMPTY)) + ; + + /* 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; + } + + writel(NPTXFEMPTY, &core_global_regs->gintsts); + + /* check for transfer completion*/ + while (!(readl(&in_ep_regs->diepint) & XFERCOMPL)) + ; + + writel(XFERCOMPL, &in_ep_regs->diepint); + + temp = readl(&core_global_regs->gintmsk); + temp &= ~NPTXFEMPTY; + writel(temp, &core_global_regs->gintmsk); +} + +/* 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; + + } else if (urb && (urb->actual_length == 0)) { + /* udc_set_nak(ep); */ + } + } +} + +/* This function returns pointer to out ep struct with number num */ +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; + } else { + for (i = 0; i < num_out_eps; ++i) { + 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 */ +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; + } else { + 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 + */ +static int dwc_otg_pcd_handle_rx_status_q_level_intr(void) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + struct dwc_pcd *pcd = &dev_if->pcd; + u32 gintmsk; + u32 status; + struct dwc_ep *ep; + u32 bcnt; + + /* Disable the Rx Status Queue Level interrupt */ + gintmsk = readl(&global_regs->gintmsk); + gintmsk &= ~RXSTSQLVL; + writel(gintmsk, &global_regs->gintmsk); + + /* Get the Status from the top of the FIFO */ + status = readl(&global_regs->grxstsp); + /* Get pointer to EP structure */ + ep = get_out_ep((status & EPNUMMSK) >> EPNUM_SHIFT); + bcnt = (status & BCNTMSK) >> BCNT_SHIFT; + + switch ((status & PKTSTSMSK) >> PKTSTS_SHIFT) { + case DWC_DSTS_GOUT_NAK: + break; + case DWC_STS_DATA_UPDT: + if (bcnt) + dw_udc_epn_rx(ep, bcnt); + break; + case DWC_STS_XFER_COMP: + break; + case DWC_DSTS_SETUP_COMP: + break; + case DWC_DSTS_SETUP_UPDT: + dwc_otg_read_setup_packet((u32 *)pcd->req); + break; + default: + break; + } + + /* Enable the Rx Status Queue Level interrupt */ + gintmsk = readl(&global_regs->gintmsk); + gintmsk |= RXSTSQLVL; + writel(gintmsk, &global_regs->gintmsk); + + /* Clear interrupt */ + writel(RXSTSQLVL, &global_regs->gintsts); + + return 1; +} + +/* + * This function starts the Zero-Length Packet for the IN status phase + * of a 2 stage control transfer. + */ +static void do_setup_in_status_phase(struct device_if *dev_if) +{ + struct device_out_ep_regs *out_regs = + dev_if->out_ep_regs[0]; + u32 doepctl, doeptsiz; + doeptsiz = 0; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl |= (CNAK | EPENA); + writel(doepctl, &out_regs->doepctl); +} + +static void udc_set_stall(int epid, int dir) +{ + if (dir) + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SSTALL, + &dev_if->in_ep_regs[epid]->diepctl); + else + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SSTALL, + &dev_if->out_ep_regs[epid]->doepctl); +} +/* + * This function handles EP0 Control transfers. + * + * The state of the control tranfers are tracked in ep0state + * + * A flag set indicates that it is not the first packet, so do not + * process setup data now. it has alreday been processed, just send the + * next data packet + */ +void handle_ep0(int in_flag) +{ + struct dwc_pcd *pcd = &dev_if->pcd; + struct dwc_ep *ep0 = &pcd->ep0; + struct usb_device_request *ctrl = pcd->req; + + /* handle inepint, only when more than 64 bytes to transfer*/ + if (in_flag & !ep0_urb->actual_length) + return; + + if (!ep0_urb->actual_length) { + if (ep0_recv_setup(ep0_urb)) { + udc_set_stall(0, ctrl->bmRequestType & USB_DIR_IN); + return; + } + ep0->xfer_buff = (u8 *)ep0_urb->buffer; + } else + ep0->xfer_buff += EP0_MAX_PACKET_SIZE; + + if (ep0_urb->actual_length <= EP0_MAX_PACKET_SIZE) { + ep0->xfer_len = ep0_urb->actual_length; + ep0_urb->actual_length = 0; + } else { + ep0->xfer_len = EP0_MAX_PACKET_SIZE; + ep0_urb->actual_length -= EP0_MAX_PACKET_SIZE; + } + + if (ctrl->bmRequestType & USB_DIR_IN) { + dwc_otg_ep_write_packet(ep0); + if (!ep0_urb->actual_length) + do_setup_in_status_phase(dev_if); + } else { + if (!ctrl->wLength) + dwc_otg_ep_write_packet(ep0); + else + udc_set_stall(0, ctrl->bmRequestType & USB_DIR_OUT); + } +} + +/* + * This function reads the Device All Endpoints Interrupt register and + * returns the OUT endpoint interrupt bits. + */ +static u32 dwc_otg_read_dev_all_out_ep_intr(void) +{ + u32 v; + + v = readl(&dev_if->dev_global_regs->daint) & + readl(&dev_if->dev_global_regs->daintmsk); + return (v & 0xffff0000) >> 16; +} + +/* + * This function reads the Device All Endpoints Interrupt register and + * returns the IN endpoint interrupt bits. + */ +static u32 dwc_otg_read_dev_all_in_ep_intr(void) +{ + u32 v; + + v = readl(&dev_if->dev_global_regs->daint) & + readl(&dev_if->dev_global_regs->daintmsk); + return v & 0xffff; +} + +/* This function returns the Device OUT EP Interrupt register */ +static u32 dwc_otg_read_doep_intr(struct dwc_ep *ep) +{ + u32 v; + + v = readl(&dev_if->out_ep_regs[ep->num]->doepint) & + readl(&dev_if->dev_global_regs->doepmsk); + return v; +} + +/*This function returns the Device IN EP Interrupt register */ +static u32 dwc_otg_read_diep_intr(struct dwc_ep *ep) +{ + u32 v; + + v = readl(&dev_if->in_ep_regs[ep->num]->diepint) & + readl(&dev_if->dev_global_regs->diepmsk); + return v; +} + +/* + * This function configures EPO to receive SETUP packets. + * + * Program the following fields in the endpoint specific registers for Control + * OUT EP 0, in order to receive a setup packet: + * + * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets) + * + * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup + * packets) + * + * In DMA mode, DOEPDMA0 Register with a memory address to store any setup + * packets received + */ +static void ep0_out_start(void) +{ + u32 temp; + + /* program transfer size*/ + temp = 8 * 3; + /* program packet count*/ + temp |= PKTCNT; + /* program setup packet count */ + temp |= (3 << SUPCNT_SHIFT); + writel(temp, &dev_if->out_ep_regs[0]->doeptsiz); +} + +/* should be called after set address is received */ +void udc_set_address_controller(u32 address) +{ + u32 dcfg; + + dcfg = readl(&dev_if->dev_global_regs->dcfg); + dcfg &= ~DEVADDRMSK; + dcfg |= address << DEVADDR_SHIFT; + writel(dcfg, &dev_if->dev_global_regs->dcfg); + + usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0); +} + +/* should be called after set configuration is received */ +static void dwc_otg_bulk_out_activate(void) +{ + struct device_out_ep_regs *out_regs = + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + u32 doepctl, doeptsiz, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_OUT_ENDPOINT + DAINTMASK_OUT_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl &= ~DOEPCTL_MPSMSK; + doepctl &= ~EPTYPEMSK; + doepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE | + CNAK | EPENA | USBACTEP | DATA0PID + | (EPTYPE_BULK << EPTYPE_SHIFT)); + writel(doepctl, &out_regs->doepctl); +} + +/* should be called after set configuration is received */ +static void dwc_otg_bulk_in_activate(void) +{ + struct device_in_ep_regs *in_regs = + dev_if->in_ep_regs[UDC_IN_ENDPOINT]; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + u32 diepctl, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_IN_ENDPOINT + DAINTMASK_IN_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + + diepctl = readl(&in_regs->diepctl); + diepctl &= ~DIEPCTL_MPSMSK; + diepctl &= ~EPTYPEMSK; + diepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE + | USBACTEP | DATA0PID + | (EPTYPE_BULK << EPTYPE_SHIFT)); + writel(diepctl, &in_regs->diepctl); +} + +static void dwc_otg_int_in_activate(void) +{ + struct device_in_ep_regs *in_regs = + dev_if->in_ep_regs[UDC_INT_ENDPOINT]; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + u32 diepctl, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_INT_ENDPOINT + DAINTMASK_IN_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + + diepctl = readl(&in_regs->diepctl); + diepctl &= ~DIEPCTL_MPSMSK; + diepctl &= ~EPTYPEMSK; + diepctl |= (UDC_INT_PACKET_SIZE + | USBACTEP | DATA0PID + | (EPTYPE_INT << EPTYPE_SHIFT)); + writel(diepctl, &in_regs->diepctl); +} + +/* should be called after set configuration is received */ +void udc_set_configuration_controller(u32 config) +{ + dwc_otg_bulk_out_activate(); + dwc_otg_bulk_in_activate(); + dwc_otg_int_in_activate(); + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); +} + +/* should be called to receive next packet */ +static void dwc_otg_bulk_out_enable(void) +{ + struct device_out_ep_regs *out_regs = + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; + u32 doepctl, doeptsiz; + + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl |= CNAK | EPENA; + writel(doepctl, &out_regs->doepctl); +} + +/* This interrupt indicates that an OUT EP has a pending Interrupt. */ + +static int dwc_otg_pcd_handle_out_ep_intr(void) +{ + u32 ep_intr; + u32 doepint; + u32 epnum = 0; + struct dwc_ep *dwc_ep; + struct device_out_ep_regs **out_ep_regs + = dev_if->out_ep_regs; + + /* Read in the device interrupt bits */ + ep_intr = dwc_otg_read_dev_all_out_ep_intr(); + while (ep_intr) { + if (ep_intr & 0x1) { + dwc_ep = get_out_ep(epnum); + doepint = dwc_otg_read_doep_intr(dwc_ep); + + /* Transfer complete */ + if (doepint & XFERCOMPL) { + /* Clear xfercompl */ + writel(XFERCOMPL, &out_ep_regs[epnum]->doepint); + if (!epnum) + ep0_out_start(); + else if (epnum == UDC_OUT_ENDPOINT) + dwc_otg_bulk_out_enable(); + } + /* Setup Phase Done (control EPs) */ + if (doepint & SETUP) { + writel(SETUP, &out_ep_regs[epnum]->doepint); + handle_ep0(0); + } + } + epnum++; + ep_intr >>= 1; + } + return 1; +} + +/* This interrupt indicates that an IN EP has a pending Interrupt. */ + +static int dwc_otg_pcd_handle_in_ep_intr(void) +{ + u32 ep_intr; + u32 diepint; + u32 epnum = 0; + struct dwc_ep *dwc_ep; + struct device_in_ep_regs **in_ep_regs + = dev_if->in_ep_regs; + + /* Read in the device interrupt bits */ + ep_intr = dwc_otg_read_dev_all_in_ep_intr(); + while (ep_intr) { + if (ep_intr & 0x1) { + dwc_ep = get_in_ep(epnum); + diepint = dwc_otg_read_diep_intr(dwc_ep); + + /* IN token received when txfifo empty */ + if (diepint & INTKNTXFEMP) { + /* Clear xfercompl */ + writel(INTKNTXFEMP, + &in_ep_regs[epnum]->diepint); + if (!epnum) + handle_ep0(1); + else if (epnum == UDC_IN_ENDPOINT) + dw_udc_epn_tx(dwc_ep); + } + } + epnum++; + ep_intr >>= 1; + } + return 1; +} + +static void dwc_otg_flush_tx_fifo(const int num) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + u32 val = 0; + int count = 0; + + val = readl(&global_regs->grstctl); + val |= TXFFLSH; + val &= ~TXFNUM; + val |= (num << TXFNUM_SHIFT); + writel(val, &global_regs->grstctl); + + do { + val = readl(&global_regs->grstctl); + if (++count > 10000) + break; + udelay(1); + } while (val & TXFFLSH); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +static void dwc_otg_flush_rx_fifo(void) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + int count = 0; + u32 val = 0; + + val = readl(&global_regs->grstctl); + val |= RXFFLSH; + writel(val, &global_regs->grstctl); + + do { + val = readl(&global_regs->grstctl); + if (++count > 10000) + break; + udelay(1); + } while (val & RXFFLSH); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/* + * This interrupt occurs when a USB Reset is detected. When the USB Reset + * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set + * to IDLE. + * + */ +static int dwc_otg_pcd_handle_usb_reset_intr(void) +{ + u32 temp; + u32 i; + u32 gintmsk; + struct device_out_ep_regs **out_ep_regs + = dev_if->out_ep_regs; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + struct core_global_regs *core_global_regs + = dev_if->core_global_regs; + /* Set NAK for all OUT EPs */ + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + temp = readl(&out_ep_regs[i]->doepctl); + temp |= SNAK; + writel(temp, &out_ep_regs[i]->doepctl); + } + + /* Flush the NP Tx FIFO */ + dwc_otg_flush_tx_fifo(DWC_GRSTCTL_TXFNUM_ALL); + dwc_otg_flush_rx_fifo(); + writel((1 << (DAINTMASK_IN_SHIFT + 0)) + | (1 << (DAINTMASK_OUT_SHIFT + 0)), + &dev_global_regs->daintmsk); + + writel(SETUPMSK | XFERCOMPLMSK | AHBERRMSK | EPDISABLEDMSK, + &dev_global_regs->doepmsk); + + writel(INTKNTXFEMP, &dev_global_regs->diepmsk); + + gintmsk = readl(&core_global_regs->gintmsk); + gintmsk |= GOUTNAKEFF; + writel(gintmsk, &core_global_regs->gintmsk); + + /* program fifo size for ep0 */ + + writel(0x200, &core_global_regs->grxfsiz); + + temp = readl(&dev_if->in_ep_regs[0]->diepctl); + temp &= 0xFFC3FFFF; /* TxFNumBF = 0, bits 25:22 */ + writel(temp, &dev_if->in_ep_regs[0]->diepctl); + + temp = readl(&core_global_regs->gnptxfsiz); + + writel(0x2000000, &core_global_regs->gnptxfsiz); + + /* Reset Device Address */ + temp = readl(&dev_global_regs->dcfg); + temp &= ~DEVADDRMSK; + writel(temp, &dev_global_regs->dcfg); + + /* setup EP0 to receive SETUP packets */ + ep0_out_start(); + + /* Clear interrupt */ + writel(USBRESET, &core_global_regs->gintsts); + + UDCDBG("device reset in progess"); + usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0); + + return 1; +} + +/* + * 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) +{ + u32 temp; + 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 */ + temp = readl(&in_ep_regs->diepctl); + temp &= ~MPSMSK0; + temp |= DWC_DEP0CTL_MPS_64; + writel(temp, &in_ep_regs->diepctl); + + temp = readl(&out_ep_regs->doepctl); + /* Enable OUT EP for receive */ + temp |= EPENA; + writel(temp, &out_ep_regs->doepctl); +} + +/* + * Read the device status register and set the device speed in the + * data structure. + * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. + */ +static int dwc_otg_pcd_handle_enum_done_intr(void) +{ + u32 gusbcfg; + struct core_global_regs *global_regs = dev_if->core_global_regs; + dwc_otg_ep0_activate(); + + gusbcfg = readl(&global_regs->gusbcfg); + gusbcfg &= ~USBTRDTIMMSK; + gusbcfg |= PHYIF_16BIT; + gusbcfg |= (9 << USBTRDTIM_SHIFT); + writel(gusbcfg, &global_regs->gusbcfg); + /* Clear interrupt */ + writel(ENUMDONE, &global_regs->gintsts); + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + return 1; +} + +static u32 dwc_otg_read_core_intr(void) +{ + return readl(&dev_if->core_global_regs->gintsts) & + readl(&dev_if->core_global_regs->gintmsk); +} + +static void dwc_otg_init(const void *reg_base) +{ + struct dwc_pcd *pcd = &dev_if->pcd; + u32 offset; + u32 i; + + dev_if->core_global_regs = (struct core_global_regs *) reg_base; + dev_if->dev_global_regs = (struct device_global_regs *) ((u32)reg_base + + DWC_DEV_GLOBAL_REG_OFFSET); + + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + offset = i * DWC_EP_REG_OFFSET; + + dev_if->in_ep_regs[i] = (struct device_in_ep_regs *) + ((u32)reg_base + DWC_DEV_IN_EP_REG_OFFSET + offset); + + dev_if->out_ep_regs[i] = (struct device_out_ep_regs *) + ((u32)reg_base + DWC_DEV_OUT_EP_REG_OFFSET + offset); + } + + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + dev_if->data_fifo[i] = + (u32 *) ((u32)reg_base + DWC_OTG_DATA_FIFO_OFFSET + + (i * DWC_OTG_DATA_FIFO_SIZE)); + } + + dev_if->speed = 0; /* unknown */ + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + pcd->in_ep[i].num = i; + pcd->out_ep[i].num = i; + } +} + +/* + * This function initializes the DWC_otg controller registers and prepares the + * core for device mode + */ +static void dwc_otg_core_init(void) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + u32 ahbcfg, gintmsk, usbcfg; + /* Step 1: Program the GAHBCFG Register. */ + ahbcfg = DWC_NPTXEMPTYLVL_EMPTY | DWC_PTXEMPTYLVL_EMPTY; + writel(ahbcfg, &global_regs->gahbcfg); + + /* Step 2: write usbcfg regs*/ + usbcfg = readl(&global_regs->gusbcfg); + usbcfg |= SRPCAP | HNPCAP; + writel(usbcfg, &global_regs->gusbcfg); + + /* step3: write int_msk reg*/ + gintmsk = USBRESET | ENUMDONE | RXSTSQLVL | OUTEPINTR | INEPINTR; + writel(gintmsk, &global_regs->gintmsk); +} + +/* Switch on the UDC */ +static void usbotg_init(void) +{ + udc_device = NULL; +#ifdef APG_BOARD + dwc_otg_init((void *)0x11000000); +#else + dwc_otg_init((void *)CONFIG_SYS_USBD_BASE); +#endif + + /* Initialize the DWC_otg core. */ + dwc_otg_core_init(); + +} + +void udc_irq(void) +{ + u32 status; + + status = dwc_otg_read_core_intr(); + while (status) { + if (status & USBRESET) + dwc_otg_pcd_handle_usb_reset_intr(); + if (status & ENUMDONE) + dwc_otg_pcd_handle_enum_done_intr(); + if (status & RXSTSQLVL) + dwc_otg_pcd_handle_rx_status_q_level_intr(); + if (status & OUTEPINTR) + dwc_otg_pcd_handle_out_ep_intr(); + if (status & INEPINTR) + dwc_otg_pcd_handle_in_ep_intr(); + status = dwc_otg_read_core_intr(); + } + +} + +void udc_set_nak(int epid) +{ + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SNAK, + &dev_if->out_ep_regs[epid]->doepctl); + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SNAK, + &dev_if->in_ep_regs[epid]->diepctl); +} + +void udc_unset_nak(int epid) +{ + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | CNAK, + &dev_if->out_ep_regs[epid]->doepctl); + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | CNAK, + &dev_if->in_ep_regs[epid]->diepctl); +} + +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK); + return 0; +} + +static void udc_enable(struct usb_device_instance *device) +{ + struct dwc_pcd *pcd = &dev_if->pcd; + + UDCDBGA("enable device %p, status %d", device, device->status); + + /* Save the device structure pointer */ + udc_device = device; + + /* Setup ep0 urb */ + if (!ep0_urb) { + ep0_urb = + usbd_alloc_urb(udc_device, + udc_device->bus->endpoint_array); + pcd->req = + (struct usb_device_request *)&ep0_urb->device_request; + pcd->ep0.xfer_buff = (u8 *)ep0_urb->buffer; + } else { +#ifndef APG_BOARD + serial_printf("udc_enable: ep0_urb already allocated %p\n", + ep0_urb); +#endif + } +} + +void udc_connect(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dcfg; + + /* remove soft disconnect */ + dcfg = readl(&dev_regs->dctl); + dcfg &= ~SFTDISCON; + writel(dcfg, &dev_regs->dctl); +} + +void udc_disconnect(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dcfg; + + /* soft disconnect */ + dcfg = readl(&dev_regs->dctl); + dcfg |= SFTDISCON; + writel(dcfg, &dev_regs->dctl); + udelay(150); +} + +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* + * The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* + * Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. + * The DW USB client controller has the capability to detect when the + * USB cable is connected to a powered USB bus, so we will defer the + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later. + */ + + udc_enable(device); + +} + +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + /* + * Nothing to do here. Hob of this function has laready been + * done during init. + */ +} + +int is_usbd_high_speed(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dsts; + + dsts = readl(&dev_regs->dsts); + dsts &= ENUMSPDMSK; + if (dsts == DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ) + return 1; + else + return 0; +} + +int udc_init(void) +{ + phy_init(); + udc_disconnect(); + usbotg_init(); + return 0; +} diff --git a/include/usb/designware_otg.h b/include/usb/designware_otg.h new file mode 100644 index 0000000..d7b686b --- /dev/null +++ b/include/usb/designware_otg.h @@ -0,0 +1,527 @@ +/* + * (C) Copyright 2011 + * Pratyush Anand, ST Micoelectronics, pratyush.anand@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 + */ + +#ifndef __DW_OTG_H +#define __DW_OTG_H +/* temp def: will be removed TBD */ +#undef APH_BOARD 1 +#ifdef APG_BOARD +#include "types.h" +#define CONFIG_USBD_HS +#define CONFIG_DW_OTG +#endif + +#include "usbdevice.h" +/* USBTTY definitions */ +#define EP0_MAX_PACKET_SIZE 64 +#define UDC_INT_ENDPOINT 1 +#define UDC_INT_PACKET_SIZE 64 +#define UDC_OUT_ENDPOINT 2 +#define UDC_BULK_PACKET_SIZE 512 +#if defined(CONFIG_USBD_HS) +#define UDC_BULK_HS_PACKET_SIZE 512 +#endif +#define UDC_IN_ENDPOINT 3 +#define UDC_OUT_PACKET_SIZE 64 +#define UDC_IN_PACKET_SIZE 64 + +/* UDC endpoint definitions */ +#define UDC_EP0 0 +#define UDC_EP1 1 +#define UDC_EP2 2 +#define UDC_EP3 3 + +#define CMD_SIZE 12 +/* OTG Register Definitions */ + +/* + * The application interfaces with the HS OTG core by reading from and + * writing to the Control and Status Register (CSR) space through the + * AHB Slave interface. These registers are 32 bits wide, and the + * addresses are 32-bit-block aligned. + * CSRs are classified as follows: + * - Core Global Registers + * - Device Mode Registers + * - Device Global Registers + * - Device Endpoint Specific Registers + * - Host Mode Registers + * - Host Global Registers + * - Host Port CSRs + * - Host Channel Specific Registers + * + * Only the Core Global registers can be accessed in both Device and + * Host modes. When the HS OTG core is operating in one mode, either + * Device or Host, the application must not access registers from the + * other mode. When the core switches from one mode to another, the + * registers in the new mode of operation must be reprogrammed as they + * would be after a power-on reset. + */ + +/* + * DWC_otg Core registers. The core_global_regs structure defines the + * size and relative field offsets for the Core Global registers. + */ +struct core_global_regs { + /* OTG Control and Status Register. Offset: 000h */ + u32 gotgctl; + /* OTG Interrupt Register. Offset: 004h */ + u32 gotgint; + /* Core AHB Configuration Register. Offset: 008h */ + u32 gahbcfg; + +#define DWC_GLBINTRMASK 0x0001 +#define DWC_DMAENABLE 0x0020 +#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 +#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 +#define DWC_PTXEMPTYLVL_EMPTY 0x0100 +#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 + + /* Core USB Configuration Register. Offset: 00Ch */ + u32 gusbcfg; +#define PHYIF_16BIT (1 << 3) +#define SRPCAP (1 << 8) +#define HNPCAP (1 << 9) +#define TERM_SEL_DL_PULSE (1 << 22) +#define USBTRDTIM_SHIFT 10 +#define USBTRDTIMMSK (0xF << USBTRDTIM_SHIFT) + /* Core Reset Register. Offset: 010h */ + u32 grstctl; +#define DWC_GRSTCTL_TXFNUM_ALL 0x10 +#define CSFTRST (1 << 0) +#define INTKNQFLSH (1 << 3) +#define RXFFLSH (1 << 4) +#define TXFFLSH (1 << 5) +#define TXFNUM_SHIFT 6 +#define TXFNUM (0x1F << TXFNUM_SHIFT) +#define AHBIDLE ((u32)1 << 31) + /* Core Interrupt Register. Offset: 014h */ + u32 gintsts; +#define RXSTSQLVL (1 << 4) +#define NPTXFEMPTY (1 << 5) +#define GOUTNAKEFF (1 << 7) +#define USBRESET (1 << 12) +#define ENUMDONE (1 << 13) +#define INEPINTR (1 << 18) +#define OUTEPINTR (1 << 19) + /* Core Interrupt Mask Register. Offset: 018h */ + u32 gintmsk; + /* + * Receive Status Queue Read Register + * (Read Only) Offset: 01Ch + */ + u32 grxstsr; + /* + * Receive Status Queue Read & POP Register + * (Read Only) Offset: 020h + */ + u32 grxstsp; +#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */ +#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */ +#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */ +#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */ +#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */ +#define EPNUM_SHIFT 0 +#define EPNUMMSK (0xF << EPNUM_SHIFT) +#define BCNT_SHIFT 4 +#define BCNTMSK (0x7FF << BCNT_SHIFT) +#define PKTSTS_SHIFT 17 +#define PKTSTSMSK (0xF << PKTSTS_SHIFT) + /* Receive FIFO Size Register. Offset: 024h */ + u32 grxfsiz; +#define dwc_param_dev_rx_fifo_size_default 1064 + /* Non Periodic Transmit FIFO Size Register. Offset: 028h */ + u32 gnptxfsiz; +#define dwc_param_dev_nperio_tx_fifo_size_default 1024 + /* + * Non Periodic Transmit FIFO/Queue Status Register + * (Read Only). Offset: 02Ch + */ + u32 gnptxsts; +#define NPTXQSPCAVAIL_SHIFT 16 +#define NPTXQSPCAVAILMSK (0xFF << NPTXQSPCAVAIL_SHIFT) +#define NPTXFSPCAVAIL_SHIFT 0 +#define NPTXFSPCAVAILMSK (0xFFFF << NPTXFSPCAVAIL_SHIFT) + /* I2C Access Register. Offset: 030h */ + u32 gi2cctl; + /* PHY Vendor Control Register. Offset: 034h */ + u32 gpvndctl; + /* General Purpose Input/Output Register. Offset: 038h */ + u32 ggpio; + /* User ID Register. Offset: 03Ch */ + u32 guid; + /* Synopsys ID Register (Read Only). Offset: 040h */ + u32 gsnpsid; + /* User HW Config1 Register (Read Only). Offset: 044h */ + u32 ghwcfg1; + /* User HW Config2 Register (Read Only). Offset: 048h */ + + u32 ghwcfg2; +#define DWC_SLAVE_ONLY_ARCH 0 +#define DWC_EXT_DMA_ARCH 1 +#define DWC_INT_DMA_ARCH 2 + +#define DWC_MODE_HNP_SRP_CAPABLE 0 +#define DWC_MODE_SRP_ONLY_CAPABLE 1 +#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 +#define DWC_MODE_SRP_CAPABLE_DEVICE 3 +#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define DWC_MODE_SRP_CAPABLE_HOST 5 +#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 +#define DYNAMIC_FIFO (1 << 19) +#define NUM_DEV_EP_SHIFT 10 +#define NUM_DEV_EP (0xF << NUM_DEV_EP_SHIFT) +#define HSPHYTYPE_SHIFT 6 +#define HSPHYTYPEMSK (3 << HSPHYTYPE_SHIFT) +#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 +#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define TKNQDEPTH_SHIFT 26 +#define TKNQDEPTHMSK (0x1F << TKNQDEPTH_SHIFT) + + /* User HW Config3 Register (Read Only). Offset: 04Ch */ + u32 ghwcfg3; +#define DFIFO_DEPTH_SHIFT 16 +#define DFIFO_DEPTH ((u32)0xFFFF << DFIFO_DEPTH_SHIFT) + /* User HW Config4 Register (Read Only). Offset: 050h */ + u32 ghwcfg4; +#define NUM_DEV_PERIO_IN_EP_SHIFT 0 +#define NUM_DEV_PERIO_IN_EP (0xF << NUM_DEV_PERIO_IN_EP_SHIFT) +#define DED_FIFO_EN (1 << 25) +#define NUM_IN_EPS_SHIFT 26 +#define NUM_IN_EPS (0xF << NUM_IN_EPS_SHIFT) +#define UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define UTMI_PHY_DATA_WIDTH (0x3 << UTMI_PHY_DATA_WIDTH_SHIFT) + /* Reserved Offset: 054h-0FFh */ + u32 reserved[43]; + /* Host Periodic Transmit FIFO Size Register. Offset: 100h */ + u32 hptxfsiz; + + /* + * Device Periodic Transmit FIFO#n Register, if dedicated fifos are + * disabled. Otherwise Device Transmit FIFO#n Register. + * + * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15) + */ + u32 dptxfsiz_dieptxf[15]; +#define dwc_param_dev_tx_fifo_size_default 256 +#define dwc_param_dev_perio_tx_fifo_size_default 256 +}; + +/* + * Device Global Registers. Offsets 800h-BFFh + * + * The following structures define the size and relative field offsets for the + * Device Mode Registers. + * + * These registers are visible only in Device mode and must not be accessed in + * Host mode, as the results are unknown. + */ +struct device_global_regs { /* CONFIG_DWC_OTG_REG_LE */ + /* Device Configuration Register. Offset: 800h */ + u32 dcfg; +#define DWC_DCFG_FRAME_INTERVAL_80 0 +#define DWC_DCFG_FRAME_INTERVAL_85 1 +#define DWC_DCFG_FRAME_INTERVAL_90 2 +#define DWC_DCFG_FRAME_INTERVAL_95 3 +#define DWC_DCFG_FRAME_INTERVAL_MASK 3 +#define PERFRINT_SHIFT 11 +#define DEVSPDMSK (0x3 << 0) +#define DEVADDR_SHIFT 4 +#define DEVADDRMSK (0x7F << DEVADDR_SHIFT) +#define NZSTSOUTHSHK (1 << 2) + /* Device Control Register. Offset: 804h */ + u32 dctl; +#define RMTWKUPSIG (1 << 0) +#define SFTDISCON (1 << 1) +#define CGNPINNAK (1 << 7) + /* Device Status Register (Read Only). Offset: 808h */ + u32 dsts; +#define ENUMSPD_SHIFT 1 +#define ENUMSPDMSK (3 << ENUMSPD_SHIFT) +#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 +#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 +#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 +#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 + /* Reserved. Offset: 80Ch */ + u32 unused; + /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */ + u32 diepmsk; +#define TIMEOUTMSK (1 << 3) +#define INTKNTXFEMP (1 << 4) +#define INTKNEPMISMSK (1 << 5) +#define INEPNAKEFFMSK (1 << 6) +#define TXFIFOUNDRN (1 << 8) + /* Device OUT Endpoint Common Interrupt Mask Register. Offset: 814h */ + u32 doepmsk; +#define XFERCOMPLMSK (1 << 0) +#define EPDISABLEDMSK (1 << 1) +#define AHBERRMSK (1 << 2) +#define SETUPMSK (1 << 3) +#define INTKNTXFEMPMSK (1 << 4) + /* Device All Endpoints Interrupt Register. Offset: 818h */ + u32 daint; + /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */ + u32 daintmsk; +#define DAINTMASK_IN_SHIFT 0 +#define DAINTMASK_OUT_SHIFT 16 + /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */ + u32 dtknqr1; +#define EPTK0_5_SHIFT 8 +#define EPTK0_5MSK ((u32)0xFFFFFF << EPTK0_5_SHIFT) +#define INTKNWPTR_SHIFT 0 +#define INTKNWPTRMSK ((u32)0x1F << INTKNWPTR_SHIFT) + /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */ + u32 dtknqr2; + /* Device VBUS discharge Register. Offset: 828h */ + u32 dvbusdis; + /* Device VBUS Pulse Register. Offset: 82Ch */ + u32 dvbuspulse; + /* Device IN Token Queue Read Register-3 (Read Only). Offset: 830h */ + u32 dtknqr3_dthrctl; + /* Device IN Token Queue Read Register-4 (Read Only). Offset: 834h */ + u32 dtknqr4_fifoemptymsk; +}; +/* + * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh + * + * There will be one set of endpoint registers per logical endpoint implemented. + * + * These registers are visible only in Device mode and must not be accessed in + * Host mode, as the results are unknown. + */ +struct device_in_ep_regs { + /* + * Device IN Endpoint Control Register. + * Offset:900h + (ep_num * 20h) + 00h + */ + u32 diepctl; +#define EPENA ((u32)1 << 31) +#define EPDIS (1 << 30) +#define SNAK (1 << 27) +#define CNAK (1 << 26) +#define SSTALL (1 << 21) +#define MPS_SHIFT 0 +#define MPSMSK0 (3 << MPS_SHIFT) +#define DWC_DEP0CTL_MPS_64 0 +#define DWC_DEP0CTL_MPS_32 1 +#define DWC_DEP0CTL_MPS_16 2 +#define DWC_DEP0CTL_MPS_8 3 +#define DIEPCTL_MPSMSK (0x7FF << MPS_SHIFT) + /* Reserved. Offset:900h + (ep_num * 20h) + 04h */ + u32 reserved04; + /* + * Device IN Endpoint Interrupt Register. + * Offset:900h + (ep_num * 20h) + 08h + */ + u32 diepint; +#define TXFEMP (1 << 7) +#define INTKNTXFEMP (1 << 4) +#define XFERCOMPL (1 << 0) + /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ + u32 reserved0C; + /* Device IN Endpoint Transfer Size Register. + * Offset:900h + (ep_num * 20h) + 10h + */ + u32 dieptsiz; +#define PKTCNT_SHIFT 19 + /* + * Device IN Endpoint DMA Address Register. + * Offset:900h + (ep_num * 20h) + 14h + */ + u32 diepdma; + /* Reserved. + * Offset:900h + (ep_num * 20h) + 18h - 900h + (ep_num * 20h) + 1Ch + */ + u32 dtxfsts; + /* + * Reserved. + * Offset:900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch + */ + u32 reserved18; +}; + +/* + * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh + * + * There will be one set of endpoint registers per logical endpoint implemented. + * + * These registers are visible only in Device mode and must not be accessed in + * Host mode, as the results are unknown. + */ +struct device_out_ep_regs { + /* + * Device OUT Endpoint Control Register. + * Offset:B00h + (ep_num * 20h) + 00h + */ + u32 doepctl; +#define DOEPCTL_MPSMSK 0x7FF +#define USBACTEP (1 << 15) +#define EPTYPE_SHIFT 18 +#define EPTYPEMSK (0x3 << EPTYPE_SHIFT) +#define EPTYPE_BULK 0x2 +#define EPTYPE_INT 0x3 +#define DATA0PID (1 << 28) +#define DATA1PID (1 << 29) +#define DPIDMSK (1 << 16) + /* + * Device OUT Endpoint Frame number Register. + * Offset: B00h + (ep_num * 20h) + 04h + */ + u32 doepfn; + /* + * Device OUT Endpoint Interrupt Register. + * Offset:B00h + (ep_num * 20h) + 08h + */ + u32 doepint; +#define XFERCOMPL (1 << 0) +#define EPDISBLD (1 << 1) +#define AHBERR (1 << 2) +#define SETUP (1 << 3) + /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ + u32 reserved0C; + /* + * Device OUT Endpoint Transfer Size Register. + * Offset: B00h + (ep_num * 20h) + 10h + */ + u32 doeptsiz; +#define XFERSIZE_SHIFT 0 +#define XFERSIZEMSK 0x3F +#define PKTCNT_SHIFT 19 +#define PKTCNT (3 << 19) +#define SUPCNT_SHIFT 29 +#define SUPCNTMSK (3 << SUPCNT_SHIFT) + /* + * Device OUT Endpoint DMA Address Register. + * Offset:B00h + (ep_num * 20h) + 14h + */ + u32 doepdma; + /* + * Reserved. + * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch + */ + u32 unused[2]; +}; +#define MAX_EPS_CHANNELS 4 + +/* + * The dwc_ep structure represents the state of a single endpoint when acting in + * device mode. It contains the data items needed for an endpoint to be + * activated and transfer packets. + */ +struct dwc_ep { + /* EP number used for register address lookup */ + u8 num; + /* EP direction 0 = OUT */ +#if 0 + u8 is_in; +#endif + /* pointer to the transfer buffer */ + u8 *xfer_buff; + /* Number of bytes to transfer */ + u32 xfer_len; +}; + +/* + * DWC_otg PCD Structure. + * This structure encapsulates the data for the dwc_otg PCD. + */ +struct dwc_pcd { +#if 0 + /* USB gadget */ + /* Current configuration */ + u8 configuration; + /* Current interface */ + u8 interface; + /* Current alternate settinng */ + u8 alternate; + /* Current Address */ + u16 address; + /* device state */ +/* usb_device_state_t device_state; */ /* current USB Device state */ + /* + * SETUP packet for EP0. This structure is allocated as a DMA buffer on + * PCD initialization with enough space for up to 3 setup packets. + */ +#endif + struct usb_device_request *req; + /* Array of EPs. */ + struct dwc_ep ep0; + /* Array of IN EPs. */ + struct dwc_ep in_ep[MAX_EPS_CHANNELS - 1]; + /* Array of OUT EPs. */ + struct dwc_ep out_ep[MAX_EPS_CHANNELS - 1]; +}; + +/* + * The device_if structure contains information needed to manage the DWC_otg + * controller acting in device mode. It represents the programming view of the + * device-specific aspects of the controller. + */ +struct device_if { + struct core_global_regs *core_global_regs; + /* Common configuration information */ + + /* Device Global Registers starting at offset 800h */ + struct device_global_regs *dev_global_regs; +#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 + + /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */ + struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS]; +#define DWC_DEV_IN_EP_REG_OFFSET 0x900 +#define DWC_EP_REG_OFFSET 0x20 + + /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ + struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS]; +#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 + + /* Push/pop addresses for endpoints or host channels.*/ + u32 *data_fifo[MAX_EPS_CHANNELS]; +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 +#define DWC_OTG_DATA_FIFO_SIZE 0x1000 + + struct dwc_pcd pcd; + int speed; +}; + + +/* Function declarations */ + +void phy_init(void); +void udc_irq(void); + +void udc_set_nak(int epid); +void udc_unset_nak(int epid); +int udc_endpoint_write(struct usb_endpoint_instance *endpoint); +int udc_init(void); +/* void udc_enable(struct usb_device_instance *device);*/ +void udc_disable(void); +void udc_connect(void); +void udc_disconnect(void); +void udc_startup_events(struct usb_device_instance *device); +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint); +void udc_set_configuration_controller(u32); +void udc_set_address_controller(u32); + +#endif /* __DW_UDC_H */