--- a/drivers/Makefile +++ b/drivers/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ +obj-$(CONFIG_USB_DWC_OTG) += usb/dwc/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ obj-$(CONFIG_PCI) += usb/ --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -116,6 +116,8 @@ source "drivers/usb/host/Kconfig" source "drivers/usb/musb/Kconfig" +source "drivers/usb/dwc/Kconfig" + source "drivers/usb/class/Kconfig" source "drivers/usb/storage/Kconfig" --- /dev/null +++ b/drivers/usb/dwc/Kconfig @@ -0,0 +1,44 @@ +# +# USB Dual Role (OTG-ready) Controller Drivers +# for silicon based on Synopsys DesignWare IP +# + +comment "Enable Host or Gadget support for DesignWare OTG controller" +depends on !USB && USB_GADGET=n + +config USB_DWC_OTG + tristate "Synopsys DWC OTG Controller" + depends on USB + help + This driver provides USB Device Controller support for the + Synopsys DesignWare USB OTG Core used on the Cavium CNS34xx SOC. + +config DWC_DEBUG + bool "Enable DWC Debugging" + depends on USB_DWC_OTG + default n + help + Enable DWC driver debugging + +choice + prompt "DWC Mode Selection" + depends on USB_DWC_OTG + default DWC_HOST_ONLY + help + Select the DWC Core in OTG, Host only, or Device only mode. + +config DWC_HOST_ONLY + bool "DWC Host Only Mode" + +config DWC_OTG_MODE + bool "DWC OTG Mode" + select USB_GADGET + select USB_GADGET_SELECTED + +config DWC_DEVICE_ONLY + bool "DWC Device Only Mode" + select USB_GADGET + select USB_GADGET_SELECTED + +endchoice + --- /dev/null +++ b/drivers/usb/dwc/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for DWC_otg Highspeed USB controller driver +# + +EXTRA_CFLAGS += -DDWC_HS_ELECT_TST +#EXTRA_CFLAGS += -Dlinux -DDWC_HS_ELECT_TST +#EXTRA_CFLAGS += -DDWC_EN_ISOC + +ifneq ($(CONFIG_DWC_HOST_ONLY),) +EXTRA_CFLAGS += -DDWC_HOST_ONLY +endif + +ifneq ($(CONFIG_DWC_DEVICE_ONLY),) +EXTRA_CFLAGS += -DDWC_DEVICE_ONLY +endif + +ifneq ($(CONFIG_DWC_DEBUG),) +EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_USB_DWC_OTG) := dwc_otg.o + +dwc_otg-objs := otg_driver.o otg_attr.o +dwc_otg-objs += otg_cil.o otg_cil_intr.o +dwc_otg-objs += otg_pcd.o otg_pcd_intr.o +dwc_otg-objs += otg_hcd.o otg_hcd_intr.o otg_hcd_queue.o --- /dev/null +++ b/drivers/usb/dwc/otg_attr.c @@ -0,0 +1,886 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $ + * $Revision: #31 $ + * $Date: 2008/07/15 $ + * $Change: 1064918 $ + * + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless + * otherwise expressly agreed to in writing between Synopsys and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product under + * any End User Software License Agreement or Agreement for Licensed Product + * with Synopsys or any supplement thereto. You are permitted to use and + * redistribute this Software in source and binary forms, with or without + * modification, provided that redistributions of source code must retain this + * notice. You may not view, use, disclose, copy or distribute this file or + * any information contained herein except pursuant to this license grant from + * Synopsys. If you do not agree with this notice, including the disclaimer + * below, then you are not authorized to use the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * ========================================================================== */ + +/** @file + * + * The diagnostic interface will provide access to the controller for + * bringing up the hardware and testing. The Linux driver attributes + * feature will be used to provide the Linux Diagnostic + * Interface. These attributes are accessed through sysfs. + */ + +/** @page "Linux Module Attributes" + * + * The Linux module attributes feature is used to provide the Linux + * Diagnostic Interface. These attributes are accessed through sysfs. + * The diagnostic interface will provide access to the controller for + * bringing up the hardware and testing. + + + The following table shows the attributes. +
Name | +Description | +Access | +
mode | +Returns the current mode: 0 for device mode, 1 for host mode | +Read | +
hnpcapable | +Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register. + Read returns the current value. | +Read/Write | +
srpcapable | +Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register. + Read returns the current value. | +Read/Write | +
hnp | +Initiates the Host Negotiation Protocol. Read returns the status. | +Read/Write | +
srp | +Initiates the Session Request Protocol. Read returns the status. | +Read/Write | +
buspower | +Gets or sets the Power State of the bus (0 - Off or 1 - On) | +Read/Write | +
bussuspend | +Suspends the USB bus. | +Read/Write | +
busconnected | +Gets the connection status of the bus | +Read | +
gotgctl | +Gets or sets the Core Control Status Register. | +Read/Write | +
gusbcfg | +Gets or sets the Core USB Configuration Register | +Read/Write | +
grxfsiz | +Gets or sets the Receive FIFO Size Register | +Read/Write | +
gnptxfsiz | +Gets or sets the non-periodic Transmit Size Register | +Read/Write | +
gpvndctl | +Gets or sets the PHY Vendor Control Register | +Read/Write | +
ggpio | +Gets the value in the lower 16-bits of the General Purpose IO Register + or sets the upper 16 bits. | +Read/Write | +
guid | +Gets or sets the value of the User ID Register | +Read/Write | +
gsnpsid | +Gets the value of the Synopsys ID Regester | +Read | +
devspeed | +Gets or sets the device speed setting in the DCFG register | +Read/Write | +
enumspeed | +Gets the device enumeration Speed. | +Read | +
hptxfsiz | +Gets the value of the Host Periodic Transmit FIFO | +Read | +
hprt0 | +Gets or sets the value in the Host Port Control and Status Register | +Read/Write | +
regoffset | +Sets the register offset for the next Register Access | +Read/Write | +
regvalue | +Gets or sets the value of the register at the offset in the regoffset attribute. | +Read/Write | +
remote_wakeup | +On read, shows the status of Remote Wakeup. On write, initiates a remote + wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote + Wakeup signalling bit in the Device Control Register is set for 1 + milli-second. | +Read/Write | +
regdump | +Dumps the contents of core registers. | +Read | +
spramdump | +Dumps the contents of core registers. | +Read | +
hcddump | +Dumps the current HCD state. | +Read | +
hcd_frrem | +Shows the average value of the Frame Remaining + field in the Host Frame Number/Frame Remaining register when an SOF interrupt + occurs. This can be used to determine the average interrupt latency. Also + shows the average Frame Remaining value for start_transfer and the "a" and + "b" sample points. The "a" and "b" sample points may be used during debugging + bto determine how long it takes to execute a section of the HCD code. | +Read | +
rd_reg_test | +Displays the time required to read the GNPTXFSIZ register many times + (the output shows the number of times the register is read). + | Read | +
wr_reg_test | +Displays the time required to write the GNPTXFSIZ register many times + (the output shows the number of times the register is written). + | Read | +
dwc_otg_hc_start_transfer
. The caller must ensure there is
+ * sufficient space in the request queue and Tx Data FIFO. This function
+ * should only be called in Slave mode. In DMA mode, the controller acts
+ * autonomously to complete transfers programmed to a host channel.
+ *
+ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
+ * if there is any data remaining to be queued. For an IN transfer, another
+ * data packet is always requested. For the SETUP phase of a control transfer,
+ * this function does nothing.
+ *
+ * @return 1 if a new request is queued, 0 if no more requests are required
+ * for this transfer.
+ */
+int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t *core_if, dwc_hc_t *hc)
+{
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+
+ if (hc->do_split) {
+ /* SPLITs always queue just once per channel */
+ return 0;
+ }
+ else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+ /* SETUPs are queued only once since they can't be NAKed. */
+ return 0;
+ }
+ else if (hc->ep_is_in) {
+ /*
+ * Always queue another request for other IN transfers. If
+ * back-to-back INs are issued and NAKs are received for both,
+ * the driver may still be processing the first NAK when the
+ * second NAK is received. When the interrupt handler clears
+ * the NAK interrupt for the first NAK, the second NAK will
+ * not be seen. So we can't depend on the NAK interrupt
+ * handler to requeue a NAKed request. Instead, IN requests
+ * are issued each time this function is called. When the
+ * transfer completes, the extra requests for the channel will
+ * be flushed.
+ */
+ hcchar_data_t hcchar;
+ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 0;
+ DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n", hcchar.d32);
+ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+ hc->requests++;
+ return 1;
+ }
+ else {
+ /* OUT transfers. */
+ if (hc->xfer_count < hc->xfer_len) {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ hcchar_data_t hcchar;
+ dwc_otg_hc_regs_t *hc_regs;
+ hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+ }
+
+ /* Load OUT packet into the appropriate Tx FIFO. */
+ dwc_otg_hc_write_packet(core_if, hc);
+ hc->requests++;
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
+/**
+ * Starts a PING transfer. This function should only be called in Slave mode.
+ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled.
+ */
+void dwc_otg_hc_do_ping(dwc_otg_core_if_t *core_if, dwc_hc_t *hc)
+{
+ hcchar_data_t hcchar;
+ hctsiz_data_t hctsiz;
+ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+
+ hctsiz.d32 = 0;
+ hctsiz.b.dopng = 1;
+ hctsiz.b.pktcnt = 1;
+ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32);
+
+ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 0;
+ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+}
+
+/*
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+void dwc_otg_hc_write_packet(dwc_otg_core_if_t *core_if, dwc_hc_t *hc)
+{
+ uint32_t i;
+ uint32_t remaining_count;
+ uint32_t byte_count;
+ uint32_t dword_count;
+
+ uint32_t *data_buff = (uint32_t *)(hc->xfer_buff);
+ uint32_t *data_fifo = core_if->data_fifo[hc->hc_num];
+
+ remaining_count = hc->xfer_len - hc->xfer_count;
+ if (remaining_count > hc->max_packet) {
+ byte_count = hc->max_packet;
+ }
+ else {
+ byte_count = remaining_count;
+ }
+
+ dword_count = (byte_count + 3) / 4;
+
+ if ((((unsigned long)data_buff) & 0x3) == 0) {
+ /* xfer_buff is DWORD aligned. */
+ for (i = 0; i < dword_count; i++, data_buff++)
+ {
+ dwc_write_reg32(data_fifo, *data_buff);
+ }
+ }
+ else {
+ /* xfer_buff is not DWORD aligned. */
+ for (i = 0; i < dword_count; i++, data_buff++)
+ {
+ dwc_write_reg32(data_fifo, get_unaligned(data_buff));
+ }
+ }
+
+ hc->xfer_count += byte_count;
+ hc->xfer_buff += byte_count;
+}
+
+/**
+ * Gets the current USB frame number. This is the frame number from the last
+ * SOF packet.
+ */
+uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t *core_if)
+{
+ dsts_data_t dsts;
+ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);
+
+ /* read current frame/microframe number from DSTS register */
+ return dsts.b.soffn;
+}
+
+/**
+ * This function reads a setup packet from the Rx FIFO into the destination
+ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl)
+ * Interrupt routine when a SETUP packet has been received in Slave mode.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dest Destination buffer for packet data.
+ */
+void dwc_otg_read_setup_packet(dwc_otg_core_if_t *core_if, uint32_t *dest)
+{
+ /* Get the 8 bytes of a setup transaction data */
+
+ /* Pop 2 DWORDS off the receive data FIFO into memory */
+ dest[0] = dwc_read_reg32(core_if->data_fifo[0]);
+ dest[1] = dwc_read_reg32(core_if->data_fifo[0]);
+}
+
+
+/**
+ * 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.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_activate(dwc_otg_core_if_t *core_if, dwc_ep_t *ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dsts_data_t dsts;
+ depctl_data_t diepctl;
+ depctl_data_t doepctl;
+ dctl_data_t dctl = { .d32 = 0 };
+
+ /* Read the Device Status and Endpoint 0 Control registers */
+ dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts);
+ diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl);
+ doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl);
+
+ /* Set the MPS of the IN EP based on the enumeration speed */
+ switch (dsts.b.enumspd) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ diepctl.b.mps = DWC_DEP0CTL_MPS_64;
+ break;
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ diepctl.b.mps = DWC_DEP0CTL_MPS_8;
+ break;
+ }
+
+ dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+ /* Enable OUT EP for receive */
+ doepctl.b.epena = 1;
+ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV,"doepctl0=%0x\n",
+ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl));
+ DWC_DEBUGPL(DBG_PCDV,"diepctl0=%0x\n",
+ dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl));
+#endif
+ dctl.b.cgnpinnak = 1;
+
+ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+ DWC_DEBUGPL(DBG_PCDV,"dctl=%0x\n",
+ dwc_read_reg32(&dev_if->dev_global_regs->dctl));
+}
+
+/**
+ * This function activates an EP. The Device EP control register for
+ * the EP is configured as defined in the ep structure. Note: This
+ * function is not used for EP0.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to activate.
+ */
+void dwc_otg_ep_activate(dwc_otg_core_if_t *core_if, dwc_ep_t *ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ depctl_data_t depctl;
+ volatile uint32_t *addr;
+ daint_data_t daintmsk = { .d32 = 0 };
+
+ DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num,
+ (ep->is_in?"IN":"OUT"));
+
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = &dev_if->in_ep_regs[ep->num]->diepctl;
+ daintmsk.ep.in = 1<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.
+ */
+typedef struct dwc_ep
+{
+ /** EP number used for register address lookup */
+ uint8_t num;
+ /** EP direction 0 = OUT */
+ unsigned is_in : 1;
+ /** EP active. */
+ unsigned active : 1;
+
+ /** Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic Tx FIFO
+ If dedicated Tx FIFOs are enabled for all IN Eps - Tx FIFO # FOR IN EPs*/
+ unsigned tx_fifo_num : 4;
+ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */
+ unsigned type : 2;
+#define DWC_OTG_EP_TYPE_CONTROL 0
+#define DWC_OTG_EP_TYPE_ISOC 1
+#define DWC_OTG_EP_TYPE_BULK 2
+#define DWC_OTG_EP_TYPE_INTR 3
+
+ /** DATA start PID for INTR and BULK EP */
+ unsigned data_pid_start : 1;
+ /** Frame (even/odd) for ISOC EP */
+ unsigned even_odd_frame : 1;
+ /** Max Packet bytes */
+ unsigned maxpacket : 11;
+
+ /** Max Transfer size */
+ unsigned maxxfer : 16;
+
+ /** @name Transfer state */
+ /** @{ */
+
+ /**
+ * Pointer to the beginning of the transfer buffer -- do not modify
+ * during transfer.
+ */
+
+ uint32_t dma_addr;
+
+ uint32_t dma_desc_addr;
+ dwc_otg_dma_desc_t* desc_addr;
+
+
+ uint8_t *start_xfer_buff;
+ /** pointer to the transfer buffer */
+ uint8_t *xfer_buff;
+ /** Number of bytes to transfer */
+ unsigned xfer_len : 19;
+ /** Number of bytes transferred. */
+ unsigned xfer_count : 19;
+ /** Sent ZLP */
+ unsigned sent_zlp : 1;
+ /** Total len for control transfer */
+ unsigned total_len : 19;
+
+ /** stall clear flag */
+ unsigned stall_clear_flag : 1;
+
+ /** Allocated DMA Desc count */
+ uint32_t desc_cnt;
+
+ uint32_t aligned_dma_addr;
+ uint32_t aligned_buf_size;
+ uint8_t *aligned_buf;
+
+
+#ifdef DWC_EN_ISOC
+ /**
+ * Variables specific for ISOC EPs
+ *
+ */
+ /** DMA addresses of ISOC buffers */
+ uint32_t dma_addr0;
+ uint32_t dma_addr1;
+
+ uint32_t iso_dma_desc_addr;
+ dwc_otg_dma_desc_t* iso_desc_addr;
+
+ /** pointer to the transfer buffers */
+ uint8_t *xfer_buff0;
+ uint8_t *xfer_buff1;
+
+ /** number of ISOC Buffer is processing */
+ uint32_t proc_buf_num;
+ /** Interval of ISOC Buffer processing */
+ uint32_t buf_proc_intrvl;
+ /** Data size for regular frame */
+ uint32_t data_per_frame;
+
+ /* todo - pattern data support is to be implemented in the future */
+ /** Data size for pattern frame */
+ uint32_t data_pattern_frame;
+ /** Frame number of pattern data */
+ uint32_t sync_frame;
+
+ /** bInterval */
+ uint32_t bInterval;
+ /** ISO Packet number per frame */
+ uint32_t pkt_per_frm;
+ /** Next frame num for which will be setup DMA Desc */
+ uint32_t next_frame;
+ /** Number of packets per buffer processing */
+ uint32_t pkt_cnt;
+ /** Info for all isoc packets */
+ iso_pkt_info_t *pkt_info;
+ /** current pkt number */
+ uint32_t cur_pkt;
+ /** current pkt number */
+ uint8_t *cur_pkt_addr;
+ /** current pkt number */
+ uint32_t cur_pkt_dma_addr;
+#endif //DWC_EN_ISOC
+/** @} */
+} dwc_ep_t;
+
+/*
+ * Reasons for halting a host channel.
+ */
+typedef enum dwc_otg_halt_status
+{
+ DWC_OTG_HC_XFER_NO_HALT_STATUS,
+ DWC_OTG_HC_XFER_COMPLETE,
+ DWC_OTG_HC_XFER_URB_COMPLETE,
+ DWC_OTG_HC_XFER_ACK,
+ DWC_OTG_HC_XFER_NAK,
+ DWC_OTG_HC_XFER_NYET,
+ DWC_OTG_HC_XFER_STALL,
+ DWC_OTG_HC_XFER_XACT_ERR,
+ DWC_OTG_HC_XFER_FRAME_OVERRUN,
+ DWC_OTG_HC_XFER_BABBLE_ERR,
+ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
+ DWC_OTG_HC_XFER_AHB_ERR,
+ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+ DWC_OTG_HC_XFER_URB_DEQUEUE
+} dwc_otg_halt_status_e;
+
+/**
+ * Host channel descriptor. This structure represents the state of a single
+ * host channel when acting in host mode. It contains the data items needed to
+ * transfer packets to an endpoint via a host channel.
+ */
+typedef struct dwc_hc
+{
+ /** Host channel number used for register address lookup */
+ uint8_t hc_num;
+
+ /** Device to access */
+ unsigned dev_addr : 7;
+
+ /** EP to access */
+ unsigned ep_num : 4;
+
+ /** EP direction. 0: OUT, 1: IN */
+ unsigned ep_is_in : 1;
+
+ /**
+ * EP speed.
+ * One of the following values:
+ * - DWC_OTG_EP_SPEED_LOW
+ * - DWC_OTG_EP_SPEED_FULL
+ * - DWC_OTG_EP_SPEED_HIGH
+ */
+ unsigned speed : 2;
+#define DWC_OTG_EP_SPEED_LOW 0
+#define DWC_OTG_EP_SPEED_FULL 1
+#define DWC_OTG_EP_SPEED_HIGH 2
+
+ /**
+ * Endpoint type.
+ * One of the following values:
+ * - DWC_OTG_EP_TYPE_CONTROL: 0
+ * - DWC_OTG_EP_TYPE_ISOC: 1
+ * - DWC_OTG_EP_TYPE_BULK: 2
+ * - DWC_OTG_EP_TYPE_INTR: 3
+ */
+ unsigned ep_type : 2;
+
+ /** Max packet size in bytes */
+ unsigned max_packet : 11;
+
+ /**
+ * PID for initial transaction.
+ * 0: DATA0,dwc_otg_core_if
structure contains information needed to manage
+ * the DWC_otg controller acting in either host or device mode. It
+ * represents the programming view of the controller as a whole.
+ */
+typedef struct dwc_otg_core_if
+{
+ /** Parameters that define how the core should be configured.*/
+ dwc_otg_core_params_t *core_params;
+
+ /** Core Global registers starting at offset 000h. */
+ dwc_otg_core_global_regs_t *core_global_regs;
+
+ /** Device-specific information */
+ dwc_otg_dev_if_t *dev_if;
+ /** Host-specific information */
+ dwc_otg_host_if_t *host_if;
+
+ /** Value from SNPSID register */
+ uint32_t snpsid;
+
+ /*
+ * Set to 1 if the core PHY interface bits in USBCFG have been
+ * initialized.
+ */
+ uint8_t phy_init_done;
+
+ /*
+ * SRP Success flag, set by srp success interrupt in FS I2C mode
+ */
+ uint8_t srp_success;
+ uint8_t srp_timer_started;
+
+ /* Common configuration information */
+ /** Power and Clock Gating Control Register */
+ volatile uint32_t *pcgcctl;
+#define DWC_OTG_PCGCCTL_OFFSET 0xE00
+
+ /** Push/pop addresses for endpoints or host channels.*/
+ uint32_t *data_fifo[MAX_EPS_CHANNELS];
+#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
+#define DWC_OTG_DATA_FIFO_SIZE 0x1000
+
+ /** Total RAM for FIFOs (Bytes) */
+ uint16_t total_fifo_size;
+ /** Size of Rx FIFO (Bytes) */
+ uint16_t rx_fifo_size;
+ /** Size of Non-periodic Tx FIFO (Bytes) */
+ uint16_t nperio_tx_fifo_size;
+
+
+ /** 1 if DMA is enabled, 0 otherwise. */
+ uint8_t dma_enable;
+
+ /** 1 if Descriptor DMA mode is enabled, 0 otherwise. */
+ uint8_t dma_desc_enable;
+
+ /** 1 if PTI Enhancement mode is enabled, 0 otherwise. */
+ uint8_t pti_enh_enable;
+
+ /** 1 if MPI Enhancement mode is enabled, 0 otherwise. */
+ uint8_t multiproc_int_enable;
+
+ /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
+ uint8_t en_multiple_tx_fifo;
+
+ /** Set to 1 if multiple packets of a high-bandwidth transfer is in
+ * process of being queued */
+ uint8_t queuing_high_bandwidth;
+
+ /** Hardware Configuration -- stored here for convenience.*/
+ hwcfg1_data_t hwcfg1;
+ hwcfg2_data_t hwcfg2;
+ hwcfg3_data_t hwcfg3;
+ hwcfg4_data_t hwcfg4;
+
+ /** Host and Device Configuration -- stored here for convenience.*/
+ hcfg_data_t hcfg;
+ dcfg_data_t dcfg;
+
+ /** The operational State, during transations
+ * (a_host>>a_peripherial and b_device=>b_host) this may not
+ * match the core but allows the software to determine
+ * transitions.
+ */
+ uint8_t op_state;
+
+ /**
+ * Set to 1 if the HCD needs to be restarted on a session request
+ * interrupt. This is required if no connector ID status change has
+ * occurred since the HCD was last disconnected.
+ */
+ uint8_t restart_hcd_on_session_req;
+
+ /** HCD callbacks */
+ /** A-Device is a_host */
+#define A_HOST (1)
+ /** A-Device is a_suspend */
+#define A_SUSPEND (2)
+ /** A-Device is a_peripherial */
+#define A_PERIPHERAL (3)
+ /** B-Device is operating as a Peripheral. */
+#define B_PERIPHERAL (4)
+ /** B-Device is operating as a Host. */
+#define B_HOST (5)
+
+ /** HCD callbacks */
+ struct dwc_otg_cil_callbacks *hcd_cb;
+ /** PCD callbacks */
+ struct dwc_otg_cil_callbacks *pcd_cb;
+
+ /** Device mode Periodic Tx FIFO Mask */
+ uint32_t p_tx_msk;
+ /** Device mode Periodic Tx FIFO Mask */
+ uint32_t tx_msk;
+
+ /** Workqueue object used for handling several interrupts */
+ struct workqueue_struct *wq_otg;
+
+ /** Work object used for handling "Connector ID Status Change" Interrupt */
+ struct work_struct w_conn_id;
+
+ /** Work object used for handling "Wakeup Detected" Interrupt */
+ struct delayed_work w_wkp;
+
+#ifdef DEBUG
+ uint32_t start_hcchar_val[MAX_EPS_CHANNELS];
+
+ hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS];
+ struct timer_list hc_xfer_timer[MAX_EPS_CHANNELS];
+
+ uint32_t hfnum_7_samples;
+ uint64_t hfnum_7_frrem_accum;
+ uint32_t hfnum_0_samples;
+ uint64_t hfnum_0_frrem_accum;
+ uint32_t hfnum_other_samples;
+ uint64_t hfnum_other_frrem_accum;
+#endif
+
+
+} dwc_otg_core_if_t;
+
+/*We must clear S3C24XX_EINTPEND external interrupt register
+ * because after clearing in this register trigerred IRQ from
+ * H/W core in kernel interrupt can be occured again before OTG
+ * handlers clear all IRQ sources of Core registers because of
+ * timing latencies and Low Level IRQ Type.
+ */
+
+#ifdef CONFIG_MACH_IPMATE
+#define S3C2410X_CLEAR_EINTPEND() \
+do { \
+ if (!dwc_otg_read_core_intr(core_if)) { \
+ __raw_writel(1UL << 11,S3C24XX_EINTPEND); \
+ } \
+} while (0)
+#else
+#define S3C2410X_CLEAR_EINTPEND() do { } while (0)
+#endif
+
+/*
+ * The following functions are functions for works
+ * using during handling some interrupts
+ */
+extern void w_conn_id_status_change(struct work_struct *p);
+extern void w_wakeup_detected(struct work_struct *p);
+
+
+/*
+ * The following functions support initialization of the CIL driver component
+ * and the DWC_otg controller.
+ */
+extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t *_reg_base_addr,
+ dwc_otg_core_params_t *_core_params);
+extern void dwc_otg_cil_remove(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_core_init(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_core_host_init(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_core_dev_init(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_enable_global_interrupts( dwc_otg_core_if_t *_core_if );
+extern void dwc_otg_disable_global_interrupts( dwc_otg_core_if_t *_core_if );
+
+/** @name Device CIL Functions
+ * The following functions support managing the DWC_otg controller in device
+ * mode.
+ */
+/**@{*/
+extern void dwc_otg_wakeup(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_read_setup_packet (dwc_otg_core_if_t *_core_if, uint32_t *_dest);
+extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_ep0_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep, int _dma);
+extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep);
+extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_dump_spram(dwc_otg_core_if_t *_core_if);
+#ifdef DWC_EN_ISOC
+extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep);
+extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep);
+#endif //DWC_EN_ISOC
+/**@}*/
+
+/** @name Host CIL Functions
+ * The following functions support managing the DWC_otg controller in host
+ * mode.
+ */
+/**@{*/
+extern void dwc_otg_hc_init(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+extern void dwc_otg_hc_halt(dwc_otg_core_if_t *_core_if,
+ dwc_hc_t *_hc,
+ dwc_otg_halt_status_e _halt_status);
+extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc);
+extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t *_core_if);
+extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t *_core_if);
+
+/**
+ * This function Reads HPRT0 in preparation to modify. It keeps the
+ * WC bits 0 so that if they are read as 1, they won't clear when you
+ * write it back
+ */
+static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t *_core_if)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0);
+ hprt0.b.prtena = 0;
+ hprt0.b.prtconndet = 0;
+ hprt0.b.prtenchng = 0;
+ hprt0.b.prtovrcurrchng = 0;
+ return hprt0.d32;
+}
+
+extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t *_core_if);
+/**@}*/
+
+/** @name Common CIL Functions
+ * The following functions support managing the DWC_otg controller in either
+ * device or host mode.
+ */
+/**@{*/
+
+extern void dwc_otg_read_packet(dwc_otg_core_if_t *core_if,
+ uint8_t *dest,
+ uint16_t bytes);
+
+extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t *_core_if);
+
+extern void dwc_otg_flush_tx_fifo( dwc_otg_core_if_t *_core_if,
+ const int _num );
+extern void dwc_otg_flush_rx_fifo( dwc_otg_core_if_t *_core_if );
+extern void dwc_otg_core_reset( dwc_otg_core_if_t *_core_if );
+
+extern dwc_otg_dma_desc_t* dwc_otg_ep_alloc_desc_chain(uint32_t * dma_desc_addr, uint32_t count);
+extern void dwc_otg_ep_free_desc_chain(dwc_otg_dma_desc_t* desc_addr, uint32_t dma_desc_addr, uint32_t count);
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t *_core_if)
+{
+ return (dwc_read_reg32(&_core_if->core_global_regs->gintsts) &
+ dwc_read_reg32(&_core_if->core_global_regs->gintmsk));
+}
+
+/**
+ * This function returns the OTG Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_otg_intr (dwc_otg_core_if_t *_core_if)
+{
+ return (dwc_read_reg32 (&_core_if->core_global_regs->gotgint));
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t *core_if)
+{
+ uint32_t v;
+
+ if(core_if->multiproc_int_enable) {
+ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachint) &
+ dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachintmsk);
+ } else {
+ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->daint) &
+ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk);
+ }
+ return (v & 0xffff);
+
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t *core_if)
+{
+ uint32_t v;
+
+ if(core_if->multiproc_int_enable) {
+ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachint) &
+ dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachintmsk);
+ } else {
+ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->daint) &
+ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk);
+ }
+
+ return ((v & 0xffff0000) >> 16);
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t *core_if,
+ dwc_ep_t *ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ uint32_t v, msk, emp;
+
+ if(core_if->multiproc_int_enable) {
+ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepeachintmsk[ep->num]);
+ emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk);
+ msk |= ((emp >> ep->num) & 0x1) << 7;
+ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+ } else {
+ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk);
+ emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk);
+ msk |= ((emp >> ep->num) & 0x1) << 7;
+ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+ }
+
+
+ return v;
+}
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t *_core_if,
+ dwc_ep_t *_ep)
+{
+ dwc_otg_dev_if_t *dev_if = _core_if->dev_if;
+ uint32_t v;
+ doepmsk_data_t msk = { .d32 = 0 };
+
+ if(_core_if->multiproc_int_enable) {
+ msk.d32 = dwc_read_reg32(&dev_if->dev_global_regs->doepeachintmsk[_ep->num]);
+ if(_core_if->pti_enh_enable) {
+ msk.b.pktdrpsts = 1;
+ }
+ v = dwc_read_reg32( &dev_if->out_ep_regs[_ep->num]->doepint) & msk.d32;
+ } else {
+ msk.d32 = dwc_read_reg32(&dev_if->dev_global_regs->doepmsk);
+ if(_core_if->pti_enh_enable) {
+ msk.b.pktdrpsts = 1;
+ }
+ v = dwc_read_reg32( &dev_if->out_ep_regs[_ep->num]->doepint) & msk.d32;
+ }
+ return v;
+}
+
+/**
+ * This function returns the Host All Channel Interrupt register
+ */
+static inline uint32_t dwc_otg_read_host_all_channels_intr (dwc_otg_core_if_t *_core_if)
+{
+ return (dwc_read_reg32 (&_core_if->host_if->host_global_regs->haint));
+}
+
+static inline uint32_t dwc_otg_read_host_channel_intr (dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc)
+{
+ return (dwc_read_reg32 (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint));
+}
+
+
+/**
+ * This function returns the mode of the operation, host or device.
+ *
+ * @return 0 - Device Mode, 1 - Host Mode
+ */
+static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t *_core_if)
+{
+ return (dwc_read_reg32( &_core_if->core_global_regs->gintsts ) & 0x1);
+}
+
+static inline uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t *_core_if)
+{
+ return (dwc_otg_mode(_core_if) != DWC_HOST_MODE);
+}
+static inline uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t *_core_if)
+{
+ return (dwc_otg_mode(_core_if) == DWC_HOST_MODE);
+}
+
+extern int32_t dwc_otg_handle_common_intr( dwc_otg_core_if_t *_core_if );
+
+
+/**@}*/
+
+/**
+ * DWC_otg CIL callback structure. This structure allows the HCD and
+ * PCD to register functions used for starting and stopping the PCD
+ * and HCD for role change on for a DRD.
+ */
+typedef struct dwc_otg_cil_callbacks
+{
+ /** Start function for role change */
+ int (*start) (void *_p);
+ /** Stop Function for role change */
+ int (*stop) (void *_p);
+ /** Disconnect Function for role change */
+ int (*disconnect) (void *_p);
+ /** Resume/Remote wakeup Function */
+ int (*resume_wakeup) (void *_p);
+ /** Suspend function */
+ int (*suspend) (void *_p);
+ /** Session Start (SRP) */
+ int (*session_start) (void *_p);
+ /** Pointer passed to start() and stop() */
+ void *p;
+} dwc_otg_cil_callbacks_t;
+
+extern void dwc_otg_cil_register_pcd_callbacks( dwc_otg_core_if_t *_core_if,
+ dwc_otg_cil_callbacks_t *_cb,
+ void *_p);
+extern void dwc_otg_cil_register_hcd_callbacks( dwc_otg_core_if_t *_core_if,
+ dwc_otg_cil_callbacks_t *_cb,
+ void *_p);
+#ifndef warn
+#define warn printk
+#endif
+
+#endif
+
--- /dev/null
+++ b/drivers/usb/dwc/otg_cil_intr.c
@@ -0,0 +1,852 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $
+ * $Revision: #10 $
+ * $Date: 2008/07/16 $
+ * $Change: 1065567 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * This file contains the Common Interrupt handlers.
+ */
+#include "otg_plat.h"
+#include "otg_regs.h"
+#include "otg_cil.h"
+#include "otg_pcd.h"
+
+#ifdef DEBUG
+inline const char *op_state_str(dwc_otg_core_if_t *core_if)
+{
+ return (core_if->op_state==A_HOST?"a_host":
+ (core_if->op_state==A_SUSPEND?"a_suspend":
+ (core_if->op_state==A_PERIPHERAL?"a_peripheral":
+ (core_if->op_state==B_PERIPHERAL?"b_peripheral":
+ (core_if->op_state==B_HOST?"b_host":
+ "unknown")))));
+}
+#endif
+
+/** This function will log a debug message
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_mode_mismatch_intr (dwc_otg_core_if_t *core_if)
+{
+ gintsts_data_t gintsts;
+ DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
+ dwc_otg_mode(core_if) ? "Host" : "Device");
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.modemismatch = 1;
+ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+
+/** Start the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void hcd_start(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->start) {
+ core_if->hcd_cb->start(core_if->hcd_cb->p);
+ }
+}
+/** Stop the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void hcd_stop(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->stop) {
+ core_if->hcd_cb->stop(core_if->hcd_cb->p);
+ }
+}
+/** Disconnect the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void hcd_disconnect(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->disconnect) {
+ core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
+ }
+}
+/** Inform the HCD the a New Session has begun. Helper function for
+ * using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void hcd_session_start(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->session_start) {
+ core_if->hcd_cb->session_start(core_if->hcd_cb->p);
+ }
+}
+
+/** Start the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void pcd_start(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->start) {
+ core_if->pcd_cb->start(core_if->pcd_cb->p);
+ }
+}
+/** Stop the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void pcd_stop(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->stop) {
+ core_if->pcd_cb->stop(core_if->pcd_cb->p);
+ }
+}
+/** Suspend the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void pcd_suspend(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
+ core_if->pcd_cb->suspend(core_if->pcd_cb->p);
+ }
+}
+/** Resume the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void pcd_resume(dwc_otg_core_if_t *core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ }
+}
+
+/**
+ * This function handles the OTG Interrupts. It reads the OTG
+ * Interrupt Register (GOTGINT) to determine what interrupt has
+ * occurred.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs =
+ core_if->core_global_regs;
+ gotgint_data_t gotgint;
+ gotgctl_data_t gotgctl;
+ gintmsk_data_t gintmsk;
+ gotgint.d32 = dwc_read_reg32(&global_regs->gotgint);
+ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
+ DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
+ op_state_str(core_if));
+ //DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32);
+
+ if (gotgint.b.sesenddet) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Session End Detected++ (%s)\n",
+ op_state_str(core_if));
+ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
+
+ if (core_if->op_state == B_HOST) {
+
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_start(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ core_if->op_state = B_PERIPHERAL;
+ } else {
+ dwc_otg_pcd_t *pcd;
+
+ /* If not B_HOST and Device HNP still set. HNP
+ * Did not succeed!*/
+ if (gotgctl.b.devhnpen) {
+ DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
+ DWC_ERROR("Device Not Connected/Responding!\n");
+ }
+
+ /* If Session End Detected the B-Cable has
+ * been disconnected. */
+ /* Reset PCD and Gadget driver to a
+ * clean state. */
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_stop(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ }
+ gotgctl.d32 = 0;
+ gotgctl.b.devhnpen = 1;
+ dwc_modify_reg32(&global_regs->gotgctl,
+ gotgctl.d32, 0);
+ }
+ if (gotgint.b.sesreqsucstschng) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Session Reqeust Success Status Change++\n");
+ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
+ if (gotgctl.b.sesreqscs) {
+ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
+ (core_if->core_params->i2c_enable)) {
+ core_if->srp_success = 1;
+ }
+ else {
+ dwc_otg_pcd_t *pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_resume(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ /* Clear Session Request */
+ gotgctl.d32 = 0;
+ gotgctl.b.sesreq = 1;
+ dwc_modify_reg32(&global_regs->gotgctl,
+ gotgctl.d32, 0);
+ }
+ }
+ }
+ if (gotgint.b.hstnegsucstschng) {
+ /* Print statements during the HNP interrupt handling
+ * can cause it to fail.*/
+ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
+ if (gotgctl.b.hstnegscs) {
+ if (dwc_otg_is_host_mode(core_if)) {
+ dwc_otg_pcd_t *pcd;
+
+ core_if->op_state = B_HOST;
+ /*
+ * Need to disable SOF interrupt immediately.
+ * When switching from device to host, the PCD
+ * interrupt handler won't handle the
+ * interrupt if host mode is already set. The
+ * HCD interrupt handler won't get called if
+ * the HCD state is HALT. This means that the
+ * interrupt does not get handled and Linux
+ * complains loudly.
+ */
+ gintmsk.d32 = 0;
+ gintmsk.b.sofintr = 1;
+ dwc_modify_reg32(&global_regs->gintmsk,
+ gintmsk.d32, 0);
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_stop(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ /*
+ * Initialize the Core for Host mode.
+ */
+ hcd_start(core_if);
+ core_if->op_state = B_HOST;
+ }
+ } else {
+ gotgctl.d32 = 0;
+ gotgctl.b.hnpreq = 1;
+ gotgctl.b.devhnpen = 1;
+ dwc_modify_reg32(&global_regs->gotgctl,
+ gotgctl.d32, 0);
+ DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
+ DWC_ERROR("Device Not Connected/Responding\n");
+ }
+ }
+ if (gotgint.b.hstnegdet) {
+ /* The disconnect interrupt is set at the same time as
+ * Host Negotiation Detected. During the mode
+ * switch all interrupts are cleared so the disconnect
+ * interrupt handler will not get executed.
+ */
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Host Negotiation Detected++ (%s)\n",
+ (dwc_otg_is_host_mode(core_if)?"Host":"Device"));
+ if (dwc_otg_is_device_mode(core_if)){
+ dwc_otg_pcd_t *pcd;
+
+ DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", core_if->op_state);
+ hcd_disconnect(core_if);
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_start(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ core_if->op_state = A_PERIPHERAL;
+ } else {
+ dwc_otg_pcd_t *pcd;
+
+ /*
+ * Need to disable SOF interrupt immediately. When
+ * switching from device to host, the PCD interrupt
+ * handler won't handle the interrupt if host mode is
+ * already set. The HCD interrupt handler won't get
+ * called if the HCD state is HALT. This means that
+ * the interrupt does not get handled and Linux
+ * complains loudly.
+ */
+ gintmsk.d32 = 0;
+ gintmsk.b.sofintr = 1;
+ dwc_modify_reg32(&global_regs->gintmsk,
+ gintmsk.d32, 0);
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_stop(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ hcd_start(core_if);
+ core_if->op_state = A_HOST;
+ }
+ }
+ if (gotgint.b.adevtoutchng) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "A-Device Timeout Change++\n");
+ }
+ if (gotgint.b.debdone) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Debounce Done++\n");
+ }
+
+ /* Clear GOTGINT */
+ dwc_write_reg32 (&core_if->core_global_regs->gotgint, gotgint.d32);
+
+ return 1;
+}
+
+
+void w_conn_id_status_change(struct work_struct *p)
+{
+ dwc_otg_core_if_t *core_if = container_of(p, dwc_otg_core_if_t, w_conn_id);
+
+ uint32_t count = 0;
+ gotgctl_data_t gotgctl = { .d32 = 0 };
+
+ gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
+ DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
+ DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
+
+ /* B-Device connector (Device Mode) */
+ if (gotgctl.b.conidsts) {
+ dwc_otg_pcd_t *pcd;
+
+ /* Wait for switch to device mode. */
+ while (!dwc_otg_is_device_mode(core_if)){
+ DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n",
+ (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
+ MDELAY(100);
+ if (++count > 10000) *(uint32_t*)NULL=0;
+ }
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_start(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ } else {
+ /* A-Device connector (Host Mode) */
+ while (!dwc_otg_is_host_mode(core_if)) {
+ DWC_PRINT("Waiting for Host Mode, Mode=%s\n",
+ (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
+ MDELAY(100);
+ if (++count > 10000) *(uint32_t*)NULL=0;
+ }
+ core_if->op_state = A_HOST;
+ /*
+ * Initialize the Core for Host mode.
+ */
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ hcd_start(core_if);
+ }
+}
+
+
+/**
+ * This function handles the Connector ID Status Change Interrupt. It
+ * reads the OTG Interrupt Register (GOTCTL) to determine whether this
+ * is a Device to Host Mode transition or a Host Mode to Device
+ * Transition.
+ *
+ * This only occurs when the cable is connected/removed from the PHY
+ * connector.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *core_if)
+{
+
+ /*
+ * Need to disable SOF interrupt immediately. If switching from device
+ * to host, the PCD interrupt handler won't handle the interrupt if
+ * host mode is already set. The HCD interrupt handler won't get
+ * called if the HCD state is HALT. This means that the interrupt does
+ * not get handled and Linux complains loudly.
+ */
+ gintmsk_data_t gintmsk = { .d32 = 0 };
+ gintsts_data_t gintsts = { .d32 = 0 };
+
+ gintmsk.b.sofintr = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+
+ DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n",
+ (dwc_otg_is_host_mode(core_if)?"Host":"Device"));
+
+ /*
+ * Need to schedule a work, as there are possible DELAY function calls
+ */
+ queue_work(core_if->wq_otg, &core_if->w_conn_id);
+
+ /* Set flag and clear interrupt */
+ gintsts.b.conidstschng = 1;
+ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that a device is initiating the Session
+ * Request Protocol to request the host to turn on bus power so a new
+ * session can begin. The handler responds by turning on bus power. If
+ * the DWC_otg controller is in low power mode, the handler brings the
+ * controller out of low power mode before turning on bus power.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t *core_if)
+{
+ hprt0_data_t hprt0;
+ gintsts_data_t gintsts;
+
+#ifndef DWC_HOST_ONLY
+ DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ DWC_PRINT("SRP: Device mode\n");
+ } else {
+ DWC_PRINT("SRP: Host mode\n");
+
+ /* Turn on the port power bit. */
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = 1;
+ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
+
+ /* Start the Connection timer. So a message can be displayed
+ * if connect does not occur within 10 seconds. */
+ hcd_session_start(core_if);
+ }
+#endif
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.sessreqintr = 1;
+ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+
+void w_wakeup_detected(struct work_struct *p)
+{
+ struct delayed_work *dw = container_of(p, struct delayed_work, work);
+ dwc_otg_core_if_t *core_if = container_of(dw, dwc_otg_core_if_t, w_wkp);
+
+ /*
+ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
+ * so that OPT tests pass with all PHYs).
+ */
+ hprt0_data_t hprt0 = {.d32=0};
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32);
+// MDELAY(70);
+ hprt0.b.prtres = 0; /* Resume */
+ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
+ DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(core_if->host_if->hprt0));
+}
+/**
+ * This interrupt indicates that the DWC_otg controller has detected a
+ * resume or remote wakeup sequence. If the DWC_otg controller is in
+ * low power mode, the handler must brings the controller out of low
+ * power mode. The controller automatically begins resume
+ * signaling. The handler schedules a time to stop resume signaling.
+ */
+int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t *core_if)
+{
+ gintsts_data_t gintsts;
+
+ DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n");
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ dctl_data_t dctl = {.d32=0};
+ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
+ dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts));
+#ifdef PARTIAL_POWER_DOWN
+ if (core_if->hwcfg4.b.power_optimiz) {
+ pcgcctl_data_t power = {.d32=0};
+
+ power.d32 = dwc_read_reg32(core_if->pcgcctl);
+ DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32);
+
+ power.b.stoppclk = 0;
+ dwc_write_reg32(core_if->pcgcctl, power.d32);
+
+ power.b.pwrclmp = 0;
+ dwc_write_reg32(core_if->pcgcctl, power.d32);
+
+ power.b.rstpdwnmodule = 0;
+ dwc_write_reg32(core_if->pcgcctl, power.d32);
+ }
+#endif
+ /* Clear the Remote Wakeup Signalling */
+ dctl.b.rmtwkupsig = 1;
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl,
+ dctl.d32, 0);
+
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ }
+
+ } else {
+ pcgcctl_data_t pcgcctl = {.d32=0};
+
+ /* Restart the Phy Clock */
+ pcgcctl.b.stoppclk = 1;
+ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0);
+
+ queue_delayed_work(core_if->wq_otg, &core_if->w_wkp, ((70 * HZ / 1000) + 1));
+ }
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.wkupintr = 1;
+ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that a device has been disconnected from
+ * the root port.
+ */
+int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t *core_if)
+{
+ gintsts_data_t gintsts;
+
+ DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
+ (dwc_otg_is_host_mode(core_if)?"Host":"Device"),
+ op_state_str(core_if));
+
+/** @todo Consolidate this if statement. */
+#ifndef DWC_HOST_ONLY
+ if (core_if->op_state == B_HOST) {
+ dwc_otg_pcd_t *pcd;
+
+ /* If in device mode Disconnect and stop the HCD, then
+ * start the PCD. */
+ hcd_disconnect(core_if);
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_start(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ core_if->op_state = B_PERIPHERAL;
+ } else if (dwc_otg_is_device_mode(core_if)) {
+ gotgctl_data_t gotgctl = { .d32 = 0 };
+ gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
+ if (gotgctl.b.hstsethnpen==1) {
+ /* Do nothing, if HNP in process the OTG
+ * interrupt "Host Negotiation Detected"
+ * interrupt will do the mode switch.
+ */
+ } else if (gotgctl.b.devhnpen == 0) {
+ dwc_otg_pcd_t *pcd;
+
+ /* If in device mode Disconnect and stop the HCD, then
+ * start the PCD. */
+ hcd_disconnect(core_if);
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_start(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+
+ core_if->op_state = B_PERIPHERAL;
+ } else {
+ DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n");
+ }
+ } else {
+ if (core_if->op_state == A_HOST) {
+ /* A-Cable still connected but device disconnected. */
+ hcd_disconnect(core_if);
+ }
+ }
+#endif
+
+ gintsts.d32 = 0;
+ gintsts.b.disconnect = 1;
+ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+/**
+ * This interrupt indicates that SUSPEND state has been detected on
+ * the USB.
+ *
+ * For HNP the USB Suspend interrupt signals the change from
+ * "a_peripheral" to "a_host".
+ *
+ * When power management is enabled the core will be put in low power
+ * mode.
+ */
+int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *core_if)
+{
+ dsts_data_t dsts;
+ gintsts_data_t gintsts;
+
+ DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n");
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ dwc_otg_pcd_t *pcd;
+
+ /* Check the Device status register to determine if the Suspend
+ * state is active. */
+ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);
+ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
+ DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
+ "HWCFG4.power Optimize=%d\n",
+ dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
+
+
+#ifdef PARTIAL_POWER_DOWN
+/** @todo Add a module parameter for power management. */
+ if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
+ pcgcctl_data_t power = {.d32=0};
+ DWC_DEBUGPL(DBG_CIL, "suspend\n");
+
+ power.b.pwrclmp = 1;
+ dwc_write_reg32(core_if->pcgcctl, power.d32);
+
+ power.b.rstpdwnmodule = 1;
+ dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
+
+ power.b.stoppclk = 1;
+ dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
+ } else {
+ DWC_DEBUGPL(DBG_ANY,"disconnect?\n");
+ }
+#endif
+ /* PCD callback for suspend. */
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_suspend(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+ } else {
+ if (core_if->op_state == A_PERIPHERAL) {
+ dwc_otg_pcd_t *pcd;
+
+ DWC_DEBUGPL(DBG_ANY,"a_peripheral->a_host\n");
+ /* Clear the a_peripheral flag, back to a_host. */
+
+ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
+ if(unlikely(!pcd)) {
+ DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
+ BUG();
+ }
+ SPIN_LOCK(&pcd->lock);
+
+ pcd_stop(core_if);
+
+ SPIN_UNLOCK(&pcd->lock);
+
+ hcd_start(core_if);
+ core_if->op_state = A_HOST;
+ }
+ }
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.usbsuspend = 1;
+ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *core_if)
+{
+ gintsts_data_t gintsts;
+ gintmsk_data_t gintmsk;
+ gintmsk_data_t gintmsk_common = {.d32=0};
+ gintmsk_common.b.wkupintr = 1;
+ gintmsk_common.b.sessreqintr = 1;
+ gintmsk_common.b.conidstschng = 1;
+ gintmsk_common.b.otgintr = 1;
+ gintmsk_common.b.modemismatch = 1;
+ gintmsk_common.b.disconnect = 1;
+ gintmsk_common.b.usbsuspend = 1;
+ /** @todo: The port interrupt occurs while in device
+ * mode. Added code to CIL to clear the interrupt for now!
+ */
+ gintmsk_common.b.portintr = 1;
+
+ gintsts.d32 = dwc_read_reg32(&core_if->core_global_regs->gintsts);
+ gintmsk.d32 = dwc_read_reg32(&core_if->core_global_regs->gintmsk);
+#ifdef DEBUG
+ /* if any common interrupts set */
+ if (gintsts.d32 & gintmsk_common.d32) {
+ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n",
+ gintsts.d32, gintmsk.d32);
+ }
+#endif
+
+ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
+
+}
+
+/**
+ * Common interrupt handler.
+ *
+ * The common interrupts are those that occur in both Host and Device mode.
+ * This handler handles the following interrupts:
+ * - Mode Mismatch Interrupt
+ * - Disconnect Interrupt
+ * - OTG Interrupt
+ * - Connector ID Status Change Interrupt
+ * - Session Request Interrupt.
+ * - Resume / Remote Wakeup Detected Interrupt.
+ *
+ */
+int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t *core_if)
+{
+ int retval = 0;
+ gintsts_data_t gintsts;
+
+ gintsts.d32 = dwc_otg_read_common_intr(core_if);
+
+ if (gintsts.b.modemismatch) {
+ retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
+ }
+ if (gintsts.b.otgintr) {
+ retval |= dwc_otg_handle_otg_intr(core_if);
+ }
+ if (gintsts.b.conidstschng) {
+ retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
+ }
+ if (gintsts.b.disconnect) {
+ retval |= dwc_otg_handle_disconnect_intr(core_if);
+ }
+ if (gintsts.b.sessreqintr) {
+ retval |= dwc_otg_handle_session_req_intr(core_if);
+ }
+ if (gintsts.b.wkupintr) {
+ retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
+ }
+ if (gintsts.b.usbsuspend) {
+ retval |= dwc_otg_handle_usb_suspend_intr(core_if);
+ }
+ if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
+ /* The port interrupt occurs while in device mode with HPRT0
+ * Port Enable/Disable.
+ */
+ gintsts.d32 = 0;
+ gintsts.b.portintr = 1;
+ dwc_write_reg32(&core_if->core_global_regs->gintsts,
+ gintsts.d32);
+ retval |= 1;
+
+ }
+
+ S3C2410X_CLEAR_EINTPEND();
+
+ return retval;
+}
--- /dev/null
+++ b/drivers/usb/dwc/otg_driver.c
@@ -0,0 +1,960 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $
+ * $Revision: #63 $
+ * $Date: 2008/09/24 $
+ * $Change: 1101777 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ * The dwc_otg_driver module provides the initialization and cleanup entry
+ * points for the DWC_otg driver. This module will be dynamically installed
+ * after Linux is booted using the insmod command. When the module is
+ * installed, the dwc_otg_driver_init function is called. When the module is
+ * removed (using rmmod), the dwc_otg_driver_cleanup function is called.
+ *
+ * This module also defines a data structure for the dwc_otg_driver, which is
+ * used in conjunction with the standard ARM lm_device structure. These
+ * structures allow the OTG driver to comply with the standard Linux driver
+ * model in which devices and drivers are registered with a bus driver. This
+ * has the benefit that Linux can expose attributes of the driver and device
+ * in its special sysfs file system. Users can then read or write files in
+ * this file system to perform diagnostics on the driver components or the
+ * device.
+ */
+
+#include struct usb_hcd
+ */
+static int32_t dwc_otg_hcd_start_cb(void *p)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(p);
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ hprt0_data_t hprt0;
+
+ if (core_if->op_state == B_HOST) {
+ /*
+ * Reset the port. During a HNP mode switch the reset
+ * needs to occur within 1ms and have a duration of at
+ * least 50ms.
+ */
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtrst = 1;
+ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
+ ((struct usb_hcd *)p)->self.is_b_host = 1;
+ } else {
+ ((struct usb_hcd *)p)->self.is_b_host = 0;
+ }
+
+ /* Need to start the HCD in a non-interrupt context. */
+// INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func);
+ INIT_DELAYED_WORK(&dwc_otg_hcd->start_work, hcd_start_func);
+// schedule_work(&dwc_otg_hcd->start_work);
+ queue_delayed_work(core_if->wq_otg, &dwc_otg_hcd->start_work, 50 * HZ / 1000);
+
+ return 1;
+}
+
+/**
+ * HCD Callback function for stopping the HCD.
+ *
+ * @param p void pointer to the struct usb_hcd
+ */
+static int32_t dwc_otg_hcd_stop_cb(void *p)
+{
+ struct usb_hcd *usb_hcd = (struct usb_hcd *)p;
+ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+ dwc_otg_hcd_stop(usb_hcd);
+ return 1;
+}
+
+static void del_xfer_timers(dwc_otg_hcd_t *hcd)
+{
+#ifdef DEBUG
+ int i;
+ int num_channels = hcd->core_if->core_params->host_channels;
+ for (i = 0; i < num_channels; i++) {
+ del_timer(&hcd->core_if->hc_xfer_timer[i]);
+ }
+#endif
+}
+
+static void del_timers(dwc_otg_hcd_t *hcd)
+{
+ del_xfer_timers(hcd);
+ del_timer(&hcd->conn_timer);
+}
+
+/**
+ * Processes all the URBs in a single list of QHs. Completes them with
+ * -ETIMEDOUT and frees the QTD.
+ */
+static void kill_urbs_in_qh_list(dwc_otg_hcd_t *hcd, struct list_head *qh_list)
+{
+ struct list_head *qh_item;
+ dwc_otg_qh_t *qh;
+ struct list_head *qtd_item;
+ dwc_otg_qtd_t *qtd;
+
+ list_for_each(qh_item, qh_list) {
+ qh = list_entry(qh_item, dwc_otg_qh_t, qh_list_entry);
+ for (qtd_item = qh->qtd_list.next;
+ qtd_item != &qh->qtd_list;
+ qtd_item = qh->qtd_list.next) {
+ qtd = list_entry(qtd_item, dwc_otg_qtd_t, qtd_list_entry);
+ if (qtd->urb != NULL) {
+ dwc_otg_hcd_complete_urb(hcd, qtd->urb,
+ -ETIMEDOUT);
+ }
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd);
+ }
+ }
+}
+
+/**
+ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+ */
+static void kill_all_urbs(dwc_otg_hcd_t *hcd)
+{
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ *
+ * @param p void pointer to the struct usb_hcd
+ */
+static int32_t dwc_otg_hcd_disconnect_cb(void *p)
+{
+ gintsts_data_t intr;
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(p);
+
+ //DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+
+ /*
+ * Set status flags for the hub driver.
+ */
+ dwc_otg_hcd->flags.b.port_connect_status_change = 1;
+ dwc_otg_hcd->flags.b.port_connect_status = 0;
+
+ /*
+ * Shutdown any transfers in process by clearing the Tx FIFO Empty
+ * interrupt mask and status bits and disabling subsequent host
+ * channel interrupts.
+ */
+ intr.d32 = 0;
+ intr.b.nptxfempty = 1;
+ intr.b.ptxfempty = 1;
+ intr.b.hcintr = 1;
+ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, intr.d32, 0);
+ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintsts, intr.d32, 0);
+
+ del_timers(dwc_otg_hcd);
+
+ /*
+ * Turn off the vbus power only if the core has transitioned to device
+ * mode. If still in host mode, need to keep power on to detect a
+ * reconnection.
+ */
+ if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) {
+ if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) {
+ hprt0_data_t hprt0 = { .d32=0 };
+ DWC_PRINT("Disconnect: PortPower off\n");
+ hprt0.b.prtpwr = 0;
+ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32);
+ }
+
+ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if);
+ }
+
+ /* Respond with an error status to all URBs in the schedule. */
+ kill_all_urbs(dwc_otg_hcd);
+
+ if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) {
+ /* Clean up any host channels that were in use. */
+ int num_channels;
+ int i;
+ dwc_hc_t *channel;
+ dwc_otg_hc_regs_t *hc_regs;
+ hcchar_data_t hcchar;
+
+ num_channels = dwc_otg_hcd->core_if->core_params->host_channels;
+
+ if (!dwc_otg_hcd->core_if->dma_enable) {
+ /* Flush out any channel requests in slave mode. */
+ for (i = 0; i < num_channels; i++) {
+ channel = dwc_otg_hcd->hc_ptr_array[i];
+ if (list_empty(&channel->hc_list_entry)) {
+ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i];
+ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ hcchar.b.chen = 0;
+ hcchar.b.chdis = 1;
+ hcchar.b.epdir = 0;
+ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < num_channels; i++) {
+ channel = dwc_otg_hcd->hc_ptr_array[i];
+ if (list_empty(&channel->hc_list_entry)) {
+ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i];
+ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ /* Halt the channel. */
+ hcchar.b.chdis = 1;
+ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
+ }
+
+ dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, channel);
+ list_add_tail(&channel->hc_list_entry,
+ &dwc_otg_hcd->free_hc_list);
+ }
+ }
+ }
+
+ /* A disconnect will end the session so the B-Device is no
+ * longer a B-host. */
+ ((struct usb_hcd *)p)->self.is_b_host = 0;
+ return 1;
+}
+
+/**
+ * Connection timeout function. An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.
+ */
+void dwc_otg_hcd_connect_timeout(unsigned long ptr)
+{
+ DWC_DEBUGPL(DBG_HCDV, "%s(%x)\n", __func__, (int)ptr);
+ DWC_PRINT("Connect Timeout\n");
+ DWC_ERROR("Device Not Connected/Responding\n");
+}
+
+/**
+ * Start the connection timer. An OTG host is required to display a
+ * message if the device does not connect within 10 seconds. The
+ * timer is deleted if a port connect interrupt occurs before the
+ * timer expires.
+ */
+static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t *hcd)
+{
+ init_timer(&hcd->conn_timer);
+ hcd->conn_timer.function = dwc_otg_hcd_connect_timeout;
+ hcd->conn_timer.data = 0;
+ hcd->conn_timer.expires = jiffies + (HZ * 10);
+ add_timer(&hcd->conn_timer);
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ *
+ * @param p void pointer to the struct usb_hcd
+ */
+static int32_t dwc_otg_hcd_session_start_cb(void *p)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(p);
+ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+ dwc_otg_hcd_start_connect_timer(dwc_otg_hcd);
+ return 1;
+}
+
+/**
+ * HCD Callback structure for handling mode switching.
+ */
+static dwc_otg_cil_callbacks_t hcd_cil_callbacks = {
+ .start = dwc_otg_hcd_start_cb,
+ .stop = dwc_otg_hcd_stop_cb,
+ .disconnect = dwc_otg_hcd_disconnect_cb,
+ .session_start = dwc_otg_hcd_session_start_cb,
+ .p = 0,
+};
+
+/**
+ * Reset tasklet function
+ */
+static void reset_tasklet_func(unsigned long data)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *)data;
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ hprt0_data_t hprt0;
+
+ DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n");
+
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtrst = 1;
+ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
+ mdelay(60);
+
+ hprt0.b.prtrst = 0;
+ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
+ dwc_otg_hcd->flags.b.port_reset_change = 1;
+}
+
+static struct tasklet_struct reset_tasklet = {
+ .next = NULL,
+ .state = 0,
+ .count = ATOMIC_INIT(0),
+ .func = reset_tasklet_func,
+ .data = 0,
+};
+
+/**
+ * Initializes the HCD. This function allocates memory for and initializes the
+ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
+ * USB bus with the core and calls the hc_driver->start() function. It returns
+ * a negative error on failure.
+ */
+int dwc_otg_hcd_init(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = NULL;
+ dwc_otg_hcd_t *dwc_otg_hcd = NULL;
+ dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev);
+
+ int num_channels;
+ int i;
+ dwc_hc_t *channel;
+
+ int retval = 0;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n");
+
+ /* Set device flags indicating whether the HCD supports DMA. */
+ if (otg_dev->core_if->dma_enable) {
+ DWC_PRINT("Using DMA mode\n");
+
+ if (otg_dev->core_if->dma_desc_enable) {
+ DWC_PRINT("Device using Descriptor DMA mode\n");
+ } else {
+ DWC_PRINT("Device using Buffer DMA mode\n");
+ }
+ }
+ /*
+ * Allocate memory for the base HCD plus the DWC OTG HCD.
+ * Initialize the base HCD.
+ */
+
+ hcd = usb_create_hcd(&dwc_otg_hc_driver, &pdev->dev, "gadget");
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto error1;
+ }
+
+ hcd->regs = otg_dev->base;
+ hcd->self.otg_port = 1;
+
+ /* Initialize the DWC OTG HCD. */
+ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ dwc_otg_hcd->core_if = otg_dev->core_if;
+ otg_dev->hcd = dwc_otg_hcd;
+
+ /* */
+ spin_lock_init(&dwc_otg_hcd->lock);
+
+ /* Register the HCD CIL Callbacks */
+ dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if,
+ &hcd_cil_callbacks, hcd);
+
+ /* Initialize the non-periodic schedule. */
+ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_inactive);
+ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_active);
+
+ /* Initialize the periodic schedule. */
+ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_inactive);
+ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_ready);
+ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_assigned);
+ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_queued);
+
+ /*
+ * Create a host channel descriptor for each host channel implemented
+ * in the controller. Initialize the channel descriptor array.
+ */
+ INIT_LIST_HEAD(&dwc_otg_hcd->free_hc_list);
+ num_channels = dwc_otg_hcd->core_if->core_params->host_channels;
+ memset(dwc_otg_hcd->hc_ptr_array, 0, sizeof(dwc_otg_hcd->hc_ptr_array));
+ for (i = 0; i < num_channels; i++) {
+ channel = kmalloc(sizeof(dwc_hc_t), GFP_KERNEL);
+ if (channel == NULL) {
+ retval = -ENOMEM;
+ DWC_ERROR("%s: host channel allocation failed\n", __func__);
+ goto error2;
+ }
+ memset(channel, 0, sizeof(dwc_hc_t));
+ channel->hc_num = i;
+ dwc_otg_hcd->hc_ptr_array[i] = channel;
+#ifdef DEBUG
+ init_timer(&dwc_otg_hcd->core_if->hc_xfer_timer[i]);
+#endif
+ DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i, channel);
+ }
+
+ /* Initialize the Connection timeout timer. */
+ init_timer(&dwc_otg_hcd->conn_timer);
+
+ /* Initialize reset tasklet. */
+ reset_tasklet.data = (unsigned long) dwc_otg_hcd;
+ dwc_otg_hcd->reset_tasklet = &reset_tasklet;
+
+ /*
+ * Finish generic HCD initialization and start the HCD. This function
+ * allocates the DMA buffer pool, registers the USB bus, requests the
+ * IRQ line, and calls dwc_otg_hcd_start method.
+ */
+ retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED);
+ if (retval < 0) {
+ goto error2;
+ }
+
+ /*
+ * Allocate space for storing data on status transactions. Normally no
+ * data is sent, but this space acts as a bit bucket. This must be
+ * done after usb_add_hcd since that function allocates the DMA buffer
+ * pool.
+ */
+ if (otg_dev->core_if->dma_enable) {
+ dwc_otg_hcd->status_buf =
+ dma_alloc_coherent(&pdev->dev,
+ DWC_OTG_HCD_STATUS_BUF_SIZE,
+ &dwc_otg_hcd->status_buf_dma,
+ GFP_KERNEL | GFP_DMA);
+ } else {
+ dwc_otg_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE,
+ GFP_KERNEL);
+ }
+ if (!dwc_otg_hcd->status_buf) {
+ retval = -ENOMEM;
+ DWC_ERROR("%s: status_buf allocation failed\n", __func__);
+ goto error3;
+ }
+
+ dwc_otg_hcd->otg_dev = otg_dev;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Initialized HCD, usbbus=%d\n",
+ hcd->self.busnum);
+ return 0;
+
+ /* Error conditions */
+ error3:
+ usb_remove_hcd(hcd);
+ error2:
+ dwc_otg_hcd_free(hcd);
+ usb_put_hcd(hcd);
+ error1:
+ return retval;
+}
+
+/**
+ * Removes the HCD.
+ * Frees memory and resources associated with the HCD and deregisters the bus.
+ */
+void dwc_otg_hcd_remove(struct platform_device *pdev)
+{
+ dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev);
+ dwc_otg_hcd_t *dwc_otg_hcd;
+ struct usb_hcd *hcd;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n");
+
+ if (!otg_dev) {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
+ return;
+ }
+
+ dwc_otg_hcd = otg_dev->hcd;
+
+ if (!dwc_otg_hcd) {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
+ return;
+ }
+
+ hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd);
+
+ if (!hcd) {
+ DWC_DEBUGPL(DBG_ANY, "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n", __func__);
+ return;
+ }
+
+ /* Turn off all interrupts */
+ dwc_write_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0);
+ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gahbcfg, 1, 0);
+
+ usb_remove_hcd(hcd);
+ dwc_otg_hcd_free(hcd);
+ usb_put_hcd(hcd);
+}
+
+/* =========================================================================
+ * Linux HC Driver Functions
+ * ========================================================================= */
+
+/**
+ * Initializes dynamic portions of the DWC_otg HCD state.
+ */
+static void hcd_reinit(dwc_otg_hcd_t *hcd)
+{
+ struct list_head *item;
+ int num_channels;
+ int i;
+ dwc_hc_t *channel;
+
+ hcd->flags.d32 = 0;
+
+ hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
+ hcd->non_periodic_channels = 0;
+ hcd->periodic_channels = 0;
+
+ /*
+ * Put all channels in the free channel list and clean up channel
+ * states.
+ */
+ item = hcd->free_hc_list.next;
+ while (item != &hcd->free_hc_list) {
+ list_del(item);
+ item = hcd->free_hc_list.next;
+ }
+ num_channels = hcd->core_if->core_params->host_channels;
+ for (i = 0; i < num_channels; i++) {
+ channel = hcd->hc_ptr_array[i];
+ list_add_tail(&channel->hc_list_entry, &hcd->free_hc_list);
+ dwc_otg_hc_cleanup(hcd->core_if, channel);
+ }
+
+ /* Initialize the DWC core for host mode operation. */
+ dwc_otg_core_host_init(hcd->core_if);
+}
+
+/** Initializes the DWC_otg controller and its root hub and prepares it for host
+ * mode operation. Activates the root port. Returns 0 on success and a negative
+ * error code on failure. */
+int dwc_otg_hcd_start(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ struct usb_bus *bus;
+
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n");
+
+ bus = hcd_to_bus(hcd);
+
+ /* Initialize the bus state. If the core is in Device Mode
+ * HALT the USB bus and return. */
+ if (dwc_otg_is_device_mode(core_if)) {
+ hcd->state = HC_STATE_RUNNING;
+ return 0;
+ }
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Initialize and connect root hub if one is not already attached */
+ if (bus->root_hub) {
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n");
+ /* Inform the HUB driver to resume. */
+ usb_hcd_resume_root_hub(hcd);
+ }
+ else {
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Does Not Have Root Hub\n");
+ }
+
+ hcd_reinit(dwc_otg_hcd);
+
+ return 0;
+}
+
+static void qh_list_free(dwc_otg_hcd_t *hcd, struct list_head *qh_list)
+{
+ struct list_head *item;
+ dwc_otg_qh_t *qh;
+
+ if (!qh_list->next) {
+ /* The list hasn't been initialized yet. */
+ return;
+ }
+
+ /* Ensure there are no QTDs or URBs left. */
+ kill_urbs_in_qh_list(hcd, qh_list);
+
+ for (item = qh_list->next; item != qh_list; item = qh_list->next) {
+ qh = list_entry(item, dwc_otg_qh_t, qh_list_entry);
+ dwc_otg_hcd_qh_remove_and_free(hcd, qh);
+ }
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+void dwc_otg_hcd_stop(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ hprt0_data_t hprt0 = { .d32=0 };
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n");
+
+ /* Turn off all host-specific interrupts. */
+ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if);
+
+ /*
+ * The root hub should be disconnected before this function is called.
+ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
+ * and the QH lists (via ..._hcd_endpoint_disable).
+ */
+
+ /* Turn off the vbus power */
+ DWC_PRINT("PortPower off\n");
+ hprt0.b.prtpwr = 0;
+ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32);
+}
+
+/** Returns the current frame number. */
+int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ hfnum_data_t hfnum;
+
+ hfnum.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if->
+ host_if->host_global_regs->hfnum);
+
+#ifdef DEBUG_SOF
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", hfnum.b.frnum);
+#endif
+ return hfnum.b.frnum;
+}
+
+/**
+ * Frees secondary storage associated with the dwc_otg_hcd structure contained
+ * in the struct usb_hcd field.
+ */
+void dwc_otg_hcd_free(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ int i;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n");
+
+ del_timers(dwc_otg_hcd);
+
+ /* Free memory for QH/QTD lists */
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued);
+
+ /* Free memory for the host channels. */
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i];
+ if (hc != NULL) {
+ DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", i, hc);
+ kfree(hc);
+ }
+ }
+
+ if (dwc_otg_hcd->core_if->dma_enable) {
+ if (dwc_otg_hcd->status_buf_dma) {
+ dma_free_coherent(hcd->self.controller,
+ DWC_OTG_HCD_STATUS_BUF_SIZE,
+ dwc_otg_hcd->status_buf,
+ dwc_otg_hcd->status_buf_dma);
+ }
+ } else if (dwc_otg_hcd->status_buf != NULL) {
+ kfree(dwc_otg_hcd->status_buf);
+ }
+}
+
+#ifdef DEBUG
+static void dump_urb_info(struct urb *urb, char* fn_name)
+{
+ DWC_PRINT("%s, urb %p\n", fn_name, urb);
+ DWC_PRINT(" Device address: %d\n", usb_pipedevice(urb->pipe));
+ DWC_PRINT(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe),
+ (usb_pipein(urb->pipe) ? "IN" : "OUT"));
+ DWC_PRINT(" Endpoint type: %s\n",
+ ({char *pipetype;
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL: pipetype = "CONTROL"; break;
+ case PIPE_BULK: pipetype = "BULK"; break;
+ case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break;
+ case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break;
+ default: pipetype = "UNKNOWN"; break;
+ }; pipetype;}));
+ DWC_PRINT(" Speed: %s\n",
+ ({char *speed;
+ switch (urb->dev->speed) {
+ case USB_SPEED_HIGH: speed = "HIGH"; break;
+ case USB_SPEED_FULL: speed = "FULL"; break;
+ case USB_SPEED_LOW: speed = "LOW"; break;
+ default: speed = "UNKNOWN"; break;
+ }; speed;}));
+ DWC_PRINT(" Max packet size: %d\n",
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
+ DWC_PRINT(" Data buffer length: %d\n", urb->transfer_buffer_length);
+ DWC_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n",
+ urb->transfer_buffer, (void *)urb->transfer_dma);
+ DWC_PRINT(" Setup buffer: %p, Setup DMA: %p\n",
+ urb->setup_packet, (void *)urb->setup_dma);
+ DWC_PRINT(" Interval: %d\n", urb->interval);
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ int i;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ DWC_PRINT(" ISO Desc %d:\n", i);
+ DWC_PRINT(" offset: %d, length %d\n",
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].length);
+ }
+ }
+}
+
+static void dump_channel_info(dwc_otg_hcd_t *hcd,
+ dwc_otg_qh_t *qh)
+{
+ if (qh->channel != NULL) {
+ dwc_hc_t *hc = qh->channel;
+ struct list_head *item;
+ dwc_otg_qh_t *qh_item;
+ int num_channels = hcd->core_if->core_params->host_channels;
+ int i;
+
+ dwc_otg_hc_regs_t *hc_regs;
+ hcchar_data_t hcchar;
+ hcsplt_data_t hcsplt;
+ hctsiz_data_t hctsiz;
+ uint32_t hcdma;
+
+ hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
+ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
+ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt);
+ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz);
+ hcdma = dwc_read_reg32(&hc_regs->hcdma);
+
+ DWC_PRINT(" Assigned to channel %p:\n", hc);
+ DWC_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32);
+ DWC_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma);
+ DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+ hc->dev_addr, hc->ep_num, hc->ep_is_in);
+ DWC_PRINT(" ep_type: %d\n", hc->ep_type);
+ DWC_PRINT(" max_packet: %d\n", hc->max_packet);
+ DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start);
+ DWC_PRINT(" xfer_started: %d\n", hc->xfer_started);
+ DWC_PRINT(" halt_status: %d\n", hc->halt_status);
+ DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff);
+ DWC_PRINT(" xfer_len: %d\n", hc->xfer_len);
+ DWC_PRINT(" qh: %p\n", hc->qh);
+ DWC_PRINT(" NP inactive sched:\n");
+ list_for_each(item, &hcd->non_periodic_sched_inactive) {
+ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry);
+ DWC_PRINT(" %p\n", qh_item);
+ }
+ DWC_PRINT(" NP active sched:\n");
+ list_for_each(item, &hcd->non_periodic_sched_active) {
+ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry);
+ DWC_PRINT(" %p\n", qh_item);
+ }
+ DWC_PRINT(" Channels: \n");
+ for (i = 0; i < num_channels; i++) {
+ dwc_hc_t *hc = hcd->hc_ptr_array[i];
+ DWC_PRINT(" %2d: %p\n", i, hc);
+ }
+ }
+}
+#endif
+
+
+//OTG host require the DMA addr is DWORD-aligned,
+//patch it if the buffer is not DWORD-aligned
+inline
+void hcd_check_and_patch_dma_addr(struct urb *urb){
+
+ if((!urb->transfer_buffer)||!urb->transfer_dma||urb->transfer_dma==0xffffffff)
+ return;
+
+ if(((u32)urb->transfer_buffer)& 0x3){
+ /*
+ printk("%s: "
+ "urb(%.8x) "
+ "transfer_buffer=%.8x, "
+ "transfer_dma=%.8x, "
+ "transfer_buffer_length=%d, "
+ "actual_length=%d(%x), "
+ "\n",
+ ((urb->transfer_flags & URB_DIR_MASK)==URB_DIR_OUT)?"OUT":"IN",
+ urb,
+ urb->transfer_buffer,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ urb->actual_length,urb->actual_length
+ );
+ */
+ if(!urb->aligned_transfer_buffer||urb->aligned_transfer_buffer_length
. The USB EP operations API is
+ * defined in the structure usb_ep_ops
and the USB
+ * Controller API is defined in the structure
+ * usb_gadget_ops
.
+ *
+ * An important function of the PCD is managing interrupts generated
+ * by the DWC_otg controller. The implementation of the DWC_otg device
+ * mode interrupt service routines is in dwc_otg_pcd_intr.c.
+ *
+ * @todo Add Device Mode test modes (Test J mode, Test K mode, etc).
+ * @todo Does it work when the request size is greater than DEPTSIZ
+ * transfer size
+ *
+ */
+
+
+#include dwc_otg_pcd_t
+ */
+static int32_t dwc_otg_pcd_start_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
+
+ /*
+ * Initialized the Core for Device mode.
+ */
+ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) {
+ dwc_otg_core_dev_init(GET_CORE_IF(pcd));
+ }
+ return 1;
+}
+
+/**
+ * PCD Callback function for stopping the PCD when switching to Host
+ * mode.
+ *
+ * @param p void pointer to the dwc_otg_pcd_t
+ */
+static int32_t dwc_otg_pcd_stop_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
+ extern void dwc_otg_pcd_stop(dwc_otg_pcd_t *_pcd);
+
+ dwc_otg_pcd_stop(pcd);
+ return 1;
+}
+
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param p void pointer to the dwc_otg_pcd_t
+ */
+static int32_t dwc_otg_pcd_suspend_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
+
+ if (pcd->driver && pcd->driver->resume) {
+ SPIN_UNLOCK(&pcd->lock);
+ pcd->driver->suspend(&pcd->gadget);
+ SPIN_LOCK(&pcd->lock);
+ }
+
+ return 1;
+}
+
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param p void pointer to the dwc_otg_pcd_t
+ */
+static int32_t dwc_otg_pcd_resume_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
+
+ if (pcd->driver && pcd->driver->resume) {
+ SPIN_UNLOCK(&pcd->lock);
+ pcd->driver->resume(&pcd->gadget);
+ SPIN_LOCK(&pcd->lock);
+ }
+
+ /* Stop the SRP timeout timer. */
+ if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) ||
+ (!GET_CORE_IF(pcd)->core_params->i2c_enable)) {
+ if (GET_CORE_IF(pcd)->srp_timer_started) {
+ GET_CORE_IF(pcd)->srp_timer_started = 0;
+ del_timer(&pcd->srp_timer);
+ }
+ }
+ return 1;
+}
+
+
+/**
+ * PCD Callback structure for handling mode switching.
+ */
+static dwc_otg_cil_callbacks_t pcd_callbacks =
+{
+ .start = dwc_otg_pcd_start_cb,
+ .stop = dwc_otg_pcd_stop_cb,
+ .suspend = dwc_otg_pcd_suspend_cb,
+ .resume_wakeup = dwc_otg_pcd_resume_cb,
+ .p = 0, /* Set at registration */
+};
+
+/**
+ * This function is called when the SRP timer expires. The SRP should
+ * complete within 6 seconds.
+ */
+static void srp_timeout(unsigned long ptr)
+{
+ gotgctl_data_t gotgctl;
+ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *)ptr;
+ volatile uint32_t *addr = &core_if->core_global_regs->gotgctl;
+
+ gotgctl.d32 = dwc_read_reg32(addr);
+
+ core_if->srp_timer_started = 0;
+
+ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
+ (core_if->core_params->i2c_enable)) {
+ DWC_PRINT("SRP Timeout\n");
+
+ if ((core_if->srp_success) &&
+ (gotgctl.b.bsesvld)) {
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ }
+
+ /* Clear Session Request */
+ gotgctl.d32 = 0;
+ gotgctl.b.sesreq = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gotgctl,
+ gotgctl.d32, 0);
+
+ core_if->srp_success = 0;
+ }
+ else {
+ DWC_ERROR("Device not connected/responding\n");
+ gotgctl.b.sesreq = 0;
+ dwc_write_reg32(addr, gotgctl.d32);
+ }
+ }
+ else if (gotgctl.b.sesreq) {
+ DWC_PRINT("SRP Timeout\n");
+
+ DWC_ERROR("Device not connected/responding\n");
+ gotgctl.b.sesreq = 0;
+ dwc_write_reg32(addr, gotgctl.d32);
+ }
+ else {
+ DWC_PRINT(" SRP GOTGCTL=%0x\n", gotgctl.d32);
+ }
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ *
+ * @param pcd the pcd structure.
+ */
+void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t *pcd)
+{
+ struct timer_list *srp_timer = &pcd->srp_timer;
+ GET_CORE_IF(pcd)->srp_timer_started = 1;
+ init_timer(srp_timer);
+ srp_timer->function = srp_timeout;
+ srp_timer->data = (unsigned long)GET_CORE_IF(pcd);
+ srp_timer->expires = jiffies + (HZ*6);
+ add_timer(srp_timer);
+}
+
+/**
+ * Tasklet
+ *
+ */
+extern void start_next_request(dwc_otg_pcd_ep_t *ep);
+
+static void start_xfer_tasklet_func (unsigned long data)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t*)data;
+ dwc_otg_core_if_t *core_if = pcd->otg_dev->core_if;
+
+ int i;
+ depctl_data_t diepctl;
+
+ DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n");
+
+ diepctl.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl);
+
+ if (pcd->ep0.queue_sof) {
+ pcd->ep0.queue_sof = 0;
+ start_next_request (&pcd->ep0);
+ // break;
+ }
+
+ for (i=0; iCommand | Driver | Description |
GET_STATUS | PCD | Command is processed as + * defined in chapter 9 of the USB 2.0 Specification chapter 9 + * |
CLEAR_FEATURE | PCD | The Device and Endpoint + * requests are the ENDPOINT_HALT feature is procesed, all others the + * interface requests are ignored. |
SET_FEATURE | PCD | The Device and Endpoint + * requests are processed by the PCD. Interface requests are passed + * to the Gadget Driver. |
SET_ADDRESS | PCD | Program the DCFG reg, + * with device address received |
GET_DESCRIPTOR | Gadget Driver | Return the + * requested descriptor |
SET_DESCRIPTOR | Gadget Driver | Optional - + * not implemented by any of the existing Gadget Drivers. |
SET_CONFIGURATION | Gadget Driver | Disable + * all EPs and enable EPs for new configuration. |
GET_CONFIGURATION | Gadget Driver | Return + * the current configuration |
SET_INTERFACE | Gadget Driver | Disable all + * EPs and enable EPs for new configuration. |
GET_INTERFACE | Gadget Driver | Return the + * current interface. |
SYNC_FRAME | PCD | Display debug + * message. |
ep0state
.
+ */
+static void handle_ep0(dwc_otg_pcd_t *pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ desc_sts_data_t desc_sts;
+ deptsiz0_data_t deptsiz;
+ uint32_t byte_count;
+
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
+ print_ep0_state(pcd);
+#endif
+
+ switch (pcd->ep0state) {
+ case EP0_DISCONNECT:
+ break;
+
+ case EP0_IDLE:
+ pcd->request_config = 0;
+
+ pcd_setup(pcd);
+ break;
+
+ case EP0_IN_DATA_PHASE:
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n",
+ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"),
+ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
+#endif
+
+ if (core_if->dma_enable != 0) {
+ /*
+ * For EP0 we can only program 1 packet at a time so we
+ * need to do the make calculations after each complete.
+ * Call write_packet to make the calculations, as in
+ * slave mode, and use those values to determine if we
+ * can complete.
+ */
+ if(core_if->dma_desc_enable == 0) {
+ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->dieptsiz);
+ byte_count = ep0->dwc_ep.xfer_len - deptsiz.b.xfersize;
+ }
+ else {
+ desc_sts.d32 = readl(core_if->dev_if->in_desc_addr);
+ byte_count = ep0->dwc_ep.xfer_len - desc_sts.b.bytes;
+ }
+
+ ep0->dwc_ep.xfer_count += byte_count;
+ ep0->dwc_ep.xfer_buff += byte_count;
+ ep0->dwc_ep.dma_addr += byte_count;
+ }
+ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
+ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep);
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ }
+ else if(ep0->dwc_ep.sent_zlp) {
+ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep);
+ ep0->dwc_ep.sent_zlp = 0;
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ }
+ else {
+ ep0_complete_request(ep0);
+ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
+ }
+ break;
+ case EP0_OUT_DATA_PHASE:
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n",
+ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"),
+ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
+#endif
+ if (core_if->dma_enable != 0) {
+ if(core_if->dma_desc_enable == 0) {
+ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->out_ep_regs[0]->doeptsiz);
+ byte_count = ep0->dwc_ep.maxpacket - deptsiz.b.xfersize;
+
+ //todo: invalidate cache & aligned buf patch on completion
+ dma_sync_single_for_device(NULL,ep0->dwc_ep.dma_addr,byte_count,DMA_FROM_DEVICE);
+ aligned_buf_patch_on_buf_dma_oep_completion(ep0,byte_count);
+ }
+ else {
+ desc_sts.d32 = readl(core_if->dev_if->out_desc_addr);
+ byte_count = ep0->dwc_ep.maxpacket - desc_sts.b.bytes;
+
+ //todo: invalidate cache & aligned buf patch on completion
+ //
+
+ }
+ ep0->dwc_ep.xfer_count += byte_count;
+ ep0->dwc_ep.xfer_buff += byte_count;
+ ep0->dwc_ep.dma_addr += byte_count;
+ }
+ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
+ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep);
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ }
+ else if(ep0->dwc_ep.sent_zlp) {
+ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep);
+ ep0->dwc_ep.sent_zlp = 0;
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ }
+ else {
+ ep0_complete_request(ep0);
+ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
+ }
+ break;
+
+ case EP0_IN_STATUS_PHASE:
+ case EP0_OUT_STATUS_PHASE:
+ DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n");
+ ep0_complete_request(ep0);
+ pcd->ep0state = EP0_IDLE;
+ ep0->stopped = 1;
+ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */
+
+ /* Prepare for more SETUP Packets */
+ if(core_if->dma_enable) {
+ ep0_out_start(core_if, pcd);
+ }
+ break;
+
+ case EP0_STALL:
+ DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n");
+ break;
+ }
+#ifdef DEBUG_EP0
+ print_ep0_state(pcd);
+#endif
+}
+
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(dwc_otg_pcd_t *pcd, const uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if;
+ dwc_otg_dev_if_t *dev_if;
+ deptsiz_data_t dieptsiz = {.d32=0};
+ dwc_otg_pcd_ep_t *ep;
+
+ ep = get_in_ep(pcd, epnum);
+
+#ifdef DWC_EN_ISOC
+ if(ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+ return;
+ }
+#endif /* DWC_EN_ISOC */
+
+ core_if = GET_CORE_IF(pcd);
+ dev_if = core_if->dev_if;
+
+ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz);
+
+ DWC_DEBUGPL(DBG_PCD,"xfer_buff=%p xfer_count=%0x xfer_len=%0x"
+ " stopped=%d\n", ep->dwc_ep.xfer_buff,
+ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len ,
+ ep->stopped);
+ /*
+ * If xfersize is 0 and pktcnt in not 0, resend the last packet.
+ */
+ if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 &&
+ ep->dwc_ep.start_xfer_buff != 0) {
+ if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) {
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
+ }
+ else {
+ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+ /* convert packet size to dwords. */
+ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
+ }
+ ep->stopped = 0;
+ DWC_DEBUGPL(DBG_PCD,"xfer_buff=%p xfer_count=%0x "
+ "xfer_len=%0x stopped=%d\n",
+ ep->dwc_ep.xfer_buff,
+ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len ,
+ ep->stopped
+ );
+ if (epnum == 0) {
+ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+ }
+ else {
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ }
+ }
+}
+
+
+/**
+ * handle the IN EP disable interrupt.
+ */
+static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t *pcd,
+ const uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ deptsiz_data_t dieptsiz = {.d32=0};
+ dctl_data_t dctl = {.d32=0};
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+
+ ep = get_in_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+ if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+ return;
+ }
+
+ DWC_DEBUGPL(DBG_PCD,"diepctl%d=%0x\n", epnum,
+ dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl));
+ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz);
+
+ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
+ dieptsiz.b.pktcnt,
+ dieptsiz.b.xfersize);
+
+ if (ep->stopped) {
+ /* Flush the Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+ /* Clear the Global IN NP NAK */
+ dctl.d32 = 0;
+ dctl.b.cgnpinnak = 1;
+ dwc_modify_reg32(&dev_if->dev_global_regs->dctl,
+ dctl.d32, 0);
+ /* Restart the transaction */
+ if (dieptsiz.b.pktcnt != 0 ||
+ dieptsiz.b.xfersize != 0) {
+ restart_transfer(pcd, epnum);
+ }
+ }
+ else {
+ /* Restart the transaction */
+ if (dieptsiz.b.pktcnt != 0 ||
+ dieptsiz.b.xfersize != 0) {
+ restart_transfer(pcd, epnum);
+ }
+ DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n");
+ }
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t *pcd,
+ const uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+
+#ifdef DEBUG
+ deptsiz_data_t dieptsiz = {.d32=0};
+ uint32_t num = 0;
+#endif
+ dctl_data_t dctl = {.d32=0};
+ dwc_otg_pcd_ep_t *ep;
+
+ gintmsk_data_t intr_mask = {.d32 = 0};
+
+ ep = get_in_ep(pcd, epnum);
+
+ /* Disable the NP Tx Fifo Empty Interrrupt */
+ if (!core_if->dma_enable) {
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
+ }
+ /** @todo NGS Check EP type.
+ * Implement for Periodic EPs */
+ /*
+ * Non-periodic EP
+ */
+ /* Enable the Global IN NAK Effective Interrupt */
+ intr_mask.b.ginnakeff = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+ 0, intr_mask.d32);
+
+ /* Set Global IN NAK */
+ dctl.b.sgnpinnak = 1;
+ dwc_modify_reg32(&dev_if->dev_global_regs->dctl,
+ dctl.d32, dctl.d32);
+
+ ep->stopped = 1;
+
+#ifdef DEBUG
+ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[num]->dieptsiz);
+ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
+ dieptsiz.b.pktcnt,
+ dieptsiz.b.xfersize);
+#endif
+
+#ifdef DISABLE_PERIODIC_EP
+ /*
+ * Set the NAK bit for this EP to
+ * start the disable process.
+ */
+ diepctl.d32 = 0;
+ diepctl.b.snak = 1;
+ dwc_modify_reg32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32, diepctl.d32);
+ ep->disabling = 1;
+ ep->stopped = 1;
+#endif
+}
+
+/**
+ * Handler for the IN EP NAK interrupt.
+ */
+static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t *pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t* core_if;
+ diepmsk_data_t intr_mask = { .d32 = 0};
+
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "IN EP NAK");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.nak = 1;
+
+ if(core_if->multiproc_int_enable) {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->diepeachintmsk[epnum],
+ intr_mask.d32, 0);
+ } else {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->diepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * Handler for the OUT EP Babble interrupt.
+ */
+static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t *pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t* core_if;
+ doepmsk_data_t intr_mask = { .d32 = 0};
+
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT EP Babble");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.babble = 1;
+
+ if(core_if->multiproc_int_enable) {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[epnum],
+ intr_mask.d32, 0);
+ } else {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * Handler for the OUT EP NAK interrupt.
+ */
+static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t *pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t* core_if;
+ doepmsk_data_t intr_mask = { .d32 = 0};
+
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT EP NAK");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.nak = 1;
+
+ if(core_if->multiproc_int_enable) {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[epnum],
+ intr_mask.d32, 0);
+ } else {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * Handler for the OUT EP NYET interrupt.
+ */
+static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t *pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t* core_if;
+ doepmsk_data_t intr_mask = { .d32 = 0};
+
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.nyet = 1;
+
+ if(core_if->multiproc_int_enable) {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[epnum],
+ intr_mask.d32, 0);
+ } else {
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ * -# Read the Device All Endpoint Interrupt register
+ * -# Repeat the following for each IN EP interrupt bit set (from
+ * LSB to MSB).
+ * -# Read the Device Endpoint Interrupt (DIEPINTn) register
+ * -# If "Transfer Complete" call the request complete function
+ * -# If "Endpoint Disabled" complete the EP disable procedure.
+ * -# If "AHB Error Interrupt" log error
+ * -# If "Time-out Handshake" log error
+ * -# If "IN Token Received when TxFIFO Empty" write packet to Tx
+ * FIFO.
+ * -# If "IN Token EP Mismatch" (disable, this is handled by EP
+ * Mismatch Interrupt)
+ */
+static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t *pcd)
+{
+#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \
+do { \
+ diepint_data_t diepint = {.d32=0}; \
+ diepint.b.__intr = 1; \
+ dwc_write_reg32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \
+ diepint.d32); \
+} while (0)
+
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ diepint_data_t diepint = {.d32=0};
+ dctl_data_t dctl = {.d32=0};
+ depctl_data_t depctl = {.d32=0};
+ uint32_t ep_intr;
+ uint32_t epnum = 0;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+ gintmsk_data_t intr_mask = {.d32 = 0};
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd);
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+ /* Service the Device IN interrupts for each endpoint */
+ while(ep_intr) {
+ if (ep_intr&0x1) {
+ uint32_t empty_msk;
+ /* Get EP pointer */
+ ep = get_in_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl);
+ empty_msk = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk);
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "IN EP INTERRUPT - %d\nepmty_msk - %8x diepctl - %8x\n",
+ epnum,
+ empty_msk,
+ depctl.d32);
+
+ DWC_DEBUGPL(DBG_PCD,
+ "EP%d-%s: type=%d, mps=%d\n",
+ dwc_ep->num, (dwc_ep->is_in ?"IN":"OUT"),
+ dwc_ep->type, dwc_ep->maxpacket);
+
+ diepint.d32 = dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep);
+
+ DWC_DEBUGPL(DBG_PCDV, "EP %d Interrupt Register - 0x%x\n", epnum, diepint.d32);
+ /* Transfer complete */
+ if (diepint.b.xfercompl) {
+ /* Disable the NP Tx FIFO Empty
+ * Interrrupt */
+ if(core_if->en_multiple_tx_fifo == 0) {
+ intr_mask.b.nptxfempty = 1;
+ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
+ }
+ else {
+ /* Disable the Tx FIFO Empty Interrupt for this EP */
+ uint32_t fifoemptymsk = 0x1 << dwc_ep->num;
+ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ fifoemptymsk, 0);
+ }
+ /* Clear the bit in DIEPINTn for this interrupt */
+ CLEAR_IN_EP_INTR(core_if,epnum,xfercompl);
+
+ /* Complete the transfer */
+ if (epnum == 0) {
+ handle_ep0(pcd);
+ }
+#ifdef DWC_EN_ISOC
+ else if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if(!ep->stopped)
+ complete_iso_ep(ep);
+ }
+#endif //DWC_EN_ISOC
+ else {
+
+ complete_ep(ep);
+ }
+ }
+ /* Endpoint disable */
+ if (diepint.b.epdisabled) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN disabled\n", epnum);
+ handle_in_ep_disable_intr(pcd, epnum);
+
+ /* Clear the bit in DIEPINTn for this interrupt */
+ CLEAR_IN_EP_INTR(core_if,epnum,epdisabled);
+ }
+ /* AHB Error */
+ if (diepint.b.ahberr) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN AHB Error\n", epnum);
+ /* Clear the bit in DIEPINTn for this interrupt */
+ CLEAR_IN_EP_INTR(core_if,epnum,ahberr);
+ }
+ /* TimeOUT Handshake (non-ISOC IN EPs) */
+ if (diepint.b.timeout) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN Time-out\n", epnum);
+ handle_in_ep_timeout_intr(pcd, epnum);
+
+ CLEAR_IN_EP_INTR(core_if,epnum,timeout);
+ }
+ /** IN Token received with TxF Empty */
+ if (diepint.b.intktxfemp) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN TKN TxFifo Empty\n",
+ epnum);
+ if (!ep->stopped && epnum != 0) {
+
+ diepmsk_data_t diepmsk = { .d32 = 0};
+ diepmsk.b.intktxfemp = 1;
+
+ if(core_if->multiproc_int_enable) {
+ dwc_modify_reg32(&dev_if->dev_global_regs->diepeachintmsk[epnum],
+ diepmsk.d32, 0);
+ } else {
+ dwc_modify_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32, 0);
+ }
+ start_next_request(ep);
+ }
+ else if(core_if->dma_desc_enable && epnum == 0 &&
+ pcd->ep0state == EP0_OUT_STATUS_PHASE) {
+ // EP0 IN set STALL
+ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl);
+
+ /* set the disable and stall bits */
+ if (depctl.b.epena) {
+ depctl.b.epdis = 1;
+ }
+ depctl.b.stall = 1;
+ dwc_write_reg32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32);
+ }
+ CLEAR_IN_EP_INTR(core_if,epnum,intktxfemp);
+ }
+ /** IN Token Received with EP mismatch */
+ if (diepint.b.intknepmis) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN TKN EP Mismatch\n", epnum);
+ CLEAR_IN_EP_INTR(core_if,epnum,intknepmis);
+ }
+ /** IN Endpoint NAK Effective */
+ if (diepint.b.inepnakeff) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN EP NAK Effective\n", epnum);
+ /* Periodic EP */
+ if (ep->disabling) {
+ depctl.d32 = 0;
+ depctl.b.snak = 1;
+ depctl.b.epdis = 1;
+ dwc_modify_reg32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32, depctl.d32);
+ }
+ CLEAR_IN_EP_INTR(core_if,epnum,inepnakeff);
+
+ }
+
+ /** IN EP Tx FIFO Empty Intr */
+ if (diepint.b.emptyintr) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d Tx FIFO Empty Intr \n", epnum);
+ write_empty_tx_fifo(pcd, epnum);
+
+ CLEAR_IN_EP_INTR(core_if,epnum,emptyintr);
+ }
+
+ /** IN EP BNA Intr */
+ if (diepint.b.bna) {
+ CLEAR_IN_EP_INTR(core_if,epnum,bna);
+ if(core_if->dma_desc_enable) {
+#ifdef DWC_EN_ISOC
+ if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * This checking is performed to prevent first "false" BNA
+ * handling occuring right after reconnect
+ */
+ if(dwc_ep->next_frame != 0xffffffff)
+ dwc_otg_pcd_handle_iso_bna(ep);
+ }
+ else
+#endif //DWC_EN_ISOC
+ {
+ dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl);
+
+ /* If Global Continue on BNA is disabled - disable EP */
+ if(!dctl.b.gcontbna) {
+ depctl.d32 = 0;
+ depctl.b.snak = 1;
+ depctl.b.epdis = 1;
+ dwc_modify_reg32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32, depctl.d32);
+ } else {
+ start_next_request(ep);
+ }
+ }
+ }
+ }
+ /* NAK Interrutp */
+ if (diepint.b.nak) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d IN NAK Interrupt\n", epnum);
+ handle_in_ep_nak_intr(pcd, epnum);
+
+ CLEAR_IN_EP_INTR(core_if,epnum,nak);
+ }
+ }
+ epnum++;
+ ep_intr >>=1;
+ }
+
+ return 1;
+#undef CLEAR_IN_EP_INTR
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ * -# Read the Device All Endpoint Interrupt register
+ * -# Repeat the following for each OUT EP interrupt bit set (from
+ * LSB to MSB).
+ * -# Read the Device Endpoint Interrupt (DOEPINTn) register
+ * -# If "Transfer Complete" call the request complete function
+ * -# If "Endpoint Disabled" complete the EP disable procedure.
+ * -# If "AHB Error Interrupt" log error
+ * -# If "Setup Phase Done" process Setup Packet (See Standard USB
+ * Command Processing)
+ */
+static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t *pcd)
+{
+#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \
+do { \
+ doepint_data_t doepint = {.d32=0}; \
+ doepint.b.__intr = 1; \
+ dwc_write_reg32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \
+ doepint.d32); \
+} while (0)
+
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ uint32_t ep_intr;
+ doepint_data_t doepint = {.d32=0};
+ dctl_data_t dctl = {.d32=0};
+ depctl_data_t doepctl = {.d32=0};
+ uint32_t epnum = 0;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+
+ while(ep_intr) {
+ if (ep_intr&0x1) {
+ /* Get EP pointer */
+ ep = get_out_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV,
+ "EP%d-%s: type=%d, mps=%d\n",
+ dwc_ep->num, (dwc_ep->is_in ?"IN":"OUT"),
+ dwc_ep->type, dwc_ep->maxpacket);
+#endif
+ doepint.d32 = dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep);
+
+ /* Transfer complete */
+ if (doepint.b.xfercompl) {
+ if (epnum == 0) {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl);
+ if(core_if->dma_desc_enable == 0 || pcd->ep0state != EP0_IDLE)
+ handle_ep0(pcd);
+#ifdef DWC_EN_ISOC
+ } else if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (doepint.b.pktdrpsts == 0) {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl);
+ complete_iso_ep(ep);
+ } else {
+ doepint_data_t doepint = {.d32=0};
+ doepint.b.xfercompl = 1;
+ doepint.b.pktdrpsts = 1;
+ dwc_write_reg32(&core_if->dev_if->out_ep_regs[epnum]->doepint,
+ doepint.d32);
+ if(handle_iso_out_pkt_dropped(core_if,dwc_ep)) {
+ complete_iso_ep(ep);
+ }
+ }
+#endif //DWC_EN_ISOC
+ } else {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl);
+ complete_ep(ep);
+ }
+
+ }
+
+ /* Endpoint disable */
+ if (doepint.b.epdisabled) {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if,epnum,epdisabled);
+ }
+ /* AHB Error */
+ if (doepint.b.ahberr) {
+ DWC_DEBUGPL(DBG_PCD,"EP%d OUT AHB Error\n", epnum);
+ DWC_DEBUGPL(DBG_PCD,"EP DMA REG %d \n", core_if->dev_if->out_ep_regs[epnum]->doepdma);
+ CLEAR_OUT_EP_INTR(core_if,epnum,ahberr);
+ }
+ /* Setup Phase Done (contorl EPs) */
+ if (doepint.b.setup) {
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD,"EP%d SETUP Done\n",
+ epnum);
+#endif
+ CLEAR_OUT_EP_INTR(core_if,epnum,setup);
+ handle_ep0(pcd);
+ }
+
+ /** OUT EP BNA Intr */
+ if (doepint.b.bna) {
+ CLEAR_OUT_EP_INTR(core_if,epnum,bna);
+ if(core_if->dma_desc_enable) {
+#ifdef DWC_EN_ISOC
+ if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * This checking is performed to prevent first "false" BNA
+ * handling occuring right after reconnect
+ */
+ if(dwc_ep->next_frame != 0xffffffff)
+ dwc_otg_pcd_handle_iso_bna(ep);
+ }
+ else
+#endif //DWC_EN_ISOC
+ {
+ dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl);
+
+ /* If Global Continue on BNA is disabled - disable EP*/
+ if(!dctl.b.gcontbna) {
+ doepctl.d32 = 0;
+ doepctl.b.snak = 1;
+ doepctl.b.epdis = 1;
+ dwc_modify_reg32(&dev_if->out_ep_regs[epnum]->doepctl, doepctl.d32, doepctl.d32);
+ } else {
+ start_next_request(ep);
+ }
+ }
+ }
+ }
+ if (doepint.b.stsphsercvd) {
+ CLEAR_OUT_EP_INTR(core_if,epnum,stsphsercvd);
+ if(core_if->dma_desc_enable) {
+ do_setup_in_status_phase(pcd);
+ }
+ }
+ /* Babble Interrutp */
+ if (doepint.b.babble) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d OUT Babble\n", epnum);
+ handle_out_ep_babble_intr(pcd, epnum);
+
+ CLEAR_OUT_EP_INTR(core_if,epnum,babble);
+ }
+ /* NAK Interrutp */
+ if (doepint.b.nak) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d OUT NAK\n", epnum);
+ handle_out_ep_nak_intr(pcd, epnum);
+
+ CLEAR_OUT_EP_INTR(core_if,epnum,nak);
+ }
+ /* NYET Interrutp */
+ if (doepint.b.nyet) {
+ DWC_DEBUGPL(DBG_ANY,"EP%d OUT NYET\n", epnum);
+ handle_out_ep_nyet_intr(pcd, epnum);
+
+ CLEAR_OUT_EP_INTR(core_if,epnum,nyet);
+ }
+ }
+
+ epnum++;
+ ep_intr >>=1;
+ }
+
+ return 1;
+
+#undef CLEAR_OUT_EP_INTR
+}
+
+
+/**
+ * Incomplete ISO IN Transfer Interrupt.
+ * This interrupt indicates one of the following conditions occurred
+ * while transmitting an ISOC transaction.
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ * The follow actions will be taken:
+ * -# Determine the EP
+ * -# Set incomplete flag in dwc_ep structure
+ * -# Disable EP; when "Endpoint Disabled" interrupt is received
+ * Flush FIFO
+ */
+int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t *pcd)
+{
+ gintsts_data_t gintsts;
+
+
+#ifdef DWC_EN_ISOC
+ dwc_otg_dev_if_t *dev_if;
+ deptsiz_data_t deptsiz = { .d32 = 0};
+ depctl_data_t depctl = { .d32 = 0};
+ dsts_data_t dsts = { .d32 = 0};
+ dwc_ep_t *dwc_ep;
+ int i;
+
+ dev_if = GET_CORE_IF(pcd)->dev_if;
+
+ for(i = 1; i <= dev_if->num_in_eps; ++i) {
+ dwc_ep = &pcd->in_ep[i].dwc_ep;
+ if(dwc_ep->active &&
+ dwc_ep->type == USB_ENDPOINT_XFER_ISOC)
+ {
+ deptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->dieptsiz);
+ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl);
+
+ if(depctl.b.epdis && deptsiz.d32) {
+ set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep);
+ if(dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+ dwc_ep->cur_pkt = 0;
+ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
+
+ if(dwc_ep->proc_buf_num) {
+ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1;
+ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1;
+ } else {
+ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0;
+ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0;
+ }
+ }
+
+ dsts.d32 = dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts);
+ dwc_ep->next_frame = dsts.b.soffn;
+
+ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF(pcd), dwc_ep);
+ }
+ }
+ }
+
+#else
+ gintmsk_data_t intr_mask = { .d32 = 0};
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n",
+ "IN ISOC Incomplete");
+
+ intr_mask.b.incomplisoin = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+#endif //DWC_EN_ISOC
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.incomplisoin = 1;
+ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt.
+ *
+ * This interrupt indicates that the core has dropped an ISO OUT
+ * packet. The following conditions can be the cause:
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ * The follow actions will be taken:
+ * -# Determine the EP
+ * -# Set incomplete flag in dwc_ep structure
+ * -# Read any data from the FIFO
+ * -# Disable EP. when "Endpoint Disabled" interrupt is received
+ * re-enable EP.
+ */
+int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t *pcd)
+{
+ /* @todo implement ISR */
+ gintsts_data_t gintsts;
+
+#ifdef DWC_EN_ISOC
+ dwc_otg_dev_if_t *dev_if;
+ deptsiz_data_t deptsiz = { .d32 = 0};
+ depctl_data_t depctl = { .d32 = 0};
+ dsts_data_t dsts = { .d32 = 0};
+ dwc_ep_t *dwc_ep;
+ int i;
+
+ dev_if = GET_CORE_IF(pcd)->dev_if;
+
+ for(i = 1; i <= dev_if->num_out_eps; ++i) {
+ dwc_ep = &pcd->in_ep[i].dwc_ep;
+ if(pcd->out_ep[i].dwc_ep.active &&
+ pcd->out_ep[i].dwc_ep.type == USB_ENDPOINT_XFER_ISOC)
+ {
+ deptsiz.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doeptsiz);
+ depctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doepctl);
+
+ if(depctl.b.epdis && deptsiz.d32) {
+ set_current_pkt_info(GET_CORE_IF(pcd), &pcd->out_ep[i].dwc_ep);
+ if(dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+ dwc_ep->cur_pkt = 0;
+ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
+
+ if(dwc_ep->proc_buf_num) {
+ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1;
+ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1;
+ } else {
+ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0;
+ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0;
+ }
+ }
+
+ dsts.d32 = dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts);
+ dwc_ep->next_frame = dsts.b.soffn;
+
+ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF(pcd), dwc_ep);
+ }
+ }
+ }
+#else
+ /** @todo implement ISR */
+ gintmsk_data_t intr_mask = { .d32 = 0};
+
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n",
+ "OUT ISOC Incomplete");
+
+ intr_mask.b.incomplisoout = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+#endif // DWC_EN_ISOC
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.incomplisoout = 1;
+ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ *
+ */
+int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t *pcd)
+{
+ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+ depctl_data_t diepctl = { .d32 = 0};
+ depctl_data_t diepctl_rd = { .d32 = 0};
+ gintmsk_data_t intr_mask = { .d32 = 0};
+ gintsts_data_t gintsts;
+ int i;
+
+ DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n");
+
+ /* Disable all active IN EPs */
+ diepctl.b.epdis = 1;
+ diepctl.b.snak = 1;
+
+ for (i=0; i <= dev_if->num_in_eps; i++)
+ {
+ diepctl_rd.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl);
+ if (diepctl_rd.b.epena) {
+ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepctl,
+ diepctl.d32);
+ }
+ }
+ /* Disable the Global IN NAK Effective Interrupt */
+ intr_mask.b.ginnakeff = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.ginnakeff = 1;
+ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * OUT NAK Effective.
+ *
+ */
+int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t *pcd)
+{
+ gintmsk_data_t intr_mask = { .d32 = 0};
+ gintsts_data_t gintsts;
+
+ DWC_PRINT("INTERRUPT Handler not implemented for %s\n",
+ "Global IN NAK Effective\n");
+ /* Disable the Global IN NAK Effective Interrupt */
+ intr_mask.b.goutnakeff = 1;
+ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.goutnakeff = 1;
+ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts. Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t *pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+#ifdef VERBOSE
+ dwc_otg_core_global_regs_t *global_regs =
+ core_if->core_global_regs;
+#endif
+ gintsts_data_t gintr_status;
+ int32_t retval = 0;
+
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n",
+ __func__,
+ dwc_read_reg32(&global_regs->gintsts),
+ dwc_read_reg32(&global_regs->gintmsk));
+#endif
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ SPIN_LOCK(&pcd->lock);
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x gintmsk=%08x\n",
+ __func__,
+ dwc_read_reg32(&global_regs->gintsts),
+ dwc_read_reg32(&global_regs->gintmsk));
+#endif
+
+ gintr_status.d32 = dwc_otg_read_core_intr(core_if);
+/*
+ if (!gintr_status.d32) {
+ SPIN_UNLOCK(&pcd->lock);
+ return 0;
+ }
+*/
+ DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n",
+ __func__, gintr_status.d32);
+
+ if (gintr_status.b.sofintr) {
+ retval |= dwc_otg_pcd_handle_sof_intr(pcd);
+ }
+ if (gintr_status.b.rxstsqlvl) {
+ retval |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+ }
+ if (gintr_status.b.nptxfempty) {
+ retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+ }
+ if (gintr_status.b.ginnakeff) {
+ retval |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+ }
+ if (gintr_status.b.goutnakeff) {
+ retval |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+ }
+ if (gintr_status.b.i2cintr) {
+ retval |= dwc_otg_pcd_handle_i2c_intr(pcd);
+ }
+ if (gintr_status.b.erlysuspend) {
+ retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+ }
+ if (gintr_status.b.usbreset) {
+ retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+ }
+ if (gintr_status.b.enumdone) {
+ retval |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+ }
+ if (gintr_status.b.isooutdrop) {
+ retval |= dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(pcd);
+ }
+ if (gintr_status.b.eopframe) {
+ retval |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+ }
+ if (gintr_status.b.epmismatch) {
+ retval |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if);
+ }
+ if (gintr_status.b.inepint) {
+ if(!core_if->multiproc_int_enable) {
+ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+ }
+ }
+ if (gintr_status.b.outepintr) {
+ if(!core_if->multiproc_int_enable) {
+ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+ }
+ }
+ if (gintr_status.b.incomplisoin) {
+ retval |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+ }
+ if (gintr_status.b.incomplisoout) {
+ retval |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+ }
+
+ /* In MPI mode De vice Endpoints intterrupts are asserted
+ * without setting outepintr and inepint bits set, so these
+ * Interrupt handlers are called without checking these bit-fields
+ */
+ if(core_if->multiproc_int_enable) {
+ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+ }
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__,
+ dwc_read_reg32(&global_regs->gintsts));
+#endif
+ SPIN_UNLOCK(&pcd->lock);
+ }
+ S3C2410X_CLEAR_EINTPEND();
+
+ return retval;
+}
+
+#endif /* DWC_HOST_ONLY */
--- /dev/null
+++ b/drivers/usb/dwc/otg_plat.h
@@ -0,0 +1,266 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/platform/dwc_otg_plat.h $
+ * $Revision: #23 $
+ * $Date: 2008/07/15 $
+ * $Change: 1064915 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_OTG_PLAT_H__)
+#define __DWC_OTG_PLAT_H__
+
+#include uint32_t dev_ctl = dwc_read_reg32(&dev_regs->dctl);
+ */
+static __inline__ uint32_t dwc_read_reg32( volatile uint32_t *reg)
+{
+ return __raw_readl(reg);
+ // return readl(reg);
+};
+
+/**
+ * Writes a register with a 32 bit value.
+ *
+ * @param reg address of register to read.
+ * @param value to write to _reg.
+ *
+ * Usage:dwc_write_reg32(&dev_regs->dctl, 0);
+ */
+static __inline__ void dwc_write_reg32( volatile uint32_t *reg, const uint32_t value)
+{
+ // writel( value, reg );
+ __raw_writel(value, reg);
+
+};
+
+/**
+ * This function modifies bit values in a register. Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ *
+ * @param reg address of register to read.
+ * @param clear_mask bit mask to be cleared.
+ * @param set_mask bit mask to be set.
+ *
+ * Usage: // Clear the SOF Interrupt Mask bit and
+ * // set the OTG Interrupt mask bit, leaving all others as they were.
+ * dwc_modify_reg32(&dev_regs->gintmsk, DWC_SOF_INT, DWC_OTG_INT);
+ */
+static __inline__
+ void dwc_modify_reg32( volatile uint32_t *reg, const uint32_t clear_mask, const uint32_t set_mask)
+{
+ // writel( (readl(reg) & ~clear_mask) | set_mask, reg );
+ __raw_writel( (__raw_readl(reg) & ~clear_mask) | set_mask, reg );
+};
+
+
+/**
+ * Wrapper for the OS micro-second delay function.
+ * @param[in] usecs Microseconds of delay
+ */
+static __inline__ void UDELAY( const uint32_t usecs )
+{
+ udelay( usecs );
+}
+
+/**
+ * Wrapper for the OS milli-second delay function.
+ * @param[in] msecs milliseconds of delay
+ */
+static __inline__ void MDELAY( const uint32_t msecs )
+{
+ mdelay( msecs );
+}
+
+/**
+ * Wrapper for the Linux spin_lock. On the ARM (Integrator)
+ * spin_lock() is a nop.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+static __inline__ void SPIN_LOCK( spinlock_t *lock )
+{
+ spin_lock(lock);
+}
+
+/**
+ * Wrapper for the Linux spin_unlock. On the ARM (Integrator)
+ * spin_lock() is a nop.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+static __inline__ void SPIN_UNLOCK( spinlock_t *lock )
+{
+ spin_unlock(lock);
+}
+
+/**
+ * Wrapper (macro) for the Linux spin_lock_irqsave. On the ARM
+ * (Integrator) spin_lock() is a nop.
+ *
+ * @param l Pointer to the spinlock.
+ * @param f unsigned long for irq flags storage.
+ */
+#define SPIN_LOCK_IRQSAVE( l, f ) spin_lock_irqsave(l,f);
+
+/**
+ * Wrapper (macro) for the Linux spin_unlock_irqrestore. On the ARM
+ * (Integrator) spin_lock() is a nop.
+ *
+ * @param l Pointer to the spinlock.
+ * @param f unsigned long for irq flags storage.
+ */
+#define SPIN_UNLOCK_IRQRESTORE( l,f ) spin_unlock_irqrestore(l,f);
+
+/*
+ * Debugging support vanishes in non-debug builds.
+ */
+
+
+/**
+ * The Debug Level bit-mask variable.
+ */
+extern uint32_t g_dbg_lvl;
+/**
+ * Set the Debug Level variable.
+ */
+static inline uint32_t SET_DEBUG_LEVEL( const uint32_t new )
+{
+ uint32_t old = g_dbg_lvl;
+ g_dbg_lvl = new;
+ return old;
+}
+
+/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
+#define DBG_CIL (0x2)
+/** When debug level has the DBG_CILV bit set, display CIL Verbose debug
+ * messages */
+#define DBG_CILV (0x20)
+/** When debug level has the DBG_PCD bit set, display PCD (Device) debug
+ * messages */
+#define DBG_PCD (0x4)
+/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug
+ * messages */
+#define DBG_PCDV (0x40)
+/** When debug level has the DBG_HCD bit set, display Host debug messages */
+#define DBG_HCD (0x8)
+/** When debug level has the DBG_HCDV bit set, display Verbose Host debug
+ * messages */
+#define DBG_HCDV (0x80)
+/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
+ * mode. */
+#define DBG_HCD_URB (0x800)
+
+/** When debug level has any bit set, display debug messages */
+#define DBG_ANY (0xFF)
+
+/** All debug messages off */
+#define DBG_OFF 0
+
+/** Prefix string for DWC_DEBUG print macros. */
+#define USB_DWC "DWC_otg: "
+
+/**
+ * Print a debug message when the Global debug level variable contains
+ * the bit defined in lvl
.
+ *
+ * @param[in] lvl - Debug level, use one of the DBG_ constants above.
+ * @param[in] x - like printf
+ *
+ * Example:
+ *
+ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr);
+ *
+ *
+ * results in:
+ *
+ * usb-DWC_otg: dwc_otg_cil_init(ca867000)
+ *
+ */
+#ifdef DEBUG
+
+# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)printk( KERN_DEBUG USB_DWC x ); }while(0)
+# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x )
+
+# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl)
+
+#else
+
+# define DWC_DEBUGPL(lvl, x...) do{}while(0)
+# define DWC_DEBUGP(x...)
+
+# define CHK_DEBUG_LEVEL(level) (0)
+
+#endif /*DEBUG*/
+
+/**
+ * Print an Error message.
+ */
+#define DWC_ERROR(x...) printk( KERN_ERR USB_DWC x )
+/**
+ * Print a Warning message.
+ */
+#define DWC_WARN(x...) printk( KERN_WARNING USB_DWC x )
+/**
+ * Print a notice (normal but significant message).
+ */
+#define DWC_NOTICE(x...) printk( KERN_NOTICE USB_DWC x )
+/**
+ * Basic message printing.
+ */
+#define DWC_PRINT(x...) printk( KERN_INFO USB_DWC x )
+
+#endif
+
--- /dev/null
+++ b/drivers/usb/dwc/otg_regs.h
@@ -0,0 +1,2059 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $
+ * $Revision: #72 $
+ * $Date: 2008/09/19 $
+ * $Change: 1099526 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_REGS_H__
+#define __DWC_OTG_REGS_H__
+
+/**
+ * @file
+ *
+ * This file contains the data structures for accessing the DWC_otg core registers.
+ *
+ * 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.
+ */
+
+/** Maximum number of Periodic FIFOs */
+#define MAX_PERIO_FIFOS 15
+/** Maximum number of Transmit FIFOs */
+#define MAX_TX_FIFOS 15
+
+/** Maximum number of Endpoints/HostChannels */
+#define MAX_EPS_CHANNELS 16
+
+/****************************************************************************/
+/** DWC_otg Core registers .
+ * The dwc_otg_core_global_regs structure defines the size
+ * and relative field offsets for the Core Global registers.
+ */
+typedef struct dwc_otg_core_global_regs
+{
+ /** OTG Control and Status Register. Offset: 000h */
+ volatile uint32_t gotgctl;
+ /** OTG Interrupt Register. Offset: 004h */
+ volatile uint32_t gotgint;
+ /**Core AHB Configuration Register. Offset: 008h */
+ volatile uint32_t 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 */
+ volatile uint32_t gusbcfg;
+ /**Core Reset Register. Offset: 010h */
+ volatile uint32_t grstctl;
+ /**Core Interrupt Register. Offset: 014h */
+ volatile uint32_t gintsts;
+ /**Core Interrupt Mask Register. Offset: 018h */
+ volatile uint32_t gintmsk;
+ /**Receive Status Queue Read Register (Read Only). Offset: 01Ch */
+ volatile uint32_t grxstsr;
+ /**Receive Status Queue Read & POP Register (Read Only). Offset: 020h*/
+ volatile uint32_t grxstsp;
+ /**Receive FIFO Size Register. Offset: 024h */
+ volatile uint32_t grxfsiz;
+ /**Non Periodic Transmit FIFO Size Register. Offset: 028h */
+ volatile uint32_t gnptxfsiz;
+ /**Non Periodic Transmit FIFO/Queue Status Register (Read
+ * Only). Offset: 02Ch */
+ volatile uint32_t gnptxsts;
+ /**I2C Access Register. Offset: 030h */
+ volatile uint32_t gi2cctl;
+ /**PHY Vendor Control Register. Offset: 034h */
+ volatile uint32_t gpvndctl;
+ /**General Purpose Input/Output Register. Offset: 038h */
+ volatile uint32_t ggpio;
+ /**User ID Register. Offset: 03Ch */
+ volatile uint32_t guid;
+ /**Synopsys ID Register (Read Only). Offset: 040h */
+ volatile uint32_t gsnpsid;
+ /**User HW Config1 Register (Read Only). Offset: 044h */
+ volatile uint32_t ghwcfg1;
+ /**User HW Config2 Register (Read Only). Offset: 048h */
+ volatile uint32_t 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
+
+ /**User HW Config3 Register (Read Only). Offset: 04Ch */
+ volatile uint32_t ghwcfg3;
+ /**User HW Config4 Register (Read Only). Offset: 050h*/
+ volatile uint32_t ghwcfg4;
+ /** Reserved Offset: 054h-0FFh */
+ volatile uint32_t reserved[43];
+ /** Host Periodic Transmit FIFO Size Register. Offset: 100h */
+ volatile uint32_t 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). */
+ volatile uint32_t dptxfsiz_dieptxf[15];
+} dwc_otg_core_global_regs_t;
+
+/**
+ * This union represents the bit fields of the Core OTG Control
+ * and Status Register (GOTGCTL). Set the bits using the bit
+ * fields then write the d32 value to the register.
+ */
+typedef union gotgctl_data
+{
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct
+ {
+ unsigned sesreqscs : 1;
+ unsigned sesreq : 1;
+ unsigned reserved2_7 : 6;
+ unsigned hstnegscs : 1;
+ unsigned hnpreq : 1;
+ unsigned hstsethnpen : 1;
+ unsigned devhnpen : 1;
+ unsigned reserved12_15 : 4;
+ unsigned conidsts : 1;
+ unsigned reserved17 : 1;
+ unsigned asesvld : 1;
+ unsigned bsesvld : 1;
+ unsigned currmod : 1;
+ unsigned reserved21_31 : 11;
+ } b;
+} gotgctl_data_t;
+
+/**
+ * This union represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT). Set/clear the bits using the bit fields then write the d32
+ * value to the register.
+ */
+typedef union gotgint_data
+{
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct
+ {
+ /** Current Mode */
+ unsigned reserved0_1 : 2;
+
+ /** Session End Detected */
+ unsigned sesenddet : 1;
+
+ unsigned reserved3_7 : 5;
+
+ /** Session Request Success Status Change */
+ unsigned sesreqsucstschng : 1;
+ /** Host Negotiation Success Status Change */
+ unsigned hstnegsucstschng : 1;
+
+ unsigned reserver10_16 : 7;
+
+ /** Host Negotiation Detected */
+ unsigned hstnegdet : 1;
+ /** A-Device Timeout Change */
+ unsigned adevtoutchng : 1;
+ /** Debounce Done */
+ unsigned debdone : 1;
+
+ unsigned reserved31_20 : 12;
+
+ } b;
+} gotgint_data_t;
+
+
+/**
+ * This union represents the bit fields of the Core AHB Configuration
+ * Register (GAHBCFG). Set/clear the bits using the bit fields then
+ * write the d32 value to the register.
+ */
+typedef union gahbcfg_data
+{
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct
+ {
+ unsigned glblintrmsk : 1;
+#define DWC_GAHBCFG_GLBINT_ENABLE 1
+
+ unsigned hburstlen : 4;
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7
+
+ unsigned dmaenable : 1;
+#define DWC_GAHBCFG_DMAENABLE 1
+ unsigned reserved : 1;
+ unsigned nptxfemplvl_txfemplvl : 1;
+ unsigned ptxfemplvl : 1;
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
+ unsigned reserved9_31 : 23;
+ } b;
+} gahbcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core USB Configuration
+ * Register (GUSBCFG). Set the bits using the bit fields then write
+ * the d32 value to the register.
+ */
+typedef union gusbcfg_data
+{
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct
+ {
+ unsigned toutcal : 3;
+ unsigned phyif : 1;
+ unsigned ulpi_utmi_sel : 1;
+ unsigned fsintf : 1;
+ unsigned physel : 1;
+ unsigned ddrsel : 1;
+ unsigned srpcap : 1;
+ unsigned hnpcap : 1;
+ unsigned usbtrdtim : 4;
+ unsigned nptxfrwnden : 1;
+ unsigned phylpwrclksel : 1;
+ unsigned otgutmifssel : 1;
+ unsigned ulpi_fsls : 1;
+ unsigned ulpi_auto_res : 1;
+ unsigned ulpi_clk_sus_m : 1;
+ unsigned ulpi_ext_vbus_drv : 1;
+ unsigned ulpi_int_vbus_indicator : 1;
+ unsigned term_sel_dl_pulse : 1;
+ unsigned reserved23_27 : 5;
+ unsigned tx_end_delay : 1;
+ unsigned reserved29_31 : 3;
+ } b;
+} gusbcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core Reset Register
+ * (GRSTCTL). Set/clear the bits using the bit fields then write the
+ * d32 value to the register.
+ */
+typedef union grstctl_data
+{
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct
+ {
+ /** Core Soft Reset (CSftRst) (Device and Host)
+ *
+ * The application can flush the control logic in the
+ * entire core using this bit. This bit resets the
+ * pipelines in the AHB Clock domain as well as the
+ * PHY Clock domain.
+ *
+ * The state machines are reset to an IDLE state, the
+ * control bits in the CSRs are cleared, all the
+ * transmit FIFOs and the receive FIFO are flushed.
+ *
+ * The status mask bits that control the generation of
+ * the interrupt, are cleared, to clear the
+ * interrupt. The interrupt status bits are not
+ * cleared, so the application can get the status of
+ * any events that occurred in the core after it has
+ * set this bit.
+ *
+ * Any transactions on the AHB are terminated as soon
+ * as possible following the protocol. Any
+ * transactions on the USB are terminated immediately.
+ *
+ * The configuration settings in the CSRs are
+ * unchanged, so the software doesn't have to
+ * reprogram these registers (Device
+ * Configuration/Host Configuration/Core System
+ * Configuration/Core PHY Configuration).
+ *
+ * The application can write to this bit, any time it
+ * wants to reset the core. This is a self clearing
+ * bit and the core clears this bit after all the
+ * necessary logic is reset in the core, which may
+ * take several clocks, depending on the current state
+ * of the core.
+ */
+ unsigned csftrst : 1;
+ /** Hclk Soft Reset
+ *
+ * The application uses this bit to reset the control logic in
+ * the AHB clock domain. Only AHB clock domain pipelines are
+ * reset.
+ */
+ unsigned hsftrst : 1;
+ /** Host Frame Counter Reset (Host Only)
+ *
+ * The application can reset the (micro)frame number
+ * counter inside the core, using this bit. When the
+ * (micro)frame counter is reset, the subsequent SOF
+ * sent out by the core, will have a (micro)frame
+ * number of 0.
+ */
+ unsigned hstfrm : 1;
+ /** In Token Sequence Learning Queue Flush
+ * (INTknQFlsh) (Device Only)
+ */
+ unsigned intknqflsh : 1;
+ /** RxFIFO Flush (RxFFlsh) (Device and Host)
+ *
+ * The application can flush the entire Receive FIFO
+ * using this bit.
The application must first + * ensure that the core is not in the middle of a + * transaction.
The application should write into + * this bit, only after making sure that neither the + * DMA engine is reading from the RxFIFO nor the MAC + * is writing the data in to the FIFO.
The + * application should wait until the bit is cleared + * before performing any other operations. This bit + * will takes 8 clocks (slowest of PHY or AHB clock) + * to clear. + */ + unsigned rxfflsh : 1; + /** TxFIFO Flush (TxFFlsh) (Device and Host). + * + * This bit is used to selectively flush a single or + * all transmit FIFOs. The application must first + * ensure that the core is not in the middle of a + * transaction.
The application should write into + * this bit, only after making sure that neither the + * DMA engine is writing into the TxFIFO nor the MAC + * is reading the data out of the FIFO.
The + * application should wait until the core clears this + * bit, before performing any operations. This bit + * will takes 8 clocks (slowest of PHY or AHB clock) + * to clear. + */ + unsigned txfflsh : 1; + /** TxFIFO Number (TxFNum) (Device and Host). + * + * This is the FIFO number which needs to be flushed, + * using the TxFIFO Flush bit. This field should not + * be changed until the TxFIFO Flush bit is cleared by + * the core. + * - 0x0 : Non Periodic TxFIFO Flush + * - 0x1 : Periodic TxFIFO #1 Flush in device mode + * or Periodic TxFIFO in host mode + * - 0x2 : Periodic TxFIFO #2 Flush in device mode. + * - ... + * - 0xF : Periodic TxFIFO #15 Flush in device mode + * - 0x10: Flush all the Transmit NonPeriodic and + * Transmit Periodic FIFOs in the core + */ + unsigned txfnum : 5; + /** Reserved */ + unsigned reserved11_29 : 19; + /** DMA Request Signal. Indicated DMA request is in + * probress. Used for debug purpose. */ + unsigned dmareq : 1; + /** AHB Master Idle. Indicates the AHB Master State + * Machine is in IDLE condition. */ + unsigned ahbidle : 1; + } b; +} grstctl_t; + + +/** + * This union represents the bit fields of the Core Interrupt Mask + * Register (GINTMSK). Set/clear the bits using the bit fields then + * write the d32 value to the register. + */ +typedef union gintmsk_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned reserved0 : 1; + unsigned modemismatch : 1; + unsigned otgintr : 1; + unsigned sofintr : 1; + unsigned rxstsqlvl : 1; + unsigned nptxfempty : 1; + unsigned ginnakeff : 1; + unsigned goutnakeff : 1; + unsigned reserved8 : 1; + unsigned i2cintr : 1; + unsigned erlysuspend : 1; + unsigned usbsuspend : 1; + unsigned usbreset : 1; + unsigned enumdone : 1; + unsigned isooutdrop : 1; + unsigned eopframe : 1; + unsigned reserved16 : 1; + unsigned epmismatch : 1; + unsigned inepintr : 1; + unsigned outepintr : 1; + unsigned incomplisoin : 1; + unsigned incomplisoout : 1; + unsigned reserved22_23 : 2; + unsigned portintr : 1; + unsigned hcintr : 1; + unsigned ptxfempty : 1; + unsigned reserved27 : 1; + unsigned conidstschng : 1; + unsigned disconnect : 1; + unsigned sessreqintr : 1; + unsigned wkupintr : 1; + } b; +} gintmsk_data_t; +/** + * This union represents the bit fields of the Core Interrupt Register + * (GINTSTS). Set/clear the bits using the bit fields then write the + * d32 value to the register. + */ +typedef union gintsts_data +{ + /** raw register data */ + uint32_t d32; +#define DWC_SOF_INTR_MASK 0x0008 + /** register bits */ + struct + { +#define DWC_HOST_MODE 1 + unsigned curmode : 1; + unsigned modemismatch : 1; + unsigned otgintr : 1; + unsigned sofintr : 1; + unsigned rxstsqlvl : 1; + unsigned nptxfempty : 1; + unsigned ginnakeff : 1; + unsigned goutnakeff : 1; + unsigned reserved8 : 1; + unsigned i2cintr : 1; + unsigned erlysuspend : 1; + unsigned usbsuspend : 1; + unsigned usbreset : 1; + unsigned enumdone : 1; + unsigned isooutdrop : 1; + unsigned eopframe : 1; + unsigned intokenrx : 1; + unsigned epmismatch : 1; + unsigned inepint: 1; + unsigned outepintr : 1; + unsigned incomplisoin : 1; + unsigned incomplisoout : 1; + unsigned reserved22_23 : 2; + unsigned portintr : 1; + unsigned hcintr : 1; + unsigned ptxfempty : 1; + unsigned reserved27 : 1; + unsigned conidstschng : 1; + unsigned disconnect : 1; + unsigned sessreqintr : 1; + unsigned wkupintr : 1; + } b; +} gintsts_data_t; + + +/** + * This union represents the bit fields in the Device Receive Status Read and + * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 + * element then read out the bits using the bit elements. + */ +typedef union device_grxsts_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned epnum : 4; + unsigned bcnt : 11; + unsigned dpid : 2; +#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 + unsigned pktsts : 4; + unsigned fn : 4; + unsigned reserved : 7; + } b; +} device_grxsts_data_t; + +/** + * This union represents the bit fields in the Host Receive Status Read and + * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 + * element then read out the bits using the bit elements. + */ +typedef union host_grxsts_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned chnum : 4; + unsigned bcnt : 11; + unsigned dpid : 2; + unsigned pktsts : 4; +#define DWC_GRXSTS_PKTSTS_IN 0x2 +#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3 +#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5 +#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7 + unsigned reserved : 11; + } b; +} host_grxsts_data_t; + +/** + * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ, + * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the d32 element then + * read out the bits using the bit elements. + */ +typedef union fifosize_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned startaddr : 16; + unsigned depth : 16; + } b; +} fifosize_data_t; + +/** + * This union represents the bit fields in the Non-Periodic Transmit + * FIFO/Queue Status Register (GNPTXSTS). Read the register into the + * d32 element then read out the bits using the bit + * elements. + */ +typedef union gnptxsts_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned nptxfspcavail : 16; + unsigned nptxqspcavail : 8; + /** Top of the Non-Periodic Transmit Request Queue + * - bit 24 - Terminate (Last entry for the selected + * channel/EP) + * - bits 26:25 - Token Type + * - 2'b00 - IN/OUT + * - 2'b01 - Zero Length OUT + * - 2'b10 - PING/Complete Split + * - 2'b11 - Channel Halt + * - bits 30:27 - Channel/EP Number + */ + unsigned nptxqtop_terminate : 1; + unsigned nptxqtop_token : 2; + unsigned nptxqtop_chnep : 4; + unsigned reserved : 1; + } b; +} gnptxsts_data_t; + +/** + * This union represents the bit fields in the Transmit + * FIFO Status Register (DTXFSTS). Read the register into the + * d32 element then read out the bits using the bit + * elements. + */ +typedef union dtxfsts_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned txfspcavail : 16; + unsigned reserved : 16; + } b; +} dtxfsts_data_t; + +/** + * This union represents the bit fields in the I2C Control Register + * (I2CCTL). Read the register into the d32 element then read out the + * bits using the bit elements. + */ +typedef union gi2cctl_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned rwdata : 8; + unsigned regaddr : 8; + unsigned addr : 7; + unsigned i2cen : 1; + unsigned ack : 1; + unsigned i2csuspctl : 1; + unsigned i2cdevaddr : 2; + unsigned reserved : 2; + unsigned rw : 1; + unsigned bsydne : 1; + } b; +} gi2cctl_data_t; + +/** + * This union represents the bit fields in the User HW Config1 + * Register. Read the register into the d32 element then read + * out the bits using the bit elements. + */ +typedef union hwcfg1_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned ep_dir0 : 2; + unsigned ep_dir1 : 2; + unsigned ep_dir2 : 2; + unsigned ep_dir3 : 2; + unsigned ep_dir4 : 2; + unsigned ep_dir5 : 2; + unsigned ep_dir6 : 2; + unsigned ep_dir7 : 2; + unsigned ep_dir8 : 2; + unsigned ep_dir9 : 2; + unsigned ep_dir10 : 2; + unsigned ep_dir11 : 2; + unsigned ep_dir12 : 2; + unsigned ep_dir13 : 2; + unsigned ep_dir14 : 2; + unsigned ep_dir15 : 2; + } b; +} hwcfg1_data_t; + +/** + * This union represents the bit fields in the User HW Config2 + * Register. Read the register into the d32 element then read + * out the bits using the bit elements. + */ +typedef union hwcfg2_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /* GHWCFG2 */ + unsigned op_mode : 3; +#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0 +#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1 +#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2 +#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 +#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 +#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 + + unsigned architecture : 2; + unsigned point2point : 1; + unsigned hs_phy_type : 2; +#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 + + unsigned fs_phy_type : 2; + unsigned num_dev_ep : 4; + unsigned num_host_chan : 4; + unsigned perio_ep_supported : 1; + unsigned dynamic_fifo : 1; + unsigned multi_proc_int : 1; + unsigned reserved21 : 1; + unsigned nonperio_tx_q_depth : 2; + unsigned host_perio_tx_q_depth : 2; + unsigned dev_token_q_depth : 5; + unsigned reserved31 : 1; + } b; +} hwcfg2_data_t; + +/** + * This union represents the bit fields in the User HW Config3 + * Register. Read the register into the d32 element then read + * out the bits using the bit elements. + */ +typedef union hwcfg3_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /* GHWCFG3 */ + unsigned xfer_size_cntr_width : 4; + unsigned packet_size_cntr_width : 3; + unsigned otg_func : 1; + unsigned i2c : 1; + unsigned vendor_ctrl_if : 1; + unsigned optional_features : 1; + unsigned synch_reset_type : 1; + unsigned ahb_phy_clock_synch : 1; + unsigned reserved15_13 : 3; + unsigned dfifo_depth : 16; + } b; +} hwcfg3_data_t; + +/** + * This union represents the bit fields in the User HW Config4 + * Register. Read the register into the d32 element then read + * out the bits using the bit elements. + */ +typedef union hwcfg4_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned num_dev_perio_in_ep : 4; + unsigned power_optimiz : 1; + unsigned min_ahb_freq : 9; + unsigned utmi_phy_data_width : 2; + unsigned num_dev_mode_ctrl_ep : 4; + unsigned iddig_filt_en : 1; + unsigned vbus_valid_filt_en : 1; + unsigned a_valid_filt_en : 1; + unsigned b_valid_filt_en : 1; + unsigned session_end_filt_en : 1; + unsigned ded_fifo_en : 1; + unsigned num_in_eps : 4; + unsigned desc_dma : 1; + unsigned desc_dma_dyn : 1; + } b; +} hwcfg4_data_t; + +//////////////////////////////////////////// +// Device Registers +/** + * 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. + */ +typedef struct dwc_otg_dev_global_regs +{ + /** Device Configuration Register. Offset 800h */ + volatile uint32_t dcfg; + /** Device Control Register. Offset: 804h */ + volatile uint32_t dctl; + /** Device Status Register (Read Only). Offset: 808h */ + volatile uint32_t dsts; + /** Reserved. Offset: 80Ch */ + uint32_t unused; + /** Device IN Endpoint Common Interrupt Mask + * Register. Offset: 810h */ + volatile uint32_t diepmsk; + /** Device OUT Endpoint Common Interrupt Mask + * Register. Offset: 814h */ + volatile uint32_t doepmsk; + /** Device All Endpoints Interrupt Register. Offset: 818h */ + volatile uint32_t daint; + /** Device All Endpoints Interrupt Mask Register. Offset: + * 81Ch */ + volatile uint32_t daintmsk; + /** Device IN Token Queue Read Register-1 (Read Only). + * Offset: 820h */ + volatile uint32_t dtknqr1; + /** Device IN Token Queue Read Register-2 (Read Only). + * Offset: 824h */ + volatile uint32_t dtknqr2; + /** Device VBUS discharge Register. Offset: 828h */ + volatile uint32_t dvbusdis; + /** Device VBUS Pulse Register. Offset: 82Ch */ + volatile uint32_t dvbuspulse; + /** Device IN Token Queue Read Register-3 (Read Only). / + * Device Thresholding control register (Read/Write) + * Offset: 830h */ + volatile uint32_t dtknqr3_dthrctl; + /** Device IN Token Queue Read Register-4 (Read Only). / + * Device IN EPs empty Inr. Mask Register (Read/Write) + * Offset: 834h */ + volatile uint32_t dtknqr4_fifoemptymsk; + /** Device Each Endpoint Interrupt Register (Read Only). / + * Offset: 838h */ + volatile uint32_t deachint; + /** Device Each Endpoint Interrupt mask Register (Read/Write). / + * Offset: 83Ch */ + volatile uint32_t deachintmsk; + /** Device Each In Endpoint Interrupt mask Register (Read/Write). / + * Offset: 840h */ + volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS]; + /** Device Each Out Endpoint Interrupt mask Register (Read/Write). / + * Offset: 880h */ + volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS]; +} dwc_otg_device_global_regs_t; + +/** + * This union represents the bit fields in the Device Configuration + * Register. Read the register into the d32 member then + * set/clear the bits using the bit elements. Write the + * d32 member to the dcfg register. + */ +typedef union dcfg_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Device Speed */ + unsigned devspd : 2; + /** Non Zero Length Status OUT Handshake */ + unsigned nzstsouthshk : 1; +#define DWC_DCFG_SEND_STALL 1 + + unsigned reserved3 : 1; + /** Device Addresses */ + unsigned devaddr : 7; + /** Periodic Frame Interval */ + unsigned perfrint : 2; +#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 + + unsigned reserved13_17 : 5; + /** In Endpoint Mis-match count */ + unsigned epmscnt : 5; + /** Enable Descriptor DMA in Device mode */ + unsigned descdma : 1; + } b; +} dcfg_data_t; + +/** + * This union represents the bit fields in the Device Control + * Register. Read the register into the d32 member then + * set/clear the bits using the bit elements. + */ +typedef union dctl_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Remote Wakeup */ + unsigned rmtwkupsig : 1; + /** Soft Disconnect */ + unsigned sftdiscon : 1; + /** Global Non-Periodic IN NAK Status */ + unsigned gnpinnaksts : 1; + /** Global OUT NAK Status */ + unsigned goutnaksts : 1; + /** Test Control */ + unsigned tstctl : 3; + /** Set Global Non-Periodic IN NAK */ + unsigned sgnpinnak : 1; + /** Clear Global Non-Periodic IN NAK */ + unsigned cgnpinnak : 1; + /** Set Global OUT NAK */ + unsigned sgoutnak : 1; + /** Clear Global OUT NAK */ + unsigned cgoutnak : 1; + + /** Power-On Programming Done */ + unsigned pwronprgdone : 1; + /** Global Continue on BNA */ + unsigned gcontbna : 1; + /** Global Multi Count */ + unsigned gmc : 2; + /** Ignore Frame Number for ISOC EPs */ + unsigned ifrmnum : 1; + /** NAK on Babble */ + unsigned nakonbble : 1; + + unsigned reserved16_31 : 16; + } b; +} dctl_data_t; + +/** + * This union represents the bit fields in the Device Status + * Register. Read the register into the d32 member then + * set/clear the bits using the bit elements. + */ +typedef union dsts_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Suspend Status */ + unsigned suspsts : 1; + /** Enumerated Speed */ + unsigned enumspd : 2; +#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 + /** Erratic Error */ + unsigned errticerr : 1; + unsigned reserved4_7: 4; + /** Frame or Microframe Number of the received SOF */ + unsigned soffn : 14; + unsigned reserved22_31 : 10; + } b; +} dsts_data_t; + + +/** + * This union represents the bit fields in the Device IN EP Interrupt + * Register and the Device IN EP Common Mask Register. + * + * - Read the register into the d32 member then set/clear the + * bits using the bit elements. + */ +typedef union diepint_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Transfer complete mask */ + unsigned xfercompl : 1; + /** Endpoint disable mask */ + unsigned epdisabled : 1; + /** AHB Error mask */ + unsigned ahberr : 1; + /** TimeOUT Handshake mask (non-ISOC EPs) */ + unsigned timeout : 1; + /** IN Token received with TxF Empty mask */ + unsigned intktxfemp : 1; + /** IN Token Received with EP mismatch mask */ + unsigned intknepmis : 1; + /** IN Endpoint HAK Effective mask */ + unsigned inepnakeff : 1; + /** IN Endpoint HAK Effective mask */ + unsigned emptyintr : 1; + unsigned txfifoundrn : 1; + + /** BNA Interrupt mask */ + unsigned bna : 1; + unsigned reserved10_12 : 3; + /** BNA Interrupt mask */ + unsigned nak : 1; + unsigned reserved14_31 : 18; + } b; +} diepint_data_t; + +/** + * This union represents the bit fields in the Device IN EP + * Common/Dedicated Interrupt Mask Register. + */ +typedef union diepint_data diepmsk_data_t; + +/** + * This union represents the bit fields in the Device OUT EP Interrupt + * Registerand Device OUT EP Common Interrupt Mask Register. + * + * - Read the register into the d32 member then set/clear the + * bits using the bit elements. + */ +typedef union doepint_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Transfer complete */ + unsigned xfercompl : 1; + /** Endpoint disable */ + unsigned epdisabled : 1; + /** AHB Error */ + unsigned ahberr : 1; + /** Setup Phase Done (contorl EPs) */ + unsigned setup : 1; + /** OUT Token Received when Endpoint Disabled */ + unsigned outtknepdis : 1; + unsigned stsphsercvd : 1; + /** Back-to-Back SETUP Packets Received */ + unsigned back2backsetup : 1; + unsigned reserved7 : 1; + /** OUT packet Error */ + unsigned outpkterr : 1; + /** BNA Interrupt */ + unsigned bna : 1; + unsigned reserved10 : 1; + /** Packet Drop Status */ + unsigned pktdrpsts : 1; + /** Babble Interrupt */ + unsigned babble : 1; + /** NAK Interrupt */ + unsigned nak : 1; + /** NYET Interrupt */ + unsigned nyet : 1; + + unsigned reserved15_31 : 17; + } b; +} doepint_data_t; + +/** + * This union represents the bit fields in the Device OUT EP + * Common/Dedicated Interrupt Mask Register. + */ +typedef union doepint_data doepmsk_data_t; + +/** + * This union represents the bit fields in the Device All EP Interrupt + * and Mask Registers. + * - Read the register into the d32 member then set/clear the + * bits using the bit elements. + */ +typedef union daint_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** IN Endpoint bits */ + unsigned in : 16; + /** OUT Endpoint bits */ + unsigned out : 16; + } ep; + struct + { + /** IN Endpoint bits */ + unsigned inep0 : 1; + unsigned inep1 : 1; + unsigned inep2 : 1; + unsigned inep3 : 1; + unsigned inep4 : 1; + unsigned inep5 : 1; + unsigned inep6 : 1; + unsigned inep7 : 1; + unsigned inep8 : 1; + unsigned inep9 : 1; + unsigned inep10 : 1; + unsigned inep11 : 1; + unsigned inep12 : 1; + unsigned inep13 : 1; + unsigned inep14 : 1; + unsigned inep15 : 1; + /** OUT Endpoint bits */ + unsigned outep0 : 1; + unsigned outep1 : 1; + unsigned outep2 : 1; + unsigned outep3 : 1; + unsigned outep4 : 1; + unsigned outep5 : 1; + unsigned outep6 : 1; + unsigned outep7 : 1; + unsigned outep8 : 1; + unsigned outep9 : 1; + unsigned outep10 : 1; + unsigned outep11 : 1; + unsigned outep12 : 1; + unsigned outep13 : 1; + unsigned outep14 : 1; + unsigned outep15 : 1; + } b; +} daint_data_t; + +/** + * This union represents the bit fields in the Device IN Token Queue + * Read Registers. + * - Read the register into the d32 member. + * - READ-ONLY Register + */ +typedef union dtknq1_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** In Token Queue Write Pointer */ + unsigned intknwptr : 5; + /** Reserved */ + unsigned reserved05_06 : 2; + /** write pointer has wrapped. */ + unsigned wrap_bit : 1; + /** EP Numbers of IN Tokens 0 ... 4 */ + unsigned epnums0_5 : 24; + }b; +} dtknq1_data_t; + +/** + * This union represents Threshold control Register + * - Read and write the register into the d32 member. + * - READ-WRITABLE Register + */ +typedef union dthrctl_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** non ISO Tx Thr. Enable */ + unsigned non_iso_thr_en : 1; + /** ISO Tx Thr. Enable */ + unsigned iso_thr_en : 1; + /** Tx Thr. Length */ + unsigned tx_thr_len : 9; + /** Reserved */ + unsigned reserved11_15 : 5; + /** Rx Thr. Enable */ + unsigned rx_thr_en : 1; + /** Rx Thr. Length */ + unsigned rx_thr_len : 9; + /** Reserved */ + unsigned reserved26_31 : 6; + }b; +} dthrctl_data_t; + + +/** + * 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. + */ +typedef struct dwc_otg_dev_in_ep_regs +{ + /** Device IN Endpoint Control Register. Offset:900h + + * (ep_num * 20h) + 00h */ + volatile uint32_t diepctl; + /** Reserved. Offset:900h + (ep_num * 20h) + 04h */ + uint32_t reserved04; + /** Device IN Endpoint Interrupt Register. Offset:900h + + * (ep_num * 20h) + 08h */ + volatile uint32_t diepint; + /** Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ + uint32_t reserved0C; + /** Device IN Endpoint Transfer Size + * Register. Offset:900h + (ep_num * 20h) + 10h */ + volatile uint32_t dieptsiz; + /** Device IN Endpoint DMA Address Register. Offset:900h + + * (ep_num * 20h) + 14h */ + volatile uint32_t diepdma; + /** Device IN Endpoint Transmit FIFO Status Register. Offset:900h + + * (ep_num * 20h) + 18h */ + volatile uint32_t dtxfsts; + /** Device IN Endpoint DMA Buffer Register. Offset:900h + + * (ep_num * 20h) + 1Ch */ + volatile uint32_t diepdmab; +} dwc_otg_dev_in_ep_regs_t; + +/** + * 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. + */ +typedef struct dwc_otg_dev_out_ep_regs +{ + /** Device OUT Endpoint Control Register. Offset:B00h + + * (ep_num * 20h) + 00h */ + volatile uint32_t doepctl; + /** Device OUT Endpoint Frame number Register. Offset: + * B00h + (ep_num * 20h) + 04h */ + volatile uint32_t doepfn; + /** Device OUT Endpoint Interrupt Register. Offset:B00h + + * (ep_num * 20h) + 08h */ + volatile uint32_t doepint; + /** Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ + uint32_t reserved0C; + /** Device OUT Endpoint Transfer Size Register. Offset: + * B00h + (ep_num * 20h) + 10h */ + volatile uint32_t doeptsiz; + /** Device OUT Endpoint DMA Address Register. Offset:B00h + * + (ep_num * 20h) + 14h */ + volatile uint32_t doepdma; + /** Reserved. Offset:B00h + * (ep_num * 20h) + 1Ch */ + uint32_t unused; + /** Device OUT Endpoint DMA Buffer Register. Offset:B00h + * + (ep_num * 20h) + 1Ch */ + uint32_t doepdmab; +} dwc_otg_dev_out_ep_regs_t; + +/** + * This union represents the bit fields in the Device EP Control + * Register. Read the register into the d32 member then + * set/clear the bits using the bit elements. + */ +typedef union depctl_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Maximum Packet Size + * IN/OUT EPn + * IN/OUT EP0 - 2 bits + * 2'b00: 64 Bytes + * 2'b01: 32 + * 2'b10: 16 + * 2'b11: 8 */ + unsigned mps : 11; +#define DWC_DEP0CTL_MPS_64 0 +#define DWC_DEP0CTL_MPS_32 1 +#define DWC_DEP0CTL_MPS_16 2 +#define DWC_DEP0CTL_MPS_8 3 + + /** Next Endpoint + * IN EPn/IN EP0 + * OUT EPn/OUT EP0 - reserved */ + unsigned nextep : 4; + + /** USB Active Endpoint */ + unsigned usbactep : 1; + + /** Endpoint DPID (INTR/Bulk IN and OUT endpoints) + * This field contains the PID of the packet going to + * be received or transmitted on this endpoint. The + * application should program the PID of the first + * packet going to be received or transmitted on this + * endpoint , after the endpoint is + * activated. Application use the SetD1PID and + * SetD0PID fields of this register to program either + * D0 or D1 PID. + * + * The encoding for this field is + * - 0: D0 + * - 1: D1 + */ + unsigned dpid : 1; + + /** NAK Status */ + unsigned naksts : 1; + + /** Endpoint Type + * 2'b00: Control + * 2'b01: Isochronous + * 2'b10: Bulk + * 2'b11: Interrupt */ + unsigned eptype : 2; + + /** Snoop Mode + * OUT EPn/OUT EP0 + * IN EPn/IN EP0 - reserved */ + unsigned snp : 1; + + /** Stall Handshake */ + unsigned stall : 1; + + /** Tx Fifo Number + * IN EPn/IN EP0 + * OUT EPn/OUT EP0 - reserved */ + unsigned txfnum : 4; + + /** Clear NAK */ + unsigned cnak : 1; + /** Set NAK */ + unsigned snak : 1; + /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints) + * Writing to this field sets the Endpoint DPID (DPID) + * field in this register to DATA0. Set Even + * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints) + * Writing to this field sets the Even/Odd + * (micro)frame (EO_FrNum) field to even (micro) + * frame. + */ + unsigned setd0pid : 1; + /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints) + * Writing to this field sets the Endpoint DPID (DPID) + * field in this register to DATA1 Set Odd + * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints) + * Writing to this field sets the Even/Odd + * (micro)frame (EO_FrNum) field to odd (micro) frame. + */ + unsigned setd1pid : 1; + /** Endpoint Disable */ + unsigned epdis : 1; + /** Endpoint Enable */ + unsigned epena : 1; + } b; +} depctl_data_t; + +/** + * This union represents the bit fields in the Device EP Transfer + * Size Register. Read the register into the d32 member then + * set/clear the bits using the bit elements. + */ +typedef union deptsiz_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + /** Transfer size */ + unsigned xfersize : 19; + /** Packet Count */ + unsigned pktcnt : 10; + /** Multi Count - Periodic IN endpoints */ + unsigned mc : 2; + unsigned reserved : 1; + } b; +} deptsiz_data_t; + +/** + * This union represents the bit fields in the Device EP 0 Transfer + * Size Register. Read the register into the d32 member then + * set/clear the bits using the bit elements. + */ +typedef union deptsiz0_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + /** Transfer size */ + unsigned xfersize : 7; + /** Reserved */ + unsigned reserved7_18 : 12; + /** Packet Count */ + unsigned pktcnt : 1; + /** Reserved */ + unsigned reserved20_28 : 9; + /**Setup Packet Count (DOEPTSIZ0 Only) */ + unsigned supcnt : 2; + unsigned reserved31; + } b; +} deptsiz0_data_t; + + +///////////////////////////////////////////////// +// DMA Descriptor Specific Structures +// + +/** Buffer status definitions */ + +#define BS_HOST_READY 0x0 +#define BS_DMA_BUSY 0x1 +#define BS_DMA_DONE 0x2 +#define BS_HOST_BUSY 0x3 + +/** Receive/Transmit status definitions */ + +#define RTS_SUCCESS 0x0 +#define RTS_BUFFLUSH 0x1 +#define RTS_RESERVED 0x2 +#define RTS_BUFERR 0x3 + + +/** + * This union represents the bit fields in the DMA Descriptor + * status quadlet. Read the quadlet into the d32 member then + * set/clear the bits using the bit, b_iso_out and + * b_iso_in elements. + */ +typedef union desc_sts_data +{ + /** raw register data */ + uint32_t d32; + /** quadlet bits */ + struct { + /** Received number of bytes */ + unsigned bytes : 16; + + unsigned reserved16_22 : 7; + /** Multiple Transfer - only for OUT EPs */ + unsigned mtrf : 1; + /** Setup Packet received - only for OUT EPs */ + unsigned sr : 1; + /** Interrupt On Complete */ + unsigned ioc : 1; + /** Short Packet */ + unsigned sp : 1; + /** Last */ + unsigned l : 1; + /** Receive Status */ + unsigned sts : 2; + /** Buffer Status */ + unsigned bs : 2; + } b; + +#ifdef DWC_EN_ISOC + /** iso out quadlet bits */ + struct { + /** Received number of bytes */ + unsigned rxbytes : 11; + + unsigned reserved11 : 1; + /** Frame Number */ + unsigned framenum : 11; + /** Received ISO Data PID */ + unsigned pid : 2; + /** Interrupt On Complete */ + unsigned ioc : 1; + /** Short Packet */ + unsigned sp : 1; + /** Last */ + unsigned l : 1; + /** Receive Status */ + unsigned rxsts : 2; + /** Buffer Status */ + unsigned bs : 2; + } b_iso_out; + + /** iso in quadlet bits */ + struct { + /** Transmited number of bytes */ + unsigned txbytes : 12; + /** Frame Number */ + unsigned framenum : 11; + /** Transmited ISO Data PID */ + unsigned pid : 2; + /** Interrupt On Complete */ + unsigned ioc : 1; + /** Short Packet */ + unsigned sp : 1; + /** Last */ + unsigned l : 1; + /** Transmit Status */ + unsigned txsts : 2; + /** Buffer Status */ + unsigned bs : 2; + } b_iso_in; +#endif //DWC_EN_ISOC +} desc_sts_data_t; + +/** + * DMA Descriptor structure + * + * DMA Descriptor structure contains two quadlets: + * Status quadlet and Data buffer pointer. + */ +typedef struct dwc_otg_dma_desc +{ + /** DMA Descriptor status quadlet */ + desc_sts_data_t status; + /** DMA Descriptor data buffer pointer */ + dma_addr_t buf; +} dwc_otg_dma_desc_t; + +/** + * The dwc_otg_dev_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. + */ +typedef struct dwc_otg_dev_if +{ + /** Pointer to device Global registers. + * Device Global Registers starting at offset 800h + */ + dwc_otg_device_global_regs_t *dev_global_regs; +#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 + + /** + * Device Logical IN Endpoint-Specific Registers 900h-AFCh + */ + dwc_otg_dev_in_ep_regs_t *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 */ + dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS]; +#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 + + /* Device configuration information*/ + uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */ + uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */ + uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/ + + /** Size of periodic FIFOs (Bytes) */ + uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS]; + + /** Size of Tx FIFOs (Bytes) */ + uint16_t tx_fifo_size[MAX_TX_FIFOS]; + + /** Thresholding enable flags and length varaiables **/ + uint16_t rx_thr_en; + uint16_t iso_tx_thr_en; + uint16_t non_iso_tx_thr_en; + + uint16_t rx_thr_length; + uint16_t tx_thr_length; + + /** + * Pointers to the DMA Descriptors for EP0 Control + * transfers (virtual and physical) + */ + /** 2 descriptors for SETUP packets */ + uint32_t dma_setup_desc_addr[2]; + dwc_otg_dma_desc_t* setup_desc_addr[2]; + + /** Pointer to Descriptor with latest SETUP packet */ + dwc_otg_dma_desc_t* psetup; + + /** Index of current SETUP handler descriptor */ + uint32_t setup_desc_index; + + /** Descriptor for Data In or Status In phases */ + uint32_t dma_in_desc_addr; + dwc_otg_dma_desc_t* in_desc_addr;; + + /** Descriptor for Data Out or Status Out phases */ + uint32_t dma_out_desc_addr; + dwc_otg_dma_desc_t* out_desc_addr; +} dwc_otg_dev_if_t; + + + + +///////////////////////////////////////////////// +// Host Mode Register Structures +// +/** + * The Host Global Registers structure defines the size and relative + * field offsets for the Host Mode Global Registers. Host Global + * Registers offsets 400h-7FFh. +*/ +typedef struct dwc_otg_host_global_regs +{ + /** Host Configuration Register. Offset: 400h */ + volatile uint32_t hcfg; + /** Host Frame Interval Register. Offset: 404h */ + volatile uint32_t hfir; + /** Host Frame Number / Frame Remaining Register. Offset: 408h */ + volatile uint32_t hfnum; + /** Reserved. Offset: 40Ch */ + uint32_t reserved40C; + /** Host Periodic Transmit FIFO/ Queue Status Register. Offset: 410h */ + volatile uint32_t hptxsts; + /** Host All Channels Interrupt Register. Offset: 414h */ + volatile uint32_t haint; + /** Host All Channels Interrupt Mask Register. Offset: 418h */ + volatile uint32_t haintmsk; +} dwc_otg_host_global_regs_t; + +/** + * This union represents the bit fields in the Host Configuration Register. + * Read the register into the d32 member then set/clear the bits using + * the bit elements. Write the d32 member to the hcfg register. + */ +typedef union hcfg_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + /** FS/LS Phy Clock Select */ + unsigned fslspclksel : 2; +#define DWC_HCFG_30_60_MHZ 0 +#define DWC_HCFG_48_MHZ 1 +#define DWC_HCFG_6_MHZ 2 + + /** FS/LS Only Support */ + unsigned fslssupp : 1; + } b; +} hcfg_data_t; + +/** + * This union represents the bit fields in the Host Frame Remaing/Number + * Register. + */ +typedef union hfir_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + unsigned frint : 16; + unsigned reserved : 16; + } b; +} hfir_data_t; + +/** + * This union represents the bit fields in the Host Frame Remaing/Number + * Register. + */ +typedef union hfnum_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + unsigned frnum : 16; +#define DWC_HFNUM_MAX_FRNUM 0x3FFF + unsigned frrem : 16; + } b; +} hfnum_data_t; + +typedef union hptxsts_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + unsigned ptxfspcavail : 16; + unsigned ptxqspcavail : 8; + /** Top of the Periodic Transmit Request Queue + * - bit 24 - Terminate (last entry for the selected channel) + * - bits 26:25 - Token Type + * - 2'b00 - Zero length + * - 2'b01 - Ping + * - 2'b10 - Disable + * - bits 30:27 - Channel Number + * - bit 31 - Odd/even microframe + */ + unsigned ptxqtop_terminate : 1; + unsigned ptxqtop_token : 2; + unsigned ptxqtop_chnum : 4; + unsigned ptxqtop_odd : 1; + } b; +} hptxsts_data_t; + +/** + * This union represents the bit fields in the Host Port Control and Status + * Register. Read the register into the d32 member then set/clear the + * bits using the bit elements. Write the d32 member to the + * hprt0 register. + */ +typedef union hprt0_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned prtconnsts : 1; + unsigned prtconndet : 1; + unsigned prtena : 1; + unsigned prtenchng : 1; + unsigned prtovrcurract : 1; + unsigned prtovrcurrchng : 1; + unsigned prtres : 1; + unsigned prtsusp : 1; + unsigned prtrst : 1; + unsigned reserved9 : 1; + unsigned prtlnsts : 2; + unsigned prtpwr : 1; + unsigned prttstctl : 4; + unsigned prtspd : 2; +#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0 +#define DWC_HPRT0_PRTSPD_FULL_SPEED 1 +#define DWC_HPRT0_PRTSPD_LOW_SPEED 2 + unsigned reserved19_31 : 13; + } b; +} hprt0_data_t; + +/** + * This union represents the bit fields in the Host All Interrupt + * Register. + */ +typedef union haint_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned ch0 : 1; + unsigned ch1 : 1; + unsigned ch2 : 1; + unsigned ch3 : 1; + unsigned ch4 : 1; + unsigned ch5 : 1; + unsigned ch6 : 1; + unsigned ch7 : 1; + unsigned ch8 : 1; + unsigned ch9 : 1; + unsigned ch10 : 1; + unsigned ch11 : 1; + unsigned ch12 : 1; + unsigned ch13 : 1; + unsigned ch14 : 1; + unsigned ch15 : 1; + unsigned reserved : 16; + } b; + + struct + { + unsigned chint : 16; + unsigned reserved : 16; + } b2; +} haint_data_t; + +/** + * This union represents the bit fields in the Host All Interrupt + * Register. + */ +typedef union haintmsk_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + unsigned ch0 : 1; + unsigned ch1 : 1; + unsigned ch2 : 1; + unsigned ch3 : 1; + unsigned ch4 : 1; + unsigned ch5 : 1; + unsigned ch6 : 1; + unsigned ch7 : 1; + unsigned ch8 : 1; + unsigned ch9 : 1; + unsigned ch10 : 1; + unsigned ch11 : 1; + unsigned ch12 : 1; + unsigned ch13 : 1; + unsigned ch14 : 1; + unsigned ch15 : 1; + unsigned reserved : 16; + } b; + + struct + { + unsigned chint : 16; + unsigned reserved : 16; + } b2; +} haintmsk_data_t; + +/** + * Host Channel Specific Registers. 500h-5FCh + */ +typedef struct dwc_otg_hc_regs +{ + /** Host Channel 0 Characteristic Register. Offset: 500h + (chan_num * 20h) + 00h */ + volatile uint32_t hcchar; + /** Host Channel 0 Split Control Register. Offset: 500h + (chan_num * 20h) + 04h */ + volatile uint32_t hcsplt; + /** Host Channel 0 Interrupt Register. Offset: 500h + (chan_num * 20h) + 08h */ + volatile uint32_t hcint; + /** Host Channel 0 Interrupt Mask Register. Offset: 500h + (chan_num * 20h) + 0Ch */ + volatile uint32_t hcintmsk; + /** Host Channel 0 Transfer Size Register. Offset: 500h + (chan_num * 20h) + 10h */ + volatile uint32_t hctsiz; + /** Host Channel 0 DMA Address Register. Offset: 500h + (chan_num * 20h) + 14h */ + volatile uint32_t hcdma; + /** Reserved. Offset: 500h + (chan_num * 20h) + 18h - 500h + (chan_num * 20h) + 1Ch */ + uint32_t reserved[2]; +} dwc_otg_hc_regs_t; + +/** + * This union represents the bit fields in the Host Channel Characteristics + * Register. Read the register into the d32 member then set/clear the + * bits using the bit elements. Write the d32 member to the + * hcchar register. + */ +typedef union hcchar_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + /** Maximum packet size in bytes */ + unsigned mps : 11; + + /** Endpoint number */ + unsigned epnum : 4; + + /** 0: OUT, 1: IN */ + unsigned epdir : 1; + + unsigned reserved : 1; + + /** 0: Full/high speed device, 1: Low speed device */ + unsigned lspddev : 1; + + /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ + unsigned eptype : 2; + + /** Packets per frame for periodic transfers. 0 is reserved. */ + unsigned multicnt : 2; + + /** Device address */ + unsigned devaddr : 7; + + /** + * Frame to transmit periodic transaction. + * 0: even, 1: odd + */ + unsigned oddfrm : 1; + + /** Channel disable */ + unsigned chdis : 1; + + /** Channel enable */ + unsigned chen : 1; + } b; +} hcchar_data_t; + +typedef union hcsplt_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + /** Port Address */ + unsigned prtaddr : 7; + + /** Hub Address */ + unsigned hubaddr : 7; + + /** Transaction Position */ + unsigned xactpos : 2; +#define DWC_HCSPLIT_XACTPOS_MID 0 +#define DWC_HCSPLIT_XACTPOS_END 1 +#define DWC_HCSPLIT_XACTPOS_BEGIN 2 +#define DWC_HCSPLIT_XACTPOS_ALL 3 + + /** Do Complete Split */ + unsigned compsplt : 1; + + /** Reserved */ + unsigned reserved : 14; + + /** Split Enble */ + unsigned spltena : 1; + } b; +} hcsplt_data_t; + + +/** + * This union represents the bit fields in the Host All Interrupt + * Register. + */ +typedef union hcint_data +{ + /** raw register data */ + uint32_t d32; + /** register bits */ + struct + { + /** Transfer Complete */ + unsigned xfercomp : 1; + /** Channel Halted */ + unsigned chhltd : 1; + /** AHB Error */ + unsigned ahberr : 1; + /** STALL Response Received */ + unsigned stall : 1; + /** NAK Response Received */ + unsigned nak : 1; + /** ACK Response Received */ + unsigned ack : 1; + /** NYET Response Received */ + unsigned nyet : 1; + /** Transaction Err */ + unsigned xacterr : 1; + /** Babble Error */ + unsigned bblerr : 1; + /** Frame Overrun */ + unsigned frmovrun : 1; + /** Data Toggle Error */ + unsigned datatglerr : 1; + /** Reserved */ + unsigned reserved : 21; + } b; +} hcint_data_t; + +/** + * This union represents the bit fields in the Host Channel Transfer Size + * Register. Read the register into the d32 member then set/clear the + * bits using the bit elements. Write the d32 member to the + * hcchar register. + */ +typedef union hctsiz_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + /** Total transfer size in bytes */ + unsigned xfersize : 19; + + /** Data packets to transfer */ + unsigned pktcnt : 10; + + /** + * Packet ID for next data packet + * 0: DATA0 + * 1: DATA2 + * 2: DATA1 + * 3: MDATA (non-Control), SETUP (Control) + */ + unsigned pid : 2; +#define DWC_HCTSIZ_DATA0 0 +#define DWC_HCTSIZ_DATA1 2 +#define DWC_HCTSIZ_DATA2 1 +#define DWC_HCTSIZ_MDATA 3 +#define DWC_HCTSIZ_SETUP 3 + + /** Do PING protocol when 1 */ + unsigned dopng : 1; + } b; +} hctsiz_data_t; + +/** + * This union represents the bit fields in the Host Channel Interrupt Mask + * Register. Read the register into the d32 member then set/clear the + * bits using the bit elements. Write the d32 member to the + * hcintmsk register. + */ +typedef union hcintmsk_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + unsigned xfercompl : 1; + unsigned chhltd : 1; + unsigned ahberr : 1; + unsigned stall : 1; + unsigned nak : 1; + unsigned ack : 1; + unsigned nyet : 1; + unsigned xacterr : 1; + unsigned bblerr : 1; + unsigned frmovrun : 1; + unsigned datatglerr : 1; + unsigned reserved : 21; + } b; +} hcintmsk_data_t; + +/** OTG Host Interface Structure. + * + * The OTG Host Interface Structure structure contains information + * needed to manage the DWC_otg controller acting in host mode. It + * represents the programming view of the host-specific aspects of the + * controller. + */ +typedef struct dwc_otg_host_if +{ + /** Host Global Registers starting at offset 400h.*/ + dwc_otg_host_global_regs_t *host_global_regs; +#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400 + + /** Host Port 0 Control and Status Register */ + volatile uint32_t *hprt0; +#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440 + + /** Host Channel Specific Registers at offsets 500h-5FCh. */ + dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS]; +#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500 +#define DWC_OTG_CHAN_REGS_OFFSET 0x20 + + + /* Host configuration information */ + /** Number of Host Channels (range: 1-16) */ + uint8_t num_host_channels; + /** Periodic EPs supported (0: no, 1: yes) */ + uint8_t perio_eps_supported; + /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */ + uint16_t perio_tx_fifo_size; +} dwc_otg_host_if_t; + + +/** + * This union represents the bit fields in the Power and Clock Gating Control + * Register. Read the register into the d32 member then set/clear the + * bits using the bit elements. + */ +typedef union pcgcctl_data +{ + /** raw register data */ + uint32_t d32; + + /** register bits */ + struct + { + /** Stop Pclk */ + unsigned stoppclk : 1; + /** Gate Hclk */ + unsigned gatehclk : 1; + /** Power Clamp */ + unsigned pwrclmp : 1; + /** Reset Power Down Modules */ + unsigned rstpdwnmodule : 1; + /** PHY Suspended */ + unsigned physuspended : 1; + unsigned reserved : 27; + } b; +} pcgcctl_data_t; + + +#endif --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -17,7 +17,11 @@ static void urb_destroy(struct kref *kre if (urb->transfer_flags & URB_FREE_BUFFER) kfree(urb->transfer_buffer); - + if (urb->aligned_transfer_buffer) { + kfree(urb->aligned_transfer_buffer); + urb->aligned_transfer_buffer = 0; + urb->aligned_transfer_dma = 0; + } kfree(urb); } --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1200,6 +1200,9 @@ struct urb { unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ void *transfer_buffer; /* (in) associated data buffer */ dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ + void *aligned_transfer_buffer; /* (in) associeated data buffer */ + dma_addr_t aligned_transfer_dma;/* (in) dma addr for transfer_buffer */ + u32 aligned_transfer_buffer_length; /* (in) data buffer length */ struct scatterlist *sg; /* (in) scatter gather buffer list */ int num_sgs; /* (in) number of entries in the sg list */ u32 transfer_buffer_length; /* (in) data buffer length */ --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -111,7 +111,7 @@ config USB_GADGET_SELECTED # choice prompt "USB Peripheral Controller" - depends on USB_GADGET + depends on USB_GADGET && !USB_DWC_OTG help A USB device uses a controller to talk to its host. Systems should have only one such upstream link.