diff options
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r-- | drivers/usb/dwc2/core.c | 126 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.h | 8 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 94 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc2/hw.h | 1 | ||||
-rw-r--r-- | drivers/usb/dwc2/platform.c | 34 |
6 files changed, 172 insertions, 93 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 4135a5ff67ca..fa9b26b91507 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -238,6 +238,77 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) return ret; } +/** + * dwc2_wait_for_mode() - Waits for the controller mode. + * @hsotg: Programming view of the DWC_otg controller. + * @host_mode: If true, waits for host mode, otherwise device mode. + */ +static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg, + bool host_mode) +{ + ktime_t start; + ktime_t end; + unsigned int timeout = 110; + + dev_vdbg(hsotg->dev, "Waiting for %s mode\n", + host_mode ? "host" : "device"); + + start = ktime_get(); + + while (1) { + s64 ms; + + if (dwc2_is_host_mode(hsotg) == host_mode) { + dev_vdbg(hsotg->dev, "%s mode set\n", + host_mode ? "Host" : "Device"); + break; + } + + end = ktime_get(); + ms = ktime_to_ms(ktime_sub(end, start)); + + if (ms >= (s64)timeout) { + dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n", + __func__, host_mode ? "host" : "device"); + break; + } + + usleep_range(1000, 2000); + } +} + +/** + * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce + * filter is enabled. + */ +static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) +{ + u32 gsnpsid; + u32 ghwcfg4; + + if (!dwc2_hw_is_otg(hsotg)) + return false; + + /* Check if core configuration includes the IDDIG filter. */ + ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4); + if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) + return false; + + /* + * Check if the IDDIG debounce filter is bypassed. Available + * in core version >= 3.10a. + */ + gsnpsid = dwc2_readl(hsotg->regs + GSNPSID); + if (gsnpsid >= DWC2_CORE_REV_3_10a) { + u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + + if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) + return false; + } + + return true; +} + /* * Do core a soft reset of the core. Be careful with this because it * resets all the internal state machines of the core. @@ -246,9 +317,30 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) { u32 greset; int count = 0; + bool wait_for_host_mode = false; dev_vdbg(hsotg->dev, "%s()\n", __func__); + /* + * If the current mode is host, either due to the force mode + * bit being set (which persists after core reset) or the + * connector id pin, a core soft reset will temporarily reset + * the mode to device. A delay from the IDDIG debounce filter + * will occur before going back to host mode. + * + * Determine whether we will go back into host mode after a + * reset and account for this delay after the reset. + */ + if (dwc2_iddig_filter_enabled(hsotg)) { + u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + + if (!(gotgctl & GOTGCTL_CONID_B) || + (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { + wait_for_host_mode = true; + } + } + /* Core Soft Reset */ greset = dwc2_readl(hsotg->regs + GRSTCTL); greset |= GRSTCTL_CSFTRST; @@ -277,6 +369,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) } } while (!(greset & GRSTCTL_AHBIDLE)); + if (wait_for_host_mode) + dwc2_wait_for_mode(hsotg, true); + return 0; } @@ -300,9 +395,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) * Checks are done in this function to determine whether doing a force * would be valid or not. * - * If a force is done, it requires a 25ms delay to take effect. - * - * Returns true if the mode was forced. + * If a force is done, it requires a IDDIG debounce filter delay if + * the filter is configured and enabled. We poll the current mode of + * the controller to account for this delay. */ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) { @@ -337,12 +432,18 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) gusbcfg |= set; dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); - msleep(25); + dwc2_wait_for_mode(hsotg, host); return true; } -/* - * Clears the force mode bits. +/** + * dwc2_clear_force_mode() - Clears the force mode bits. + * + * After clearing the bits, wait up to 100 ms to account for any + * potential IDDIG filter delay. We can't know if we expect this delay + * or not because the value of the connector ID status is affected by + * the force mode. We only need to call this once during probe if + * dr_mode == OTG. */ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) { @@ -353,11 +454,8 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) gusbcfg &= ~GUSBCFG_FORCEDEVMODE; dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); - /* - * NOTE: This long sleep is _very_ important, otherwise the core will - * not stay in host mode after a connector ID change! - */ - msleep(25); + if (dwc2_iddig_filter_enabled(hsotg)) + usleep_range(100000, 110000); } /* @@ -380,12 +478,6 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg) __func__, hsotg->dr_mode); break; } - - /* - * NOTE: This is required for some rockchip soc based - * platforms. - */ - msleep(50); } /* diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index d64551243789..aad4107ef927 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -259,13 +259,6 @@ enum dwc2_lx_state { DWC2_L3, /* Off state */ }; -/* - * Gadget periodic tx fifo sizes as used by legacy driver - * EP0 is not included - */ -#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \ - 768, 0, 0, 0, 0, 0, 0, 0} - /* Gadget ep0 states */ enum dwc2_ep0_state { DWC2_EP0_SETUP, @@ -890,6 +883,7 @@ struct dwc2_hsotg { #define DWC2_CORE_REV_2_92a 0x4f54292a #define DWC2_CORE_REV_2_94a 0x4f54294a #define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) union dwc2_hcd_internal_flags { diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index af46adfae41c..4cd6403a7566 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -186,9 +186,10 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, */ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) { - unsigned int ep; + unsigned int fifo; unsigned int addr; int timeout; + u32 dptxfsizn; u32 val; /* Reset fifo map if not correctly cleared during previous session */ @@ -216,16 +217,16 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) * them to endpoints dynamically according to maxpacket size value of * given endpoint. */ - for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) { - if (!hsotg->g_tx_fifo_sz[ep]) - continue; - val = addr; - val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT; - WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem, - "insufficient fifo memory"); - addr += hsotg->g_tx_fifo_sz[ep]; + for (fifo = 1; fifo < MAX_EPS_CHANNELS; fifo++) { + dptxfsizn = dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)); + + val = (dptxfsizn & FIFOSIZE_DEPTH_MASK) | addr; + addr += dptxfsizn >> FIFOSIZE_DEPTH_SHIFT; + + if (addr > hsotg->fifo_mem) + break; - dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep)); + dwc2_writel(val, hsotg->regs + DPTXFSIZN(fifo)); } /* @@ -388,7 +389,8 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, return -ENOSPC; } } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { - can_write = dwc2_readl(hsotg->regs + DTXFSTS(hs_ep->index)); + can_write = dwc2_readl(hsotg->regs + + DTXFSTS(hs_ep->fifo_index)); can_write &= 0xffff; can_write *= 4; @@ -2432,7 +2434,7 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg, if (!hsotg->dedicated_fifos) return; - size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; + size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->fifo_index)) & 0xffff) * 4; if (size < ep->fifo_size) dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); } @@ -3041,22 +3043,11 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, break; } - /* If fifo is already allocated for this ep */ - if (hs_ep->fifo_index) { - size = hs_ep->ep.maxpacket * hs_ep->mc; - /* If bigger fifo is required deallocate current one */ - if (size > hs_ep->fifo_size) { - hsotg->fifo_map &= ~(1 << hs_ep->fifo_index); - hs_ep->fifo_index = 0; - hs_ep->fifo_size = 0; - } - } - /* * if the hardware has dedicated fifos, we must give each IN EP * a unique tx-fifo even if it is non-periodic. */ - if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) { + if (dir_in && hsotg->dedicated_fifos) { u32 fifo_index = 0; u32 fifo_size = UINT_MAX; size = hs_ep->ep.maxpacket*hs_ep->mc; @@ -3129,10 +3120,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&hsotg->lock, flags); - hsotg->fifo_map &= ~(1<<hs_ep->fifo_index); - hs_ep->fifo_index = 0; - hs_ep->fifo_size = 0; - ctrl = dwc2_readl(hsotg->regs + epctrl_reg); ctrl &= ~DXEPCTL_EPENA; ctrl &= ~DXEPCTL_USBACTEP; @@ -3147,6 +3134,10 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); + hsotg->fifo_map &= ~(1 << hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + spin_unlock_irqrestore(&hsotg->lock, flags); return 0; } @@ -3475,8 +3466,11 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget); spin_lock_irqsave(&hsotg->lock, flags); - dwc2_hsotg_init(hsotg); - dwc2_hsotg_core_init_disconnected(hsotg, false); + if (dwc2_hw_is_device(hsotg)) { + dwc2_hsotg_init(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + } + hsotg->enabled = 0; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -3813,36 +3807,10 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg) { struct device_node *np = hsotg->dev->of_node; - u32 len = 0; - u32 i = 0; /* Enable dma if requested in device tree */ hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma"); - /* - * Register TX periodic fifo size per endpoint. - * EP0 is excluded since it has no fifo configuration. - */ - if (!of_find_property(np, "g-tx-fifo-size", &len)) - goto rx_fifo; - - len /= sizeof(u32); - - /* Read tx fifo sizes other than ep0 */ - if (of_property_read_u32_array(np, "g-tx-fifo-size", - &hsotg->g_tx_fifo_sz[1], len)) - goto rx_fifo; - - /* Add ep0 */ - len++; - - /* Make remaining TX fifos unavailable */ - if (len < MAX_EPS_CHANNELS) { - for (i = len; i < MAX_EPS_CHANNELS; i++) - hsotg->g_tx_fifo_sz[i] = 0; - } - -rx_fifo: /* Register RX fifo size */ of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz); @@ -3864,13 +3832,10 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) struct device *dev = hsotg->dev; int epnum; int ret; - int i; - u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE; /* Initialize to legacy fifo configuration values */ hsotg->g_rx_fifo_sz = 2048; hsotg->g_np_g_tx_fifo_sz = 1024; - memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo)); /* Device tree specific probe */ dwc2_hsotg_of_probe(hsotg); @@ -3888,9 +3853,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", hsotg->g_np_g_tx_fifo_sz); dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz); - for (i = 0; i < MAX_EPS_CHANNELS; i++) - dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i, - hsotg->g_tx_fifo_sz[i]); hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; @@ -3908,17 +3870,13 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); - if (!hsotg->ctrl_buff) { - dev_err(dev, "failed to allocate ctrl request buff\n"); + if (!hsotg->ctrl_buff) return -ENOMEM; - } hsotg->ep0_buff = devm_kzalloc(hsotg->dev, DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); - if (!hsotg->ep0_buff) { - dev_err(dev, "failed to allocate ctrl reply buff\n"); + if (!hsotg->ep0_buff) return -ENOMEM; - } ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED, dev_name(hsotg->dev), hsotg); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 2df3d04d26f5..df5a06578005 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5040,7 +5040,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) /* Create new workqueue and init work */ retval = -ENOMEM; - hsotg->wq_otg = create_singlethread_workqueue("dwc2"); + hsotg->wq_otg = alloc_ordered_workqueue("dwc2", 0); if (!hsotg->wq_otg) { dev_err(hsotg->dev, "Failed to create workqueue\n"); goto error2; diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index efc3bcde2822..91058441e62a 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -48,6 +48,7 @@ #define GOTGCTL_ASESVLD (1 << 18) #define GOTGCTL_DBNC_SHORT (1 << 17) #define GOTGCTL_CONID_B (1 << 16) +#define GOTGCTL_DBNCE_FLTR_BYPASS (1 << 15) #define GOTGCTL_DEVHNPEN (1 << 11) #define GOTGCTL_HSTSETHNPEN (1 << 10) #define GOTGCTL_HNPREQ (1 << 9) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 530959a8a6d1..8e1728b39a49 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -182,6 +182,38 @@ static const struct dwc2_core_params params_ltq = { .hibernation = -1, }; +static const struct dwc2_core_params params_amlogic = { + .otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE, + .otg_ver = -1, + .dma_enable = 1, + .dma_desc_enable = 0, + .dma_desc_fs_enable = 0, + .speed = DWC2_SPEED_PARAM_HIGH, + .enable_dynamic_fifo = 1, + .en_multiple_tx_fifo = -1, + .host_rx_fifo_size = 512, + .host_nperio_tx_fifo_size = 500, + .host_perio_tx_fifo_size = 500, + .max_transfer_size = -1, + .max_packet_count = -1, + .host_channels = 16, + .phy_type = DWC2_PHY_TYPE_PARAM_UTMI, + .phy_utmi_width = -1, + .phy_ulpi_ddr = -1, + .phy_ulpi_ext_vbus = -1, + .i2c_enable = -1, + .ulpi_fs_ls = -1, + .host_support_fs_ls_low_power = -1, + .host_ls_low_power_phy_clk = -1, + .ts_dline = -1, + .reload_ctl = 1, + .ahbcfg = GAHBCFG_HBSTLEN_INCR8 << + GAHBCFG_HBSTLEN_SHIFT, + .uframe_sched = 0, + .external_id_pin_ctl = -1, + .hibernation = -1, +}; + /* * Check the dr_mode against the module configuration and hardware * capabilities. @@ -486,6 +518,8 @@ static const struct of_device_id dwc2_of_match_table[] = { { .compatible = "lantiq,xrx200-usb", .data = ¶ms_ltq }, { .compatible = "snps,dwc2", .data = NULL }, { .compatible = "samsung,s3c6400-hsotg", .data = NULL}, + { .compatible = "amlogic,meson8b-usb", .data = ¶ms_amlogic }, + { .compatible = "amlogic,meson-gxbb-usb", .data = ¶ms_amlogic }, {}, }; MODULE_DEVICE_TABLE(of, dwc2_of_match_table); |