> +/** > + * This function initializes the DWC_otg controller registers and prepares the > + * core for device mode or host mode operation. > + */ > +void dwc_otg_core_init(struct core_if *core_if) > +{ > + u32 i; > + ulong global_reg = core_if->core_global_regs; > + struct device_if *dev_if = core_if->dev_if; > + u32 ahbcfg = 0; > + u32 i2cctl = 0; > + u32 gusbcfg; Tabify the declarations ; > + > + /* Common Initialization */ > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + > + /* Program the ULPI External VBUS bit if needed */ > + gusbcfg |= DWC_USBCFG_ULPI_EXT_VBUS_DRV; > + > + /* Set external TS Dline pulsing */ > + if (core_if->core_params->ts_dline == 1) > + gusbcfg |= DWC_USBCFG_TERM_SEL_DL_PULSE; > + else > + gusbcfg = gusbcfg & (~((u32) DWC_USBCFG_TERM_SEL_DL_PULSE)); > + > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + > + /* Reset the Controller */ > + dwc_otg_core_reset(core_if); > + > + /* Initialize parameters from Hardware configuration registers. */ > + dev_if->num_in_eps = calc_num_in_eps(core_if); > + dev_if->num_out_eps = calc_num_out_eps(core_if); > + > + for (i = 0; i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4); > + i++) { > + dev_if->perio_tx_fifo_size[i] = > + dwc_read32(global_reg + DWC_DPTX_FSIZ_DIPTXF(i)) >> 16; > + } > + for (i = 0; i < DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4); i++) { > + dev_if->tx_fifo_size[i] = > + dwc_read32(global_reg + DWC_DPTX_FSIZ_DIPTXF(i)) >> 16; > + } > + > + core_if->total_fifo_size = DWC_HWCFG3_DFIFO_DEPTH_RD(core_if->hwcfg3); > + core_if->rx_fifo_size = dwc_read32(global_reg + DWC_GRXFSIZ); > + core_if->nperio_tx_fifo_size = > + dwc_read32(global_reg + DWC_GRXFSIZ) >> 16; > + /* > + * This programming sequence needs to happen in FS mode before any > + * other programming occurs > + */ > + if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL && > + core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) { > + /* > + * core_init() is now called on every switch so only call the > + * following for the first time through. > + */ > + if (!core_if->phy_init_done) { > + core_if->phy_init_done = 1; > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL; > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + > + /* Reset after a PHY select */ > + dwc_otg_core_reset(core_if); > + } > + > + /* > + * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. > + * Also do this on HNP Dev/Host mode switches (done in dev_init > + * and host_init). > + */ > + if (dwc_otg_is_host_mode(core_if)) > + init_fslspclksel(core_if); > + else > + init_devspd(core_if); > + > + if (core_if->core_params->i2c_enable) { > + /* Program GUSBCFG.OtgUtmifsSel to I2C */ > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + gusbcfg |= DWC_USBCFG_OTGUTMIFSSEL; > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + > + /* Program GI2CCTL.I2CEn */ > + i2cctl = dwc_read32(global_reg + DWC_GI2CCTL); > + i2cctl |= DWC_I2CCTL_I2CDEVADDR(1); > + i2cctl &= ~DWC_I2CCTL_I2CEN; > + dwc_write32(global_reg + DWC_GI2CCTL, i2cctl); > + i2cctl |= DWC_I2CCTL_I2CEN; > + dwc_write32(global_reg + DWC_GI2CCTL, i2cctl); > + } > + } else if (!core_if->phy_init_done) { > + /* > + * High speed PHY. These parameters are preserved during soft > + * reset so only program them the first time. Do a soft reset > + * immediately after setting phyif. > + */ > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + core_if->phy_init_done = 1; > + if (core_if->core_params->phy_type) > + gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_ULPI_UTMI_SEL); > + > + if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) { > + /* ULPI interface */ > + gusbcfg |= DWC_USBCFG_PHYIF; > + if (core_if->core_params->phy_ulpi_ddr) > + gusbcfg |= DWC_USBCFG_DDRSEL; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_DDRSEL); > + } else { > + /* UTMI+ interface */ > + if (core_if->core_params->phy_utmi_width == 16) > + gusbcfg |= DWC_USBCFG_PHYIF; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_PHYIF); > + } > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + > + /* Reset after setting the PHY parameters */ > + dwc_otg_core_reset(core_if); > + } > + > + if (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 && > + DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 1 && > + core_if->core_params->ulpi_fs_ls) { > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + gusbcfg |= DWC_USBCFG_ULPI_FSLS; > + gusbcfg |= DWC_USBCFG_ULPI_CLK_SUS_M; > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + } else { > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + gusbcfg &= ~((u32) DWC_USBCFG_ULPI_FSLS); > + gusbcfg &= ~((u32) DWC_USBCFG_ULPI_CLK_SUS_M); > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + } > + > + /* Program the GAHBCFG Register. */ > + switch (DWC_HWCFG2_ARCH_RD(core_if->hwcfg2)) { > + case DWC_SLAVE_ONLY_ARCH: > + ahbcfg &= ~DWC_AHBCFG_NPFIFO_EMPTY; /* HALF empty */ > + ahbcfg &= ~DWC_AHBCFG_FIFO_EMPTY; /* HALF empty */ > + core_if->dma_enable = 0; > + break; > + case DWC_EXT_DMA_ARCH: > + ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) | > + DWC_AHBCFG_BURST_LEN(core_if->core_params->dma_burst_size); > + core_if->dma_enable = (core_if->core_params->dma_enable != 0); > + break; > + case DWC_INT_DMA_ARCH: > + ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) | > + DWC_AHBCFG_BURST_LEN(DWC_GAHBCFG_INT_DMA_BURST_INCR); > + core_if->dma_enable = (core_if->core_params->dma_enable != 0); > + break; > + } > + > + if (core_if->dma_enable) > + ahbcfg |= DWC_AHBCFG_DMA_ENA; > + else > + ahbcfg &= ~DWC_AHBCFG_DMA_ENA; > + dwc_write32(global_reg + DWC_GAHBCFG, ahbcfg); > + core_if->en_multiple_tx_fifo = > + DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4); > + > + /* Program the GUSBCFG register. */ > + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG); > + switch (DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2)) { > + case DWC_MODE_HNP_SRP_CAPABLE: > + if (core_if->core_params->otg_cap == > + DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) > + gusbcfg |= DWC_USBCFG_HNP_CAP; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + if (core_if->core_params->otg_cap != > + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE) > + gusbcfg |= DWC_USBCFG_SRP_CAP; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + case DWC_MODE_SRP_ONLY_CAPABLE: > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + if (core_if->core_params->otg_cap != > + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE) > + gusbcfg |= DWC_USBCFG_SRP_CAP; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + case DWC_MODE_NO_HNP_SRP_CAPABLE: > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + case DWC_MODE_SRP_CAPABLE_DEVICE: > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + if (core_if->core_params->otg_cap != > + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE) > + gusbcfg |= DWC_USBCFG_SRP_CAP; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + case DWC_MODE_NO_SRP_CAPABLE_DEVICE: > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + case DWC_MODE_SRP_CAPABLE_HOST: > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + if (core_if->core_params->otg_cap != > + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE) > + gusbcfg |= DWC_USBCFG_SRP_CAP; > + else > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + case DWC_MODE_NO_SRP_CAPABLE_HOST: > + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP); > + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP); > + break; > + } > + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg); > + > + /* Enable common interrupts */ > + dwc_otg_enable_common_interrupts(core_if); > + > + /* > + * Do device or host intialization based on mode during PCD > + * and HCD initialization > + */ > + if (dwc_otg_is_host_mode(core_if)) { > + core_if->xceiv->state = OTG_STATE_A_HOST; > + } else { > + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL; > + if (dwc_has_feature(core_if, DWC_DEVICE_ONLY)) > + dwc_otg_core_dev_init(core_if); > + } > +} > + > +/** > + * This function enables the Device mode interrupts. > + * > + * Note that the bits in the Device IN endpoint mask register are laid out > + * exactly the same as the Device IN endpoint interrupt register. > + */ > +static void dwc_otg_enable_device_interrupts(struct core_if *core_if) > +{ > + u32 intr_mask = 0; > + u32 msk = 0; > + ulong global_regs = core_if->core_global_regs; > + > + /* Disable all interrupts. */ > + dwc_write32(global_regs + DWC_GINTMSK, 0); > + > + /* Clear any pending interrupts */ > + dwc_write32(global_regs + DWC_GINTSTS, 0xFFFFFFFF); > + > + /* Enable the common interrupts */ > + dwc_otg_enable_common_interrupts(core_if); > + > + /* Enable interrupts */ > + intr_mask |= DWC_INTMSK_USB_RST; > + intr_mask |= DWC_INTMSK_ENUM_DONE; > + intr_mask |= DWC_INTMSK_IN_ENDP; > + intr_mask |= DWC_INTMSK_OUT_ENDP; > + intr_mask |= DWC_INTMSK_EARLY_SUSP; > + if (!core_if->en_multiple_tx_fifo) > + intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH; > + > + /* Periodic EP */ > + intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP; > + intr_mask |= DWC_INTMSK_END_OF_PFRM; > + intr_mask |= DWC_INTMSK_INCMP_IN_ATX; > + intr_mask |= DWC_INTMSK_INCMP_OUT_PTX; > + > + dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask); > + > + msk = DWC_DIEPMSK_TXFIFO_UNDERN_RW(msk, 1); > + dwc_modify32(core_if->dev_if->dev_global_regs + DWC_DIEPMSK, > + msk, msk); > +} > + > +/** > + * Configures the device data fifo sizes when dynamic sizing is enabled. > + */ > +static void config_dev_dynamic_fifos(struct core_if *core_if) > +{ > + u32 i; > + ulong regs = core_if->core_global_regs; > + struct core_params *params = core_if->core_params; > + u32 txsize = 0; > + u32 nptxsize = 0; > + u32 ptxsize = 0; Tabify the declarations ; > + > + /* Rx FIFO */ > + dwc_write32(regs + DWC_GRXFSIZ, params->dev_rx_fifo_size); > + > + /* Set Periodic and Non-periodic Tx FIFO Mask bits to all 0 */ > + core_if->p_tx_msk = 0; > + core_if->tx_msk = 0; > + > + if (core_if->en_multiple_tx_fifo == 0) { > + /* Non-periodic Tx FIFO */ > + nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize, > + params-> > + dev_nperio_tx_fifo_size); > + nptxsize = > + DWC_RX_FIFO_START_ADDR_WR(nptxsize, > + params->dev_rx_fifo_size); > + dwc_write32(regs + DWC_GNPTXFSIZ, nptxsize); > + > + /* > + * Periodic Tx FIFOs These FIFOs are numbered from 1 to > + * 15. Indexes of the FIFO size module parameters in the > + * dev_perio_tx_fifo_size array and the FIFO size > + * registers in the dptxfsiz array run from 0 to 14. > + */ > + ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize, > + (DWC_RX_FIFO_START_ADDR_RD > + (nptxsize) + > + DWC_RX_FIFO_DEPTH_RD > + (nptxsize))); > + for (i = 0; > + i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4); > + i++) { > + ptxsize = > + DWC_RX_FIFO_DEPTH_WR(ptxsize, > + params-> > + dev_perio_tx_fifo_size[i]); > + dwc_write32(regs + DWC_DPTX_FSIZ_DIPTXF(i), ptxsize); > + ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize, > + (DWC_RX_FIFO_START_ADDR_RD > + (ptxsize) + > + DWC_RX_FIFO_DEPTH_RD > + (ptxsize))); > + } > + } else { > + /* > + * Non-periodic Tx FIFOs These FIFOs are numbered from > + * 1 to 15. Indexes of the FIFO size module parameters > + * in the dev_tx_fifo_size array and the FIFO size > + * registers in the dptxfsiz_dieptxf array run from 0 to > + * 14. > + */ > + nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize, > + params-> > + dev_nperio_tx_fifo_size); > + nptxsize = > + DWC_RX_FIFO_START_ADDR_WR(nptxsize, > + params->dev_rx_fifo_size); > + dwc_write32(regs + DWC_GNPTXFSIZ, nptxsize); > + > + txsize = DWC_RX_FIFO_START_ADDR_WR(txsize, > + (DWC_RX_FIFO_START_ADDR_RD > + (nptxsize) + > + DWC_RX_FIFO_DEPTH_RD > + (nptxsize))); > + for (i = 1; > + i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4); > + i++) { > + txsize = > + DWC_RX_FIFO_DEPTH_WR(txsize, > + params->dev_tx_fifo_size[i]); > + dwc_write32(regs + DWC_DPTX_FSIZ_DIPTXF(i - 1), txsize); > + txsize = DWC_RX_FIFO_START_ADDR_WR(txsize, > + (DWC_RX_FIFO_START_ADDR_RD > + (txsize) + > + DWC_RX_FIFO_DEPTH_RD > + (txsize))); > + } > + } > +} > + > +/** > + * This function initializes the DWC_otg controller registers for > + * device mode. > + */ > +void dwc_otg_core_dev_init(struct core_if *c_if) > +{ > + u32 i; > + struct device_if *d_if = c_if->dev_if; > + struct core_params *params = c_if->core_params; > + u32 dcfg = 0; > + u32 resetctl = 0; > + u32 dthrctl = 0; Tabify the declarations ; > + > + /* Restart the Phy Clock */ > + dwc_write32(c_if->pcgcctl, 0); > + > + /* Device configuration register */ > + init_devspd(c_if); > + dcfg = dwc_read32(d_if->dev_global_regs + DWC_DCFG); > + dcfg = DWC_DCFG_P_FRM_INTRVL_WR(dcfg, DWC_DCFG_FRAME_INTERVAL_80); > + dwc_write32(d_if->dev_global_regs + DWC_DCFG, dcfg); > + > + /* If needed configure data FIFO sizes */ > + if (DWC_HWCFG2_DYN_FIFO_RD(c_if->hwcfg2) && params->enable_dynamic_fifo) > + config_dev_dynamic_fifos(c_if); > + > + /* Flush the FIFOs */ > + dwc_otg_flush_tx_fifo(c_if, DWC_GRSTCTL_TXFNUM_ALL); > + dwc_otg_flush_rx_fifo(c_if); > + > + /* Flush the Learning Queue. */ > + resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH; > + dwc_write32(c_if->core_global_regs + DWC_GRSTCTL, resetctl); > + > + /* Clear all pending Device Interrupts */ > + dwc_write32(d_if->dev_global_regs + DWC_DIEPMSK, 0); > + dwc_write32(d_if->dev_global_regs + DWC_DOEPMSK, 0); > + dwc_write32(d_if->dev_global_regs + DWC_DAINT, 0xFFFFFFFF); > + dwc_write32(d_if->dev_global_regs + DWC_DAINTMSK, 0); > + > + for (i = 0; i <= d_if->num_in_eps; i++) { > + u32 depctl = 0; > + > + depctl = dwc_read32(d_if->in_ep_regs[i] + DWC_DIEPCTL); > + if (DWC_DEPCTL_EPENA_RD(depctl)) { > + depctl = 0; > + depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1); > + depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1); > + } else { > + depctl = 0; > + } > + > + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPCTL, depctl); > + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPTSIZ, 0); > + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPDMA, 0); > + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPINT, 0xFF); > + } > + > + for (i = 0; i <= d_if->num_out_eps; i++) { > + u32 depctl = 0; > + depctl = dwc_read32(d_if->out_ep_regs[i] + DWC_DOEPCTL); > + if (DWC_DEPCTL_EPENA_RD(depctl)) { > + depctl = 0; > + depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1); > + depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1); > + } else { > + depctl = 0; > + } > + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPCTL, depctl); > + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPTSIZ, 0); > + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPDMA, 0); > + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPINT, 0xFF); > + } > + > + if (c_if->en_multiple_tx_fifo && c_if->dma_enable) { > + d_if->non_iso_tx_thr_en = c_if->core_params->thr_ctl & 0x1; > + d_if->iso_tx_thr_en = (c_if->core_params->thr_ctl >> 1) & 0x1; > + d_if->rx_thr_en = (c_if->core_params->thr_ctl >> 2) & 0x1; > + d_if->rx_thr_length = c_if->core_params->rx_thr_length; > + d_if->tx_thr_length = c_if->core_params->tx_thr_length; > + > + dthrctl = 0; > + dthrctl = DWC_DTHCTRL_NON_ISO_THR_ENA_RW > + (dthrctl, d_if->non_iso_tx_thr_en); > + dthrctl = DWC_DTHCTRL_ISO_THR_EN_RW > + (dthrctl, d_if->iso_tx_thr_en); > + dthrctl = DWC_DTHCTRL_TX_THR_LEN_RW > + (dthrctl, d_if->tx_thr_length); > + dthrctl = DWC_DTHCTRL_RX_THR_EN_RW(dthrctl, d_if->rx_thr_en); > + dthrctl = DWC_DTHCTRL_RX_THR_LEN_RW > + (dthrctl, d_if->rx_thr_length); > + dwc_write32(d_if->dev_global_regs + > + DWC_DTKNQR3_DTHRCTL, dthrctl); > + > + } > + > + dwc_otg_enable_device_interrupts(c_if); > +} > + > +/** > + * This function reads a packet from the Rx FIFO into the destination buffer. > + * To read SETUP data use dwc_otg_read_setup_packet. > + */ > +void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16 _bytes) > +{ > + u32 i; > + int word_count = (_bytes + 3) / 4; > + u32 fifo = core_if->data_fifo[0]; > + u32 *data_buff = (u32 *) dest; > + > + /* > + * This requires reading data from the FIFO into a u32 temp buffer, > + * then moving it into the data buffer. > + */ > + for (i = 0; i < word_count; i++, data_buff++) > + *data_buff = dwc_read_fifo32(fifo); > +} > + > +/** > + * Flush a Tx FIFO. > + */ > +void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int num) > +{ > + ulong global_regs = core_if->core_global_regs; > + u32 greset = 0; > + int count = 0; > + > + greset |= DWC_RSTCTL_TX_FIFO_FLUSH; > + greset = DWC_RSTCTL_TX_FIFO_NUM(greset, num); > + dwc_write32(global_regs + DWC_GRSTCTL, greset); > + > + do { > + greset = dwc_read32(global_regs + DWC_GRSTCTL); > + if (++count > 10000) { > + pr_warning("%s() HANG! GRSTCTL=%0x " > + "GNPTXSTS=0x%08x\n", __func__, greset, > + dwc_read32(global_regs + DWC_GNPTXSTS)); > + break; > + } > + udelay(1); > + } while (greset & DWC_RSTCTL_TX_FIFO_FLUSH); > + > + /* Wait for 3 PHY Clocks */ > + udelay(1); > +} > + > +/** > + * Flush Rx FIFO. > + */ > +void dwc_otg_flush_rx_fifo(struct core_if *core_if) > +{ > + ulong global_regs = core_if->core_global_regs; > + u32 greset = 0; > + int count = 0; > + > + greset |= DWC_RSTCTL_RX_FIFO_FLUSH; > + dwc_write32(global_regs + DWC_GRSTCTL, greset); > + > + do { > + greset = dwc_read32(global_regs + DWC_GRSTCTL); > + if (++count > 10000) { > + pr_warning("%s() HANG! GRSTCTL=%0x\n", > + __func__, greset); > + break; > + } > + udelay(1); > + } while (greset & DWC_RSTCTL_RX_FIFO_FLUSH); > + > + /* Wait for 3 PHY Clocks */ > + udelay(1); > +} > + > +/** > + * Register HCD callbacks. > + * The callbacks are used to start and stop the HCD for interrupt processing. > + */ > +void __devinit dwc_otg_cil_register_hcd_callbacks(struct core_if *c_if, > + struct cil_callbacks *cb, > + void *p) > +{ > + c_if->hcd_cb = cb; > + cb->p = p; > +} > + > +/** > + * Register PCD callbacks. > + * The callbacks are used to start and stop the PCD for interrupt processing. > + */ > +void __devinit dwc_otg_cil_register_pcd_callbacks(struct core_if *c_if, > + struct cil_callbacks *cb, > + void *p) > +{ > + c_if->pcd_cb = cb; > + cb->p = p; > +} > + > +/** > + * This function is called to initialize the DWC_otg CSR data structures. > + * > + * The register addresses in the device and host structures are initialized from > + * the base address supplied by the caller. The calling function must make the > + * OS calls to get the base address of the DWC_otg controller registers. > + * > + * The params argument holds the parameters that specify how the core should be > + * configured. > + */ > +struct core_if __devinit *dwc_otg_cil_init(const __iomem u32 * base, > + struct core_params *params) > +{ > + struct core_if *core_if; > + struct device_if *dev_if; > + struct dwc_host_if *host_if; > + u8 *reg_base = (__force u8 *)base; > + u32 offset; > + u32 i; Tabify the declarations ; > + > + core_if = kzalloc(sizeof(*core_if), GFP_KERNEL); > + if (!core_if) > + return NULL; > + > + core_if->core_params = params; > + core_if->core_global_regs = (ulong)reg_base; > + > + /* Allocate the Device Mode structures. */ > + dev_if = kmalloc(sizeof(*dev_if), GFP_KERNEL); > + if (!dev_if) { > + kfree(core_if); > + return NULL; > + } > + > + dev_if->dev_global_regs = (ulong)(reg_base + DWC_DEV_GLOBAL_REG_OFFSET); > + > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + offset = i * DWC_EP_REG_OFFSET; > + > + dev_if->in_ep_regs[i] = (ulong)(reg_base + > + DWC_DEV_IN_EP_REG_OFFSET + > + offset); > + > + dev_if->out_ep_regs[i] = (ulong)(reg_base + > + DWC_DEV_OUT_EP_REG_OFFSET + > + offset); > + } > + > + dev_if->speed = 0; /* unknown */ > + core_if->dev_if = dev_if; > + > + /* Allocate the Host Mode structures. */ > + host_if = kmalloc(sizeof(*host_if), GFP_KERNEL); > + if (!host_if) { > + kfree(dev_if); > + kfree(core_if); > + return NULL; > + } > + > + host_if->host_global_regs = (ulong)(reg_base + > + DWC_OTG_HOST_GLOBAL_REG_OFFSET); > + > + host_if->hprt0 = (ulong)(reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET); > + > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + offset = i * DWC_OTG_CHAN_REGS_OFFSET; > + > + host_if->hc_regs[i] = (ulong)(reg_base + > + DWC_OTG_HOST_CHAN_REGS_OFFSET + > + offset); > + } > + > + host_if->num_host_channels = MAX_EPS_CHANNELS; > + core_if->host_if = host_if; > + for (i = 0; i < MAX_EPS_CHANNELS; i++) { > + core_if->data_fifo[i] = > + (ulong)(reg_base + DWC_OTG_DATA_FIFO_OFFSET + > + (i * DWC_OTG_DATA_FIFO_SIZE)); > + } > + core_if->pcgcctl = (ulong)(reg_base + DWC_OTG_PCGCCTL_OFFSET); > + > + /* > + * Store the contents of the hardware configuration registers here for > + * easy access later. > + */ > + core_if->hwcfg1 = > + dwc_read32(core_if->core_global_regs + DWC_GHWCFG1); > + core_if->hwcfg2 = > + dwc_read32(core_if->core_global_regs + DWC_GHWCFG2); > + core_if->hwcfg3 = > + dwc_read32(core_if->core_global_regs + DWC_GHWCFG3); > + core_if->hwcfg4 = > + dwc_read32(core_if->core_global_regs + DWC_GHWCFG4); > + > + /* Set the SRP sucess bit for FS-I2c */ > + core_if->srp_success = 0; > + core_if->srp_timer_started = 0; > + return core_if; > +} > + > +/** > + * This function frees the structures allocated by dwc_otg_cil_init(). > + */ > +void dwc_otg_cil_remove(struct core_if *core_if) > +{ > + /* Disable all interrupts */ > + dwc_modify32(core_if->core_global_regs + DWC_GAHBCFG, 1, 0); > + dwc_write32(core_if->core_global_regs + DWC_GINTMSK, 0); > + > + if (core_if) { > + kfree(core_if->dev_if); > + kfree(core_if->host_if); > + } > + kfree(core_if); > +} > diff --git a/drivers/usb/otg/dwc/cil.h b/drivers/usb/otg/dwc/cil.h > new file mode 100644 > index 0000000..80b7da5 > --- /dev/null > +++ b/drivers/usb/otg/dwc/cil.h > @@ -0,0 +1,1177 @@ > +/* > + * DesignWare HS OTG controller driver > + * Copyright (C) 2006 Synopsys, Inc. > + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation. > + * > + * This program is free software: you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License version 2 for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see http://www.gnu.org/licenses > + * or write to the Free Software Foundation, Inc., 51 Franklin Street, > + * Suite 500, Boston, MA 02110-1335 USA. > + * > + * Based on Synopsys driver version 2.60a > + * Modified by Mark Miesfeld <mmiesfeld@xxxxxxx> > + * Modified by Stefan Roese <sr@xxxxxxx>, DENX Software Engineering > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. 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_CIL_H__) > +#define __DWC_CIL_H__ > +#include <linux/io.h> > +#include <linux/usb/ch9.h> > +#include <linux/usb/gadget.h> > +#include <linux/interrupt.h> > +#include <linux/dmapool.h> > +#include <linux/spinlock.h> > +#include <linux/usb/otg.h> > + > +#include "regs.h" > + > +#ifdef CONFIG_DWC_DEBUG > +#define DEBUG > +#endif > + > +/** > + * Reads the content of a register. > + */ > +static inline u32 dwc_read32(ulong reg) > +{ > +#ifdef CONFIG_DWC_OTG_REG_LE > + return in_le32((unsigned __iomem *)reg); > +#else > + return in_be32((unsigned __iomem *)reg); > +#endif > +}; > +/** > + * Writes a register with a 32 bit value. > + */ > +static inline void dwc_write32(ulong reg, const u32 value) > +{ > +#ifdef CONFIG_DWC_OTG_REG_LE > + out_le32((unsigned __iomem *)reg, value); > +#else > + out_be32((unsigned __iomem *)reg, value); > +#endif > +}; > + > +/** > + * This function modifies bit values in a register. Using the > + * algorithm: (reg_contents & ~clear_mask) | set_mask. > + */ > +static inline > + void dwc_modify32(ulong reg, const u32 _clear_mask, const u32 _set_mask) > +{ > +#ifdef CONFIG_DWC_OTG_REG_LE > + out_le32((unsigned __iomem *)reg, > + (in_le32((unsigned __iomem *)reg) & ~_clear_mask) | > + _set_mask); > +#else > + out_be32((unsigned __iomem *)reg, > + (in_be32(((unsigned __iomem *))reg) & ~_clear_mask) | > + _set_mask); > +#endif > +}; > + > +static inline void dwc_write_fifo32(ulong reg, const u32 _value) > +{ > +#ifdef CONFIG_DWC_OTG_FIFO_LE > + out_le32((unsigned __iomem *)reg, _value); > +#else > + out_be32((unsigned __iomem *)reg, _value); > +#endif > +}; > + > +static inline u32 dwc_read_fifo32(ulong _reg) > +{ > +#ifdef CONFIG_DWC_OTG_FIFO_LE > + return in_le32((unsigned __iomem *) _reg); > +#else > + return in_be32((unsigned __iomem *) _reg); > +#endif > +}; > + > +/* > + * Debugging support vanishes in non-debug builds. > + */ > +/* Display CIL Debug messages */ > +#define dwc_dbg_cil (0x2) > + > +/* Display CIL Verbose debug messages */ > +#define dwc_dbg_cilv (0x20) > + > +/* Display PCD (Device) debug messages */ > +#define dwc_dbg_pcd (0x4) > + > +/* Display PCD (Device) Verbose debug messages */ > +#define dwc_dbg_pcdv (0x40) > + > +/* Display Host debug messages */ > +#define dwc_dbg_hcd (0x8) > + > +/* Display Verbose Host debug messages */ > +#define dwc_dbg_hcdv (0x80) > + > +/* Display enqueued URBs in host mode. */ > +#define dwc_dbg_hcd_urb (0x800) > + > +/* Display "special purpose" debug messages */ > +#define dwc_dbg_sp (0x400) > + > +/* Display all debug messages */ > +#define dwc_dbg_any (0xFF) > + > +/* All debug messages off */ > +#define dwc_dbg_off 0 > + > +/* Prefix string for DWC_DEBUG print macros. */ > +#define usb_dwc "dwc_otg: " > + > +/* > + * This file contains the interface to the Core Interface Layer. > + */ > + > +/* > + * Added-sr: 2007-07-26 > + * > + * Since the 405EZ (Ultra) only support 2047 bytes as > + * max transfer size, we have to split up bigger transfers > + * into multiple transfers of 1024 bytes sized messages. > + * I happens often, that transfers of 4096 bytes are > + * required (zero-gadget, file_storage-gadget). > + * > + * MAX_XFER_LEN is set to 1024 right now, but could be 2047, > + * since the xfer-size field in the 405EZ USB device controller > + * implementation has 11 bits. Using 1024 seems to work for now. > + */ > +#define MAX_XFER_LEN 1024 > + > +/* > + * The dwc_ep structure represents the state of a single endpoint when acting in > + * device mode. It contains the data items needed for an endpoint to be > + * activated and transfer packets. > + */ > +struct dwc_ep { > + /* EP number used for register address lookup */ > + u8 num; > + /* EP direction 0 = OUT */ > + 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; > + > + ulong dma_addr; > + > + /* > + * Pointer to the beginning of the transfer buffer -- do not modify > + * during transfer. > + */ > + u8 *start_xfer_buff; > + /* pointer to the transfer buffer */ > + u8 *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; > + > + /* > + * Added-sr: 2007-07-26 > + * > + * Since the 405EZ (Ultra) only support 2047 bytes as > + * max transfer size, we have to split up bigger transfers > + * into multiple transfers of 1024 bytes sized messages. > + * I happens often, that transfers of 4096 bytes are > + * required (zero-gadget, file_storage-gadget). > + * > + * "bytes_pending" will hold the amount of bytes that are > + * still pending to be send in further messages to complete > + * the bigger transfer. > + */ > + u32 bytes_pending; > +}; > + > +/* > + * States of EP0. > + */ > +enum ep0_state { > + EP0_DISCONNECT = 0, /* no host */ > + EP0_IDLE = 1, > + EP0_IN_DATA_PHASE = 2, > + EP0_OUT_DATA_PHASE = 3, > + EP0_STATUS = 4, > + EP0_STALL = 5, > +}; > + > +/* Fordward declaration.*/ > +struct dwc_pcd; > + > +/* > + * This structure describes an EP, there is an array of EPs in the PCD > + * structure. > + */ > +struct pcd_ep { > + /* USB EP data */ > + struct usb_ep ep; > + /* USB EP Descriptor */ > + const struct usb_endpoint_descriptor *desc; > + > + /* queue of dwc_otg_pcd_requests. */ > + struct list_head queue; > + unsigned stopped:1; > + unsigned disabling:1; > + unsigned dma:1; > + unsigned queue_sof:1; > + unsigned wedged:1; > + > + /* DWC_otg ep data. */ > + struct dwc_ep dwc_ep; > + > + /* Pointer to PCD */ > + struct dwc_pcd *pcd; > +}; > + > +/* > + * DWC_otg PCD Structure. > + * This structure encapsulates the data for the dwc_otg PCD. > + */ > +struct dwc_pcd { > + /* USB gadget */ > + struct usb_gadget gadget; > + /* USB gadget driver pointer */ > + struct usb_gadget_driver *driver; > + /* The DWC otg device pointer. */ > + struct dwc_otg_device *otg_dev; > + > + /* State of EP0 */ > + enum ep0_state ep0state; > + /* EP0 Request is pending */ > + unsigned ep0_pending:1; > + /* Indicates when SET CONFIGURATION Request is in process */ > + unsigned request_config:1; > + /* The state of the Remote Wakeup Enable. */ > + unsigned remote_wakeup_enable:1; > + /* The state of the B-Device HNP Enable. */ > + unsigned b_hnp_enable:1; > + /* The state of A-Device HNP Support. */ > + unsigned a_hnp_support:1; > + /* The state of the A-Device Alt HNP support. */ > + unsigned a_alt_hnp_support:1; > + /* Count of pending Requests */ > + unsigned request_pending; > + > + /* > + * SETUP packet for EP0. This structure is allocated as a DMA buffer on > + * PCD initialization with enough space for up to 3 setup packets. > + */ > + union { > + struct usb_ctrlrequest req; > + u32 d32[2]; > + } *setup_pkt; > + > + struct dma_pool *dwc_pool; > + dma_addr_t setup_pkt_dma_handle; > + > + /* 2-byte dma buffer used to return status from GET_STATUS */ > + u16 *status_buf; > + dma_addr_t status_buf_dma_handle; > + > + /* Array of EPs. */ > + struct pcd_ep ep0; > + /* Array of IN EPs. */ > + struct pcd_ep in_ep[MAX_EPS_CHANNELS - 1]; > + /* Array of OUT EPs. */ > + struct pcd_ep out_ep[MAX_EPS_CHANNELS - 1]; > + spinlock_t lock; > + /* > + * Timer for SRP. If it expires before SRP is successful clear the > + * SRP. > + */ > + struct timer_list srp_timer; > + > + /* > + * Tasklet to defer starting of TEST mode transmissions until Status > + * Phase has been completed. > + */ > + struct tasklet_struct test_mode_tasklet; > + > + /* Tasklet to delay starting of xfer in DMA mode */ > + struct tasklet_struct *start_xfer_tasklet; > + > + /* The test mode to enter when the tasklet is executed. */ > + unsigned test_mode; > +}; > + > +/* > + * This structure holds the state of the HCD, including the non-periodic and > + * periodic schedules. > + */ > +struct dwc_hcd { > + spinlock_t lock; > + > + /* DWC OTG Core Interface Layer */ > + struct core_if *core_if; > + > + /* Internal DWC HCD Flags */ > + union dwc_otg_hcd_internal_flags { > + u32 d32; > + struct { > + unsigned port_connect_status_change:1; > + unsigned port_connect_status:1; > + unsigned port_reset_change:1; > + unsigned port_enable_change:1; > + unsigned port_suspend_change:1; > + unsigned port_over_current_change:1; > + unsigned reserved:27; > + } b; > + } flags; > + > + /* > + * Inactive items in the non-periodic schedule. This is a list of > + * Queue Heads. Transfers associated with these Queue Heads are not > + * currently assigned to a host channel. > + */ > + struct list_head non_periodic_sched_inactive; > + > + /* > + * Deferred items in the non-periodic schedule. This is a list of > + * Queue Heads. Transfers associated with these Queue Heads are not > + * currently assigned to a host channel. > + * When we get an NAK, the QH goes here. > + */ > + struct list_head non_periodic_sched_deferred; > + > + /* > + * Active items in the non-periodic schedule. This is a list of > + * Queue Heads. Transfers associated with these Queue Heads are > + * currently assigned to a host channel. > + */ > + struct list_head non_periodic_sched_active; > + > + /* > + * Pointer to the next Queue Head to process in the active > + * non-periodic schedule. > + */ > + struct list_head *non_periodic_qh_ptr; > + > + /* > + * Inactive items in the periodic schedule. This is a list of QHs for > + * periodic transfers that are _not_ scheduled for the next frame. > + * Each QH in the list has an interval counter that determines when it > + * needs to be scheduled for execution. This scheduling mechanism > + * allows only a simple calculation for periodic bandwidth used (i.e. > + * must assume that all periodic transfers may need to execute in the > + * same frame). However, it greatly simplifies scheduling and should > + * be sufficient for the vast majority of OTG hosts, which need to > + * connect to a small number of peripherals at one time. > + * > + * Items move from this list to periodic_sched_ready when the QH > + * interval counter is 0 at SOF. > + */ > + struct list_head periodic_sched_inactive; > + > + /* > + * List of periodic QHs that are ready for execution in the next > + * frame, but have not yet been assigned to host channels. > + * > + * Items move from this list to periodic_sched_assigned as host > + * channels become available during the current frame. > + */ > + struct list_head periodic_sched_ready; > + > + /* > + * List of periodic QHs to be executed in the next frame that are > + * assigned to host channels. > + * > + * Items move from this list to periodic_sched_queued as the > + * transactions for the QH are queued to the DWC_otg controller. > + */ > + struct list_head periodic_sched_assigned; > + > + /* > + * List of periodic QHs that have been queued for execution. > + * > + * Items move from this list to either periodic_sched_inactive or > + * periodic_sched_ready when the channel associated with the transfer > + * is released. If the interval for the QH is 1, the item moves to > + * periodic_sched_ready because it must be rescheduled for the next > + * frame. Otherwise, the item moves to periodic_sched_inactive. > + */ > + struct list_head periodic_sched_queued; > + > + /* > + * Total bandwidth claimed so far for periodic transfers. This value > + * is in microseconds per (micro)frame. The assumption is that all > + * periodic transfers may occur in the same (micro)frame. > + */ > + u16 periodic_usecs; > + > + /* > + * Total bandwidth claimed so far for all periodic transfers > + * in a frame. > + * This will include a mixture of HS and FS transfers. > + * Units are microseconds per (micro)frame. > + * We have a budget per frame and have to schedule > + * transactions accordingly. > + * Watch out for the fact that things are actually scheduled for the > + * "next frame". > + */ > + u16 frame_usecs[8]; > + > + /* > + * Frame number read from the core at SOF. The value ranges from 0 to > + * DWC_HFNUM_MAX_FRNUM. > + */ > + u16 frame_number; > + > + /* > + * Free host channels in the controller. This is a list of > + * struct dwc_hc items. > + */ > + struct list_head free_hc_list; > + > + /* > + * Number of available host channels. > + */ > + u32 available_host_channels; > + > + /* > + * Array of pointers to the host channel descriptors. Allows accessing > + * a host channel descriptor given the host channel number. This is > + * useful in interrupt handlers. > + */ > + struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS]; > + > + /* > + * Buffer to use for any data received during the status phase of a > + * control transfer. Normally no data is transferred during the status > + * phase. This buffer is used as a bit bucket. > + */ > + u8 *status_buf; > + > + /* > + * DMA address for status_buf. > + */ > + dma_addr_t status_buf_dma; > +#define DWC_OTG_HCD_STATUS_BUF_SIZE 64 > + > + /* > + * Structure to allow starting the HCD in a non-interrupt context > + * during an OTG role change. > + */ > + struct work_struct start_work; > + struct usb_hcd *_p; > + > + /* > + * Connection timer. An OTG host must display a message if the device > + * does not connect. Started when the VBus power is turned on via > + * sysfs attribute "buspower". > + */ > + struct timer_list conn_timer; > + > + /* workqueue for port wakeup */ > + struct work_struct usb_port_reset; > + > + /* Addition HCD interrupt */ > + int cp_irq; /* charge pump interrupt */ > + int cp_irq_installed; > +}; > + > +/* > + * Reasons for halting a host channel. > + */ > +enum dwc_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 > +}; > + > +/* > + * 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. > + */ > +struct dwc_hc { > + /* Host channel number used for register address lookup */ > + u8 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, > + * 1: DATA2, > + * 2: DATA1, > + * 3: MDATA (non-Control EP), > + * SETUP (Control EP) > + */ > + unsigned data_pid_start:2; > +#define DWC_OTG_HC_PID_DATA0 0 > +#define DWC_OTG_HC_PID_DATA2 1 > +#define DWC_OTG_HC_PID_DATA1 2 > +#define DWC_OTG_HC_PID_MDATA 3 > +#define DWC_OTG_HC_PID_SETUP 3 > + > + /* Number of periodic transactions per (micro)frame */ > + unsigned multi_count:2; > + > + /* Pointer to the current transfer buffer position. */ > + u8 *xfer_buff; > + /* Total number of bytes to transfer. */ > + u32 xfer_len; > + /* Number of bytes transferred so far. */ > + u32 xfer_count; > + /* Packet count at start of transfer. */ > + u16 start_pkt_count; > + > + /* > + * Flag to indicate whether the transfer has been started. Set to 1 if > + * it has been started, 0 otherwise. > + */ > + u8 xfer_started; > + > + /* > + * Set to 1 to indicate that a PING request should be issued on this > + * channel. If 0, process normally. > + */ > + u8 do_ping; > + > + /* > + * Set to 1 to indicate that the error count for this transaction is > + * non-zero. Set to 0 if the error count is 0. > + */ > + u8 error_state; > + > + /* > + * Set to 1 to indicate that this channel should be halted the next > + * time a request is queued for the channel. This is necessary in > + * slave mode if no request queue space is available when an attempt > + * is made to halt the channel. > + */ > + u8 halt_on_queue; > + > + /* > + * Set to 1 if the host channel has been halted, but the core is not > + * finished flushing queued requests. Otherwise 0. > + */ > + u8 halt_pending; > + > + /* Reason for halting the host channel. */ > + enum dwc_halt_status halt_status; > + > + /* Split settings for the host channel */ > + u8 do_split; /* Enable split for the channel */ > + u8 complete_split; /* Enable complete split */ > + u8 hub_addr; /* Address of high speed hub */ > + u8 port_addr; /* Port of the low/full speed device */ > + > + /* > + * Split transaction position. One of the following values: > + * - DWC_HCSPLIT_XACTPOS_MID > + * - DWC_HCSPLIT_XACTPOS_BEGIN > + * - DWC_HCSPLIT_XACTPOS_END > + * - DWC_HCSPLIT_XACTPOS_ALL */ > + u8 xact_pos; > + > + /* Set when the host channel does a short read. */ > + u8 short_read; > + > + /* > + * Number of requests issued for this channel since it was assigned to > + * the current transfer (not counting PINGs). > + */ > + u8 requests; > + > + /* Queue Head for the transfer being processed by this channel. */ > + struct dwc_qh *qh; > + > + /* Entry in list of host channels. */ > + struct list_head hc_list_entry; > +}; > + > +/* > + * The following parameters may be specified when starting the module. These > + * parameters define how the DWC_otg controller should be configured. Parameter > + * values are passed to the CIL initialization function dwc_otg_cil_init. > + */ > +struct core_params { > + /* > + * Specifies the OTG capabilities. The driver will automatically > + * detect the value for this parameter if none is specified. > + * 0 - HNP and SRP capable (default) > + * 1 - SRP Only capable > + * 2 - No HNP/SRP capable > + */ > + int otg_cap; > +#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0 > +#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1 > +#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 > + > +#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE > + > + /* > + * Specifies whether to use slave or DMA mode for accessing the data > + * FIFOs. The driver will automatically detect the value for this > + * parameter if none is specified. > + * 0 - Slave > + * 1 - DMA (default, if available) > + */ > + int dma_enable; > +#ifdef CONFIG_DWC_SLAVE > +#define dwc_param_dma_enable_default 0 > +#else > +#define dwc_param_dma_enable_default 1 > +#endif > + > + /* > + * The DMA Burst size (applicable only for External DMA Mode). > + * 1, 4, 8 16, 32, 64, 128, 256 (default 32) > + */ > + int dma_burst_size; /* Translate this to GAHBCFG values */ > +#define dwc_param_dma_burst_size_default 32 > + > + /* > + * Specifies the maximum speed of operation in host and device mode. > + * The actual speed depends on the speed of the attached device and > + * the value of phy_type. The actual speed depends on the speed of the > + * attached device. > + * 0 - High Speed (default) > + * 1 - Full Speed > + */ > + int speed; > +#define dwc_param_speed_default 0 > +#define DWC_SPEED_PARAM_HIGH 0 > +#define DWC_SPEED_PARAM_FULL 1 > + > + /* > + * Specifies whether low power mode is supported when attached to a Full > + * Speed or Low Speed device in host mode. > + * 0 - Don't support low power mode (default) > + * 1 - Support low power mode > + */ > + int host_support_fs_ls_low_power; > +#define dwc_param_host_support_fs_ls_low_power_default 0 > + > + /* > + * Specifies the PHY clock rate in low power mode when connected to a > + * Low Speed device in host mode. This parameter is applicable only if > + * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS > + * then defaults to 6 MHZ otherwise 48 MHZ. > + * > + * 0 - 48 MHz > + * 1 - 6 MHz > + */ > + int host_ls_low_power_phy_clk; > +#define dwc_param_host_ls_low_power_phy_clk_default 0 > +#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 > +#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 > + > + /* > + * 0 - Use cC FIFO size parameters > + * 1 - Allow dynamic FIFO sizing (default) > + */ > + int enable_dynamic_fifo; > +#define dwc_param_enable_dynamic_fifo_default 1 > + > + /* > + * Number of 4-byte words in the Rx FIFO in device mode when dynamic > + * FIFO sizing is enabled. 16 to 32768 (default 1064) > + */ > + int dev_rx_fifo_size; > +#define dwc_param_dev_rx_fifo_size_default 1064 > + > + /* > + * Number of 4-byte words in the non-periodic Tx FIFO in device mode > + * when dynamic FIFO sizing is enabled. 16 to 32768 (default 1024) > + */ > + int dev_nperio_tx_fifo_size; > +#define dwc_param_dev_nperio_tx_fifo_size_default 1024 > + > + /* > + * Number of 4-byte words in each of the periodic Tx FIFOs in device > + * mode when dynamic FIFO sizing is enabled. 4 to 768 (default 256) > + */ > + u32 dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; > +#define dwc_param_dev_perio_tx_fifo_size_default 256 > + > + /* > + * Number of 4-byte words in the Rx FIFO in host mode when dynamic > + * FIFO sizing is enabled. 16 to 32768 (default 1024) > + */ > + int host_rx_fifo_size; > +#define dwc_param_host_rx_fifo_size_default 1024 > + > + /* > + * Number of 4-byte words in the non-periodic Tx FIFO in host mode > + * when Dynamic FIFO sizing is enabled in the core. 16 to 32768 > + * (default 1024) > + */ > + int host_nperio_tx_fifo_size; > +#define dwc_param_host_nperio_tx_fifo_size_default 1024 > + > + /* > + Number of 4-byte words in the host periodic Tx FIFO when dynamic > + * FIFO sizing is enabled. 16 to 32768 (default 1024) > + */ > + int host_perio_tx_fifo_size; > +#define dwc_param_host_perio_tx_fifo_size_default 1024 > + > + /* > + * The maximum transfer size supported in bytes. 2047 to 65,535 > + * (default 65,535) > + */ > + int max_transfer_size; > +#define dwc_param_max_transfer_size_default 65535 > + > + /* > + * The maximum number of packets in a transfer. 15 to 511 (default 511) > + */ > + int max_packet_count; > +#define dwc_param_max_packet_count_default 511 > + > + /* > + * The number of host channel registers to use. > + * 1 to 16 (default 12) > + * Note: The FPGA configuration supports a maximum of 12 host channels. > + */ > + int host_channels; > +#define dwc_param_host_channels_default 12 > + > + /* > + * The number of endpoints in addition to EP0 available for device > + * mode operations. > + * 1 to 15 (default 6 IN and OUT) > + * Note: The FPGA configuration supports a maximum of 6 IN and OUT > + * endpoints in addition to EP0. > + */ > + int dev_endpoints; > +#define dwc_param_dev_endpoints_default 6 > + > + /* > + * Specifies the type of PHY interface to use. By default, the driver > + * will automatically detect the phy_type. > + * > + * 0 - Full Speed PHY > + * 1 - UTMI+ (default) > + * 2 - ULPI > + */ > + int phy_type; > +#define DWC_PHY_TYPE_PARAM_FS 0 > +#define DWC_PHY_TYPE_PARAM_UTMI 1 > +#define DWC_PHY_TYPE_PARAM_ULPI 2 > +#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI > + > + /* > + * Specifies the UTMI+ Data Width. This parameter is applicable for a > + * PHY_TYPE of UTMI+ or ULPI. (For a ULPI PHY_TYPE, this parameter > + * indicates the data width between the MAC and the ULPI Wrapper.) Also, > + * this parameter is applicable only if the OTG_HSPHY_WIDTH cC parameter > + * was set to "8 and 16 bits", meaning that the core has been configured > + * to work at either data path width. > + * > + * 8 or 16 bits (default 16) > + */ > + int phy_utmi_width; > +#define dwc_param_phy_utmi_width_default 16 > + > + /* > + * Specifies whether the ULPI operates at double or single > + * data rate. This parameter is only applicable if PHY_TYPE is > + * ULPI. > + * > + * 0 - single data rate ULPI interface with 8 bit wide data > + * bus (default) > + * 1 - double data rate ULPI interface with 4 bit wide data > + * bus > + */ > + int phy_ulpi_ddr; > +#define dwc_param_phy_ulpi_ddr_default 0 > + > + /* > + * Specifies whether to use the internal or external supply to > + * drive the vbus with a ULPI phy. > + */ > + int phy_ulpi_ext_vbus; > +#define DWC_PHY_ULPI_INTERNAL_VBUS 0 > +#define DWC_PHY_ULPI_EXTERNAL_VBUS 1 > +#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS > + > + /* > + * Specifies whether to use the I2Cinterface for full speed PHY. This > + * parameter is only applicable if PHY_TYPE is FS. > + * 0 - No (default) > + * 1 - Yes > + */ > + int i2c_enable; > +#define dwc_param_i2c_enable_default 0 > + > + int ulpi_fs_ls; > +#define dwc_param_ulpi_fs_ls_default 0 > + > + int ts_dline; > +#define dwc_param_ts_dline_default 0 > + > + /* > + * Specifies whether dedicated transmit FIFOs are enabled for non > + * periodic IN endpoints in device mode > + * 0 - No > + * 1 - Yes > + */ > + int en_multiple_tx_fifo; > +#define dwc_param_en_multiple_tx_fifo_default 1 > + > + /* > + * Number of 4-byte words in each of the Tx FIFOs in device > + * mode when dynamic FIFO sizing is enabled. 4 to 768 (default 256) > + */ > + u32 dev_tx_fifo_size[MAX_TX_FIFOS]; > +#define dwc_param_dev_tx_fifo_size_default 256 > + > + /* > + * Thresholding enable flag > + * bit 0 - enable non-ISO Tx thresholding > + * bit 1 - enable ISO Tx thresholding > + * bit 2 - enable Rx thresholding > + */ > + u32 thr_ctl; > +#define dwc_param_thr_ctl_default 0 > + > + /* Thresholding length for Tx FIFOs in 32 bit DWORDs */ > + u32 tx_thr_length; > +#define dwc_param_tx_thr_length_default 64 > + > + /* Thresholding length for Rx FIFOs in 32 bit DWORDs */ > + u32 rx_thr_length; > +#define dwc_param_rx_thr_length_default 64 > + > +}; > + > +/* > + * The 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. > + */ > +struct core_if { > + /* Parameters that define how the core should be configured. */ > + struct core_params *core_params; > + > + /* Core Global registers starting at offset 000h. */ > + ulong core_global_regs; > + > + /* Device-specific information */ > + struct device_if *dev_if; > + /* Host-specific information */ > + struct dwc_host_if *host_if; > + > + /* > + * Set to 1 if the core PHY interface bits in USBCFG have been > + * initialized. > + */ > + u8 phy_init_done; > + > + /* > + * SRP Success flag, set by srp success interrupt in FS I2C mode > + */ > + u8 srp_success; > + u8 srp_timer_started; > + > + /* Common configuration information */ > + /* Power and Clock Gating Control Register */ > + ulong pcgcctl; > +#define DWC_OTG_PCGCCTL_OFFSET 0xE00 > + > + /* Push/pop addresses for endpoints or host channels. */ > + ulong data_fifo[MAX_EPS_CHANNELS]; > +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 > +#define DWC_OTG_DATA_FIFO_SIZE 0x1000 > + > + /* Total RAM for FIFOs (Bytes) */ > + u16 total_fifo_size; > + /* Size of Rx FIFO (Bytes) */ > + u16 rx_fifo_size; > + /* Size of Non-periodic Tx FIFO (Bytes) */ > + u16 nperio_tx_fifo_size; > + > + /* 1 if DMA is enabled, 0 otherwise. */ > + u8 dma_enable; > + > + /* 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */ > + u8 en_multiple_tx_fifo; > + > + /* > + * Set to 1 if multiple packets of a high-bandwidth transfer is in > + * process of being queued > + */ > + u8 queuing_high_bandwidth; > + > + /* Hardware Configuration -- stored here for convenience. */ > + ulong hwcfg1; > + ulong hwcfg2; > + ulong hwcfg3; > + ulong hwcfg4; > + > + /* HCD callbacks */ > + /* include/linux/usb/otg.h */ > + > + /* HCD callbacks */ > + struct cil_callbacks *hcd_cb; > + /* PCD callbacks */ > + struct cil_callbacks *pcd_cb; > + > + /* Device mode Periodic Tx FIFO Mask */ > + u32 p_tx_msk; > + /* Device mode Periodic Tx FIFO Mask */ > + u32 tx_msk; > + > + /* Features of various DWC implementation */ > + u32 features; > + > + /* Added to support PLB DMA : phys-virt mapping */ > + resource_size_t phys_addr; > + > + struct delayed_work usb_port_wakeup; > + struct work_struct usb_port_otg; > + struct otg_transceiver *xceiv; > +}; > + > +/* > + * The following functions support initialization of the CIL driver component > + * and the DWC_otg controller. > + */ > +extern void dwc_otg_core_init(struct core_if *core_if); > +extern void init_fslspclksel(struct core_if *core_if); > +extern void dwc_otg_core_dev_init(struct core_if *core_if); > +extern const char *op_state_str(enum usb_otg_state state); > +extern void dwc_otg_enable_global_interrupts(struct core_if *core_if); > +extern void dwc_otg_disable_global_interrupts(struct core_if *core_if); > +extern void dwc_otg_enable_common_interrupts(struct core_if *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 u32 dwc_otg_read_hprt0(struct core_if *core_if) > +{ > + u32 hprt0 = 0; > + hprt0 = dwc_read32(core_if->host_if->hprt0); > + hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 0); > + hprt0 = DWC_HPRT0_PRT_CONN_DET_RW(hprt0, 0); > + hprt0 = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0, 0); > + hprt0 = DWC_HPRT0_PRT_OVRCURR_ACT_RW(hprt0, 0); > + return hprt0; > +} > + > +/* > + * The following functions support managing the DWC_otg controller in either > + * device or host mode. > + */ > +extern void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16 bytes); > +extern void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int _num); > +extern void dwc_otg_flush_rx_fifo(struct core_if *core_if); > + > +#define NP_TXFIFO_EMPTY -1 > +#define MAX_NP_TXREQUEST_Q_SLOTS 8 > + > +/** > + * This function returns the Core Interrupt register. > + */ > +static inline u32 dwc_otg_read_core_intr(struct core_if *core_if) > +{ > + u32 global_regs = (u32) core_if->core_global_regs; > + return dwc_read32(global_regs + DWC_GINTSTS) & > + dwc_read32(global_regs + DWC_GINTMSK); > +} > + > +/** > + * This function returns the mode of the operation, host or device. > + */ > +static inline u32 dwc_otg_mode(struct core_if *core_if) > +{ > + u32 global_regs = (u32) core_if->core_global_regs; > + return dwc_read32(global_regs + DWC_GINTSTS) & 0x1; > +} > + > +static inline u8 dwc_otg_is_device_mode(struct core_if *core_if) > +{ > + return dwc_otg_mode(core_if) != DWC_HOST_MODE; > +} > +static inline u8 dwc_otg_is_host_mode(struct core_if *core_if) > +{ > + return dwc_otg_mode(core_if) == DWC_HOST_MODE; > +} > + > +extern int dwc_otg_handle_common_intr(struct core_if *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. > + */ > +struct 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; > +}; > + > +extern void dwc_otg_cil_register_pcd_callbacks(struct core_if *core_if, > + struct cil_callbacks *cb, > + void *p); > +extern void dwc_otg_cil_register_hcd_callbacks(struct core_if *core_if, > + struct cil_callbacks *cb, > + void *p); > + > +#define DWC_LIMITED_XFER 0x00000000 > +#define DWC_DEVICE_ONLY 0x00000000 > +#define DWC_HOST_ONLY 0x00000000 > + > +#ifdef DWC_LIMITED_XFER_SIZE > +#undef DWC_LIMITED_XFER > +#define DWC_LIMITED_XFER 0x00000001 > +#endif > + > +#ifdef CONFIG_DWC_DEVICE_ONLY > +#undef DWC_DEVICE_ONLY > +#define DWC_DEVICE_ONLY 0x00000002 > +static inline void dwc_otg_hcd_remove(struct device *dev) > +{ > +} > +static inline int dwc_otg_hcd_init(struct device *_dev, > + struct dwc_otg_device *dwc_dev) > +{ > + return 0; > +} > +#else > +extern int __init dwc_otg_hcd_init(struct device *_dev, > + struct dwc_otg_device *dwc_dev); > +extern void dwc_otg_hcd_remove(struct device *_dev); > +#endif > + > +#ifdef CONFIG_DWC_HOST_ONLY > +#undef DWC_HOST_ONLY > +#define DWC_HOST_ONLY 0x00000004 > +static inline void dwc_otg_pcd_remove(struct device *dev) > +{ > +} > +static inline int dwc_otg_pcd_init(struct device *dev) > +{ > + return 0; > +} > +#else > +extern void dwc_otg_pcd_remove(struct device *dev); > +extern int __init dwc_otg_pcd_init(struct device *dev); > +#endif > + > +extern void dwc_otg_cil_remove(struct core_if *core_if); > +extern struct core_if __devinit *dwc_otg_cil_init(const __iomem u32 * base, > + struct core_params *params); > + > +static inline void dwc_set_feature(struct core_if *core_if) > +{ > + core_if->features = DWC_LIMITED_XFER | DWC_DEVICE_ONLY | DWC_HOST_ONLY; > +} > + > +static inline int dwc_has_feature(struct core_if *core_if, > + unsigned long feature) > +{ > + return core_if->features & feature; > +} > +extern struct core_params dwc_otg_module_params; > +extern int __devinit check_parameters(struct core_if *core_if); > +#endif > diff --git a/drivers/usb/otg/dwc/cil_intr.c b/drivers/usb/otg/dwc/cil_intr.c > new file mode 100644 > index 0000000..9111795 > --- /dev/null > +++ b/drivers/usb/otg/dwc/cil_intr.c > @@ -0,0 +1,616 @@ > +/* > + * DesignWare HS OTG controller driver > + * Copyright (C) 2006 Synopsys, Inc. > + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation. > + * > + * This program is free software: you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License version 2 for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see http://www.gnu.org/licenses > + * or write to the Free Software Foundation, Inc., 51 Franklin Street, > + * Suite 500, Boston, MA 02110-1335 USA. > + * > + * Based on Synopsys driver version 2.60a > + * Modified by Mark Miesfeld <mmiesfeld@xxxxxxx> > + * Modified by Stefan Roese <sr@xxxxxxx>, DENX Software Engineering > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. 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. > + * > + */ > + > +/* > + * 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 <linux/delay.h> > + > +#include "cil.h" > + > +/** > + * This function will log a debug message > + */ > +static int dwc_otg_handle_mode_mismatch_intr(struct core_if *core_if) > +{ > + u32 gintsts = 0; > + ulong global_regs = core_if->core_global_regs; > + > + pr_warning("Mode Mismatch Interrupt: currently in %s mode\n", > + dwc_otg_mode(core_if) ? "Host" : "Device"); > + > + /* Clear interrupt */ > + gintsts |= DWC_INTSTS_MODE_MISMTC; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + > + return 1; > +} > + > +/** > + * Start the HCD. Helper function for using the HCD callbacks. > + */ > +static inline void hcd_start(struct core_if *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. > + */ > +static inline void hcd_stop(struct core_if *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. > + */ > +static inline void hcd_disconnect(struct core_if *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. > + */ > +static inline void hcd_session_start(struct core_if *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. > + */ > +static inline void pcd_start(struct core_if *core_if) > +{ > + if (core_if->pcd_cb && core_if->pcd_cb->start) { > + struct dwc_pcd *pcd; > + > + pcd = (struct dwc_pcd *)core_if->pcd_cb->p; > + spin_lock(&pcd->lock); > + core_if->pcd_cb->start(core_if->pcd_cb->p); > + spin_unlock(&pcd->lock); > + } > +} > + > +/** > + * Stop the PCD. Helper function for using the PCD callbacks. > + */ > +static inline void pcd_stop(struct core_if *core_if) > +{ > + if (core_if->pcd_cb && core_if->pcd_cb->stop) { > + struct dwc_pcd *pcd; > + > + pcd = (struct dwc_pcd *)core_if->pcd_cb->p; > + spin_lock(&pcd->lock); > + core_if->pcd_cb->stop(core_if->pcd_cb->p); > + spin_unlock(&pcd->lock); > + } > +} > + > +/** > + * Suspend the PCD. Helper function for using the PCD callbacks. > + */ > +static inline void pcd_suspend(struct core_if *core_if) > +{ > + if (core_if->pcd_cb && core_if->pcd_cb->suspend) { > + struct dwc_pcd *pcd; > + > + pcd = (struct dwc_pcd *)core_if->pcd_cb->p; > + spin_lock(&pcd->lock); > + core_if->pcd_cb->suspend(core_if->pcd_cb->p); > + spin_unlock(&pcd->lock); > + } > +} > + > +/** > + * Resume the PCD. Helper function for using the PCD callbacks. > + */ > +static inline void pcd_resume(struct core_if *core_if) > +{ > + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { > + struct dwc_pcd *pcd; > + > + pcd = (struct dwc_pcd *)core_if->pcd_cb->p; > + spin_lock(&pcd->lock); > + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); > + spin_unlock(&pcd->lock); > + } > +} > + > +/** > + * This function handles the OTG Interrupts. It reads the OTG > + * Interrupt Register (GOTGINT) to determine what interrupt has > + * occurred. > + */ > +static int dwc_otg_handle_otg_intr(struct core_if *core_if) > +{ > + ulong global_regs = core_if->core_global_regs; > + u32 gotgint; > + u32 gotgctl; > + u32 gintmsk; > + > + gotgint = dwc_read32(global_regs + DWC_GOTGINT); > + if (gotgint & DWC_GINT_SES_ENDDET) { > + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL); > + if (core_if->xceiv->state == OTG_STATE_B_HOST) { > + pcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL; > + } else { > + /* > + * If not B_HOST and Device HNP still set. HNP did not > + * succeed > + */ > + if (gotgctl & DWC_GCTL_DEV_HNP_ENA) > + pr_err("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_stop(core_if); > + } > + gotgctl = 0; > + gotgctl |= DWC_GCTL_DEV_HNP_ENA; > + dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0); > + } > + if (gotgint & DWC_GINT_SES_REQSUC) { > + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL); > + if (gotgctl & DWC_GCTL_SES_REQ_SUCCESS) { > + if (core_if->core_params->phy_type == > + DWC_PHY_TYPE_PARAM_FS && > + core_if->core_params->i2c_enable) { > + core_if->srp_success = 1; > + } else { > + pcd_resume(core_if); > + > + /* Clear Session Request */ > + gotgctl = 0; > + gotgctl |= DWC_GCTL_SES_REQ; > + dwc_modify32(global_regs + DWC_GOTGCTL, > + gotgctl, 0); > + } > + } > + } > + if (gotgint & DWC_GINT_HST_NEGSUC) { > + /* > + * Print statements during the HNP interrupt handling can cause > + * it to fail. > + */ > + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL); > + if (gotgctl & DWC_GCTL_HOST_NEG_SUCCES) { > + if (dwc_otg_is_host_mode(core_if)) { > + core_if->xceiv->state = OTG_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 = 0; > + gintmsk |= DWC_INTMSK_STRT_OF_FRM; > + dwc_modify32(global_regs + DWC_GINTMSK, > + gintmsk, 0); > + pcd_stop(core_if); > + /* Initialize the Core for Host mode. */ > + hcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_B_HOST; > + } > + } else { > + gotgctl = 0; > + gotgctl |= DWC_GCTL_HNP_REQ; > + gotgctl |= DWC_GCTL_DEV_HNP_ENA; > + dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0); > + > + pr_err("Device Not Connected / Responding\n"); > + } > + } > + if (gotgint & DWC_GINT_HST_NEGDET) { > + /* > + * 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. > + */ > + if (dwc_otg_is_device_mode(core_if)) { > + hcd_disconnect(core_if); > + pcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_A_PERIPHERAL; > + } else { > + /* > + * 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 = 0; > + gintmsk |= DWC_INTMSK_STRT_OF_FRM; > + dwc_modify32(global_regs + DWC_GINTMSK, gintmsk, 0); > + pcd_stop(core_if); > + hcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_A_HOST; > + } > + } > + if (gotgint & DWC_GINT_DEVTOUT) > + pr_info(" ++OTG Interrupt: A-Device Timeout " "Change++\n"); > + if (gotgint & DWC_GINT_DEBDONE) > + pr_info(" ++OTG Interrupt: Debounce Done++\n"); > + > + /* Clear GOTGINT */ > + dwc_write32(global_regs + DWC_GOTGINT, gotgint); > + return 1; > +} > + > +/* > + * Wakeup Workqueue implementation > + */ > +static void port_otg_wqfunc(struct work_struct *work) > +{ > + struct core_if *core_if = container_of(work, struct core_if, > + usb_port_otg); > + ulong global_regs = core_if->core_global_regs; > + u32 count = 0; > + u32 gotgctl; > + > + pr_info("%s\n", __func__); > + > + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL); > + if (gotgctl & DWC_GCTL_CONN_ID_STATUS) { > + /* > + * B-Device connector (device mode) wait for switch to device > + * mode. > + */ > + while (!dwc_otg_is_device_mode(core_if) && ++count <= 10000) { > + pr_info("Waiting for Peripheral Mode, " > + "Mode=%s\n", dwc_otg_is_host_mode(core_if) ? > + "Host" : "Peripheral"); > + msleep(100); > + } > + BUG_ON(count > 10000); > + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL; > + dwc_otg_core_init(core_if); > + dwc_otg_enable_global_interrupts(core_if); > + pcd_start(core_if); > + } else { > + /* > + * A-Device connector (host mode) wait for switch to host > + * mode. > + */ > + while (!dwc_otg_is_host_mode(core_if) && ++count <= 10000) { > + pr_info("Waiting for Host Mode, Mode=%s\n", > + dwc_otg_is_host_mode(core_if) ? > + "Host" : "Peripheral"); > + msleep(100); > + } > + BUG_ON(count > 10000); > + core_if->xceiv->state = OTG_STATE_A_HOST; > + 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. > + */ > +static int dwc_otg_handle_conn_id_status_change_intr(struct core_if *core_if) > +{ > + u32 gintsts = 0; > + u32 gintmsk = 0; > + ulong global_regs = core_if->core_global_regs; > + > + /* > + * 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 |= DWC_INTSTS_STRT_OF_FRM; > + dwc_modify32(global_regs + DWC_GINTMSK, gintmsk, 0); > + > + INIT_WORK(&core_if->usb_port_otg, port_otg_wqfunc); > + schedule_work(&core_if->usb_port_otg); > + > + /* Set flag and clear interrupt */ > + gintsts |= DWC_INTSTS_CON_ID_STS_CHG; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + 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. > + */ > +static int dwc_otg_handle_session_req_intr(struct core_if *core_if) > +{ > + u32 gintsts = 0; > + ulong global_regs = core_if->core_global_regs; > + > + if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) { > + u32 hprt0; > + > + if (dwc_otg_is_device_mode(core_if)) { > + pr_info("SRP: Device mode\n"); > + } else { > + pr_info("SRP: Host mode\n"); > + > + /* Turn on the port power bit. */ > + hprt0 = dwc_otg_read_hprt0(core_if); > + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1); > + dwc_write32(core_if->host_if->hprt0, hprt0); > + > + /* > + * Start the Connection timer. > + * A message can be displayed, > + * if connect does not occur within 10 seconds. > + */ > + hcd_session_start(core_if); > + } > + } > + /* Clear interrupt */ > + gintsts |= DWC_INTSTS_NEW_SES_DET; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + return 1; > +} > + > +/** > + * 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. > + */ > +static int dwc_otg_handle_wakeup_detected_intr(struct core_if *core_if) > +{ > + u32 gintsts = 0; > + struct device_if *dev_if = core_if->dev_if; > + ulong global_regs = core_if->core_global_regs; Tabify the declarations ; > + > + if (dwc_otg_is_device_mode(core_if)) { > + u32 dctl = 0; > + > + /* Clear the Remote Wakeup Signalling */ > + dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1); > + dwc_modify32(dev_if->dev_global_regs + DWC_DCTL, dctl, 0); > + > + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) > + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); > + } else { > + u32 pcgcctl = 0; > + > + /* Restart the Phy Clock */ > + pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl); > + dwc_modify32(core_if->pcgcctl, pcgcctl, 0); > + schedule_delayed_work(&core_if->usb_port_wakeup, 10); > + } > + > + /* Clear interrupt */ > + gintsts |= DWC_INTSTS_WKP; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + return 1; > +} > + > +/** > + * This interrupt indicates that a device has been disconnected from > + * the root port. > + */ > +static int dwc_otg_handle_disconnect_intr(struct core_if *core_if) > +{ > + u32 gintsts = 0; > + ulong global_regs = core_if->core_global_regs; > + > + if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) { > + if (core_if->xceiv->state == OTG_STATE_B_HOST) { > + hcd_disconnect(core_if); > + pcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL; > + } else if (dwc_otg_is_device_mode(core_if)) { > + u32 gotgctl; > + > + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL); > + > + /* > + * If HNP is in process, do nothing. > + * The OTG "Host Negotiation Detected" > + * interrupt will do the mode switch. > + * Otherwise, since we are in device mode, > + * disconnect and stop the HCD, > + * then start the PCD. > + */ > + if ((gotgctl) & DWC_GCTL_DEV_HNP_ENA) { > + hcd_disconnect(core_if); > + pcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL; > + } > + } else if (core_if->xceiv->state == OTG_STATE_A_HOST) { > + /* A-Cable still connected but device disconnected. */ > + hcd_disconnect(core_if); > + } > + } > + gintsts |= DWC_INTSTS_SES_DISCON_DET; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + 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. > + */ > +static int dwc_otg_handle_usb_suspend_intr(struct core_if *core_if) > +{ > + u32 dsts = 0; > + u32 gintsts = 0; > + ulong global_regs = core_if->core_global_regs; > + struct device_if *dev_if = core_if->dev_if; Tabify the declarations ; > + > + if (dwc_otg_is_device_mode(core_if)) { > + /* > + * Check the Device status register to determine if the Suspend > + * state is active. > + */ > + dsts = dwc_read32(dev_if->dev_global_regs + DWC_DSTS); > + /* PCD callback for suspend. */ > + pcd_suspend(core_if); > + } else { > + if (core_if->xceiv->state == OTG_STATE_A_PERIPHERAL) { > + /* Clear the a_peripheral flag, back to a_host. */ > + pcd_stop(core_if); > + hcd_start(core_if); > + core_if->xceiv->state = OTG_STATE_A_HOST; > + } > + } > + > + /* Clear interrupt */ > + gintsts |= DWC_INTMSK_USB_SUSP; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + return 1; > +} > + > +/** > + * This function returns the Core Interrupt register. > + * > + * Although the Host Port interrupt (portintr) is documented as host mode > + * only, it appears to occur in device mode when Port Enable / Disable Changed > + * bit in HPRT0 is set. The code in dwc_otg_handle_common_intr checks if in > + * device mode and just clears the interrupt. > + */ > +static inline u32 dwc_otg_read_common_intr(struct core_if *core_if) > +{ > + u32 gintsts; > + u32 gintmsk; > + u32 gintmsk_common = 0; > + ulong global_regs = core_if->core_global_regs; > + > + gintmsk_common |= DWC_INTMSK_WKP; > + gintmsk_common |= DWC_INTMSK_NEW_SES_DET; > + gintmsk_common |= DWC_INTMSK_CON_ID_STS_CHG; > + gintmsk_common |= DWC_INTMSK_OTG; > + gintmsk_common |= DWC_INTMSK_MODE_MISMTC; > + gintmsk_common |= DWC_INTMSK_SES_DISCON_DET; > + gintmsk_common |= DWC_INTMSK_USB_SUSP; > + gintmsk_common |= DWC_INTMSK_HST_PORT; > + > + gintsts = dwc_read32(global_regs + DWC_GINTSTS); > + gintmsk = dwc_read32(global_regs + DWC_GINTMSK); > + > + return (gintsts & gintmsk) & gintmsk_common; > +} > + > +/** > + * 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. > + * > + * - Host Port Interrupt. Although this interrupt is documented as only > + * occurring in Host mode, it also occurs in Device mode when Port Enable / > + * Disable Changed bit in HPRT0 is set. If it is seen here, while in Device > + * mode, the interrupt is just cleared. > + * > + */ > +int dwc_otg_handle_common_intr(struct core_if *core_if) > +{ > + int retval = 0; > + u32 gintsts; > + ulong global_regs = core_if->core_global_regs; > + > + gintsts = dwc_otg_read_common_intr(core_if); > + > + if (gintsts & DWC_INTSTS_MODE_MISMTC) > + retval |= dwc_otg_handle_mode_mismatch_intr(core_if); > + if (gintsts & DWC_INTSTS_OTG) > + retval |= dwc_otg_handle_otg_intr(core_if); > + if (gintsts & DWC_INTSTS_CON_ID_STS_CHG) > + retval |= dwc_otg_handle_conn_id_status_change_intr(core_if); > + if (gintsts & DWC_INTSTS_SES_DISCON_DET) > + retval |= dwc_otg_handle_disconnect_intr(core_if); > + if (gintsts & DWC_INTSTS_NEW_SES_DET) > + retval |= dwc_otg_handle_session_req_intr(core_if); > + if (gintsts & DWC_INTSTS_WKP) > + retval |= dwc_otg_handle_wakeup_detected_intr(core_if); > + if (gintsts & DWC_INTMSK_USB_SUSP) > + retval |= dwc_otg_handle_usb_suspend_intr(core_if); > + > + if ((gintsts & DWC_INTSTS_HST_PORT) && > + dwc_otg_is_device_mode(core_if)) { > + gintsts = 0; > + gintsts |= DWC_INTSTS_HST_PORT; > + dwc_write32(global_regs + DWC_GINTSTS, gintsts); > + retval |= 1; > + pr_info("RECEIVED PORTINT while in Device mode\n"); > + } > + > + return retval; > +} > -- > 1.6.1.rc3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html