diff options
author | 2024-05-28 22:21:34 +0200 | |
---|---|---|
committer | 2024-05-28 22:21:34 +0200 | |
commit | f73a058be5d70dd81a43f16b2bbff4b1576a7af8 (patch) | |
tree | b7959c01cf7a5d95c7c4d5b61929ff9123370322 /drivers/usb/dwc2 | |
parent | dma-buf: handle testing kthreads creation failure (diff) | |
parent | Merge tag 'drm-misc-fixes-2024-05-23' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes (diff) | |
download | wireguard-linux-f73a058be5d70dd81a43f16b2bbff4b1576a7af8.tar.xz wireguard-linux-f73a058be5d70dd81a43f16b2bbff4b1576a7af8.zip |
Merge remote-tracking branch 'drm/drm-fixes' into drm-misc-fixes
v6.10-rc1 is released, forward from v6.9
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r-- | drivers/usb/dwc2/core.c | 42 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.h | 22 | ||||
-rw-r--r-- | drivers/usb/dwc2/core_intr.c | 98 | ||||
-rw-r--r-- | drivers/usb/dwc2/debugfs.c | 1 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 38 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd.c | 57 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd_ddma.c | 19 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd_queue.c | 52 | ||||
-rw-r--r-- | drivers/usb/dwc2/hw.h | 16 | ||||
-rw-r--r-- | drivers/usb/dwc2/params.c | 43 | ||||
-rw-r--r-- | drivers/usb/dwc2/platform.c | 2 |
11 files changed, 302 insertions, 88 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 5635e4d7ec88..9919ab725d54 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -249,6 +249,11 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); + /* Reset ULPI latch */ + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + /* Disable PMU interrupt */ gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUINTSEL; @@ -975,6 +980,41 @@ void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) dwc2_writel(hsotg, hcfg, HCFG); } +static void dwc2_set_clock_switch_timer(struct dwc2_hsotg *hsotg) +{ + u32 grstctl, gsnpsid, val = 0; + + gsnpsid = dwc2_readl(hsotg, GSNPSID); + + /* + * Applicable only to HSOTG core v5.00a or higher. + * Not applicable to HS/FS IOT devices. + */ + if ((gsnpsid & ~DWC2_CORE_REV_MASK) != DWC2_OTG_ID || + gsnpsid < DWC2_CORE_REV_5_00a) + return; + + if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) || + (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) || + (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED && + hsotg->hw_params.fs_phy_type != GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED)) { + val = GRSTCTL_CLOCK_SWITH_TIMER_VALUE_DIS; + } + + if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW && + hsotg->hw_params.hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED && + hsotg->hw_params.fs_phy_type != GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) { + val = GRSTCTL_CLOCK_SWITH_TIMER_VALUE_147; + } + + grstctl = dwc2_readl(hsotg, GRSTCTL); + grstctl &= ~GRSTCTL_CLOCK_SWITH_TIMER_MASK; + grstctl |= GRSTCTL_CLOCK_SWITH_TIMER(val); + dwc2_writel(hsotg, grstctl, GRSTCTL); +} + static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) { u32 usbcfg, ggpio, i2cctl; @@ -992,6 +1032,8 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) usbcfg |= GUSBCFG_PHYSEL; dwc2_writel(hsotg, usbcfg, GUSBCFG); + dwc2_set_clock_switch_timer(hsotg); + /* Reset after a PHY select */ retval = dwc2_core_reset(hsotg, false); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index c92a1da46a01..2bd74f3033ed 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -288,6 +288,11 @@ enum dwc2_ep0_state { * core has been configured to work at either data path * width. * 8 or 16 (default 16 if available) + * @eusb2_disc: Specifies whether eUSB2 PHY disconnect support flow + * applicable or no. Applicable in device mode of HSOTG + * and HS IOT cores v5.00 or higher. + * 0 - eUSB2 PHY disconnect support flow not applicable + * 1 - eUSB2 PHY disconnect support flow applicable * @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single * data rate. This parameter is only applicable if phy_type * is ULPI. @@ -442,6 +447,7 @@ struct dwc2_core_params { #define DWC2_SPEED_PARAM_LOW 2 u8 phy_utmi_width; + bool eusb2_disc; bool phy_ulpi_ddr; bool phy_ulpi_ext_vbus; bool enable_dynamic_fifo; @@ -729,8 +735,14 @@ struct dwc2_dregs_backup { * struct dwc2_hregs_backup - Holds host registers state before * entering partial power down * @hcfg: Backup of HCFG register + * @hflbaddr: Backup of HFLBADDR register * @haintmsk: Backup of HAINTMSK register + * @hcchar: Backup of HCCHAR register + * @hcsplt: Backup of HCSPLT register * @hcintmsk: Backup of HCINTMSK register + * @hctsiz: Backup of HCTSIZ register + * @hdma: Backup of HCDMA register + * @hcdmab: Backup of HCDMAB register * @hprt0: Backup of HPTR0 register * @hfir: Backup of HFIR register * @hptxfsiz: Backup of HPTXFSIZ register @@ -738,8 +750,14 @@ struct dwc2_dregs_backup { */ struct dwc2_hregs_backup { u32 hcfg; + u32 hflbaddr; u32 haintmsk; + u32 hcchar[MAX_EPS_CHANNELS]; + u32 hcsplt[MAX_EPS_CHANNELS]; u32 hcintmsk[MAX_EPS_CHANNELS]; + u32 hctsiz[MAX_EPS_CHANNELS]; + u32 hcidma[MAX_EPS_CHANNELS]; + u32 hcidmab[MAX_EPS_CHANNELS]; u32 hprt0; u32 hfir; u32 hptxfsiz; @@ -1086,6 +1104,7 @@ struct dwc2_hsotg { bool needs_byte_swap; /* DWC OTG HW Release versions */ +#define DWC2_CORE_REV_4_30a 0x4f54430a #define DWC2_CORE_REV_2_71a 0x4f54271a #define DWC2_CORE_REV_2_72a 0x4f54272a #define DWC2_CORE_REV_2_80a 0x4f54280a @@ -1097,8 +1116,10 @@ struct dwc2_hsotg { #define DWC2_CORE_REV_3_10a 0x4f54310a #define DWC2_CORE_REV_4_00a 0x4f54400a #define DWC2_CORE_REV_4_20a 0x4f54420a +#define DWC2_CORE_REV_5_00a 0x4f54500a #define DWC2_FS_IOT_REV_1_00a 0x5531100a #define DWC2_HS_IOT_REV_1_00a 0x5532100a +#define DWC2_HS_IOT_REV_5_00a 0x5532500a #define DWC2_CORE_REV_MASK 0x0000ffff /* DWC OTG HW Core ID */ @@ -1323,6 +1344,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg); void dwc2_enable_acg(struct dwc2_hsotg *hsotg); +void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup); /* This function should be called on every hardware interrupt. */ irqreturn_t dwc2_handle_common_intr(int irq, void *dev); diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 158ede753854..7d3e641806f8 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -84,6 +84,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) u32 gotgint; u32 gotgctl; u32 gintmsk; + u32 pcgctl; gotgint = dwc2_readl(hsotg, GOTGINT); gotgctl = dwc2_readl(hsotg, GOTGCTL); @@ -96,8 +97,22 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) dwc2_op_state_str(hsotg)); gotgctl = dwc2_readl(hsotg, GOTGCTL); - if (dwc2_is_device_mode(hsotg)) + if (dwc2_is_device_mode(hsotg)) { + if (hsotg->params.eusb2_disc) { + /* Clear the Gate hclk. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Clear Phy Clock bit. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + } dwc2_hsotg_disconnect(hsotg); + } if (hsotg->op_state == OTG_STATE_B_HOST) { hsotg->op_state = OTG_STATE_B_PERIPHERAL; @@ -117,7 +132,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) * disconnected */ /* Reset to a clean state */ - hsotg->lx_state = DWC2_L0; + hsotg->lx_state = DWC2_L3; } gotgctl = dwc2_readl(hsotg, GOTGCTL); @@ -286,7 +301,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) hsotg->lx_state); if (dwc2_is_device_mode(hsotg)) { - if (hsotg->lx_state == DWC2_L2) { + if (hsotg->lx_state != DWC2_L0) { if (hsotg->in_ppd) { ret = dwc2_exit_partial_power_down(hsotg, 0, true); @@ -297,7 +312,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) /* Exit gadget mode clock gating. */ if (hsotg->params.power_down == - DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && + !hsotg->params.no_clock_gating) dwc2_gadget_exit_clock_gating(hsotg, 0); } @@ -322,10 +338,11 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) * @hsotg: Programming view of DWC_otg controller * */ -static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg) +void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup) { u32 glpmcfg; - u32 i = 0; + u32 pcgctl; + u32 dctl; if (hsotg->lx_state != DWC2_L1) { dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n"); @@ -334,37 +351,57 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg) glpmcfg = dwc2_readl(hsotg, GLPMCFG); if (dwc2_is_device_mode(hsotg)) { - dev_dbg(hsotg->dev, "Exit from L1 state\n"); + dev_dbg(hsotg->dev, "Exit from L1 state, remotewakeup=%d\n", remotewakeup); glpmcfg &= ~GLPMCFG_ENBLSLPM; - glpmcfg &= ~GLPMCFG_HIRD_THRES_EN; + glpmcfg &= ~GLPMCFG_HIRD_THRES_MASK; dwc2_writel(hsotg, glpmcfg, GLPMCFG); - do { - glpmcfg = dwc2_readl(hsotg, GLPMCFG); + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_ENBL_SLEEP_GATING; + dwc2_writel(hsotg, pcgctl, PCGCTL); - if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK | - GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS))) - break; + glpmcfg = dwc2_readl(hsotg, GLPMCFG); + if (glpmcfg & GLPMCFG_ENBESL) { + glpmcfg |= GLPMCFG_RSTRSLPSTS; + dwc2_writel(hsotg, glpmcfg, GLPMCFG); + } + + if (remotewakeup) { + if (dwc2_hsotg_wait_bit_set(hsotg, GLPMCFG, GLPMCFG_L1RESUMEOK, 1000)) { + dev_warn(hsotg->dev, "%s: timeout GLPMCFG_L1RESUMEOK\n", __func__); + goto fail; + return; + } - udelay(1); - } while (++i < 200); + dctl = dwc2_readl(hsotg, DCTL); + dctl |= DCTL_RMTWKUPSIG; + dwc2_writel(hsotg, dctl, DCTL); - if (i == 200) { - dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n"); + if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) { + dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__); + goto fail; + return; + } + } + + glpmcfg = dwc2_readl(hsotg, GLPMCFG); + if (glpmcfg & GLPMCFG_COREL1RES_MASK || glpmcfg & GLPMCFG_SLPSTS || + glpmcfg & GLPMCFG_L1RESUMEOK) { + goto fail; return; } - dwc2_gadget_init_lpm(hsotg); + + /* Inform gadget to exit from L1 */ + call_gadget(hsotg, resume); + /* Change to L0 state */ + hsotg->lx_state = DWC2_L0; + hsotg->bus_suspended = false; +fail: dwc2_gadget_init_lpm(hsotg); } else { /* TODO */ dev_err(hsotg->dev, "Host side LPM is not supported.\n"); return; } - - /* Change to L0 state */ - hsotg->lx_state = DWC2_L0; - - /* Inform gadget to exit from L1 */ - call_gadget(hsotg, resume); } /* @@ -385,7 +422,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); if (hsotg->lx_state == DWC2_L1) { - dwc2_wakeup_from_lpm_l1(hsotg); + dwc2_wakeup_from_lpm_l1(hsotg, false); return; } @@ -408,7 +445,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Exit gadget mode clock gating. */ if (hsotg->params.power_down == - DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && + !hsotg->params.no_clock_gating) dwc2_gadget_exit_clock_gating(hsotg, 0); } else { /* Change to L0 state */ @@ -425,7 +463,8 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) } if (hsotg->params.power_down == - DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && + !hsotg->params.no_clock_gating) dwc2_host_exit_clock_gating(hsotg, 1); /* @@ -690,6 +729,11 @@ static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg, gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + /* Reset ULPI latch */ + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + /* De-assert Wakeup Logic */ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp &= ~GPWRDN_PMUACTV; diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index 1d72ece9cfe4..7c82ab590401 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -686,6 +686,7 @@ static int params_show(struct seq_file *seq, void *v) print_param(seq, p, host_channels); print_param(seq, p, phy_type); print_param(seq, p, phy_utmi_width); + print_param(seq, p, eusb2_disc); print_param(seq, p, phy_ulpi_ddr); print_param(seq, p, phy_ulpi_ext_vbus); print_param(seq, p, i2c_enable); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b517a7216de2..74ac79abd8f3 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1415,6 +1415,10 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); + if (hs->lx_state == DWC2_L1) { + dwc2_wakeup_from_lpm_l1(hs, true); + } + /* Prevent new request submission when controller is suspended */ if (hs->lx_state != DWC2_L0) { dev_dbg(hs->dev, "%s: submit request only in active state\n", @@ -3420,8 +3424,11 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_init_fifo(hsotg); - if (!is_usb_reset) + if (!is_usb_reset) { dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); + if (hsotg->params.eusb2_disc) + dwc2_set_bit(hsotg, GOTGCTL, GOTGCTL_EUSB2_DISC_SUPP); + } dcfg |= DCFG_EPMISCNT(1); @@ -3727,6 +3734,12 @@ irq_retry: if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2) dwc2_exit_partial_power_down(hsotg, 0, true); + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && + !hsotg->params.no_clock_gating) + dwc2_gadget_exit_clock_gating(hsotg, 0); + hsotg->lx_state = DWC2_L0; } @@ -5306,6 +5319,8 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) { u32 gpwrdn; + u32 gusbcfg; + u32 pcgcctl; int ret = 0; /* Change to L2(suspend) state */ @@ -5325,6 +5340,22 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) } gpwrdn = GPWRDN_PWRDNRSTN; + udelay(10); + gusbcfg = dwc2_readl(hsotg, GUSBCFG); + if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { + /* ULPI interface */ + gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + } + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + + /* Suspend the Phy Clock */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(10); + + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PMUACTV; dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); @@ -5425,6 +5456,11 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, if (reset) dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); + /* Reset ULPI latch */ + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + /* De-assert Wakeup Logic */ gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 35c7a4df8e71..cb54390e7de4 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2701,8 +2701,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( hsotg->available_host_channels--; } qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); - if (dwc2_assign_and_init_hc(hsotg, qh)) + if (dwc2_assign_and_init_hc(hsotg, qh)) { + if (hsotg->params.uframe_sched) + hsotg->available_host_channels++; break; + } /* * Move the QH from the periodic ready schedule to the @@ -2735,8 +2738,11 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( hsotg->available_host_channels--; } - if (dwc2_assign_and_init_hc(hsotg, qh)) + if (dwc2_assign_and_init_hc(hsotg, qh)) { + if (hsotg->params.uframe_sched) + hsotg->available_host_channels++; break; + } /* * Move the QH from the non-periodic inactive schedule to the @@ -4143,6 +4149,8 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, urb->actual_length); if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + if (!hsotg->params.dma_desc_enable) + urb->start_frame = qtd->qh->start_active_frame; urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb); for (i = 0; i < urb->number_of_packets; ++i) { urb->iso_frame_desc[i].actual_length = @@ -4649,7 +4657,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, } if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && - hsotg->bus_suspended) { + hsotg->bus_suspended && !hsotg->params.no_clock_gating) { if (dwc2_is_device_mode(hsotg)) dwc2_gadget_exit_clock_gating(hsotg, 0); else @@ -5406,9 +5414,16 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) /* Backup Host regs */ hr = &hsotg->hr_backup; hr->hcfg = dwc2_readl(hsotg, HCFG); + hr->hflbaddr = dwc2_readl(hsotg, HFLBADDR); hr->haintmsk = dwc2_readl(hsotg, HAINTMSK); - for (i = 0; i < hsotg->params.host_channels; ++i) + for (i = 0; i < hsotg->params.host_channels; ++i) { + hr->hcchar[i] = dwc2_readl(hsotg, HCCHAR(i)); + hr->hcsplt[i] = dwc2_readl(hsotg, HCSPLT(i)); hr->hcintmsk[i] = dwc2_readl(hsotg, HCINTMSK(i)); + hr->hctsiz[i] = dwc2_readl(hsotg, HCTSIZ(i)); + hr->hcidma[i] = dwc2_readl(hsotg, HCDMA(i)); + hr->hcidmab[i] = dwc2_readl(hsotg, HCDMAB(i)); + } hr->hprt0 = dwc2_read_hprt0(hsotg); hr->hfir = dwc2_readl(hsotg, HFIR); @@ -5442,10 +5457,17 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) hr->valid = false; dwc2_writel(hsotg, hr->hcfg, HCFG); + dwc2_writel(hsotg, hr->hflbaddr, HFLBADDR); dwc2_writel(hsotg, hr->haintmsk, HAINTMSK); - for (i = 0; i < hsotg->params.host_channels; ++i) + for (i = 0; i < hsotg->params.host_channels; ++i) { + dwc2_writel(hsotg, hr->hcchar[i], HCCHAR(i)); + dwc2_writel(hsotg, hr->hcsplt[i], HCSPLT(i)); dwc2_writel(hsotg, hr->hcintmsk[i], HCINTMSK(i)); + dwc2_writel(hsotg, hr->hctsiz[i], HCTSIZ(i)); + dwc2_writel(hsotg, hr->hcidma[i], HCDMA(i)); + dwc2_writel(hsotg, hr->hcidmab[i], HCDMAB(i)); + } dwc2_writel(hsotg, hr->hprt0, HPRT0); dwc2_writel(hsotg, hr->hfir, HFIR); @@ -5503,6 +5525,11 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) gusbcfg = dwc2_readl(hsotg, GUSBCFG); if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { /* ULPI interface */ + udelay(10); + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); /* Suspend the Phy Clock */ pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl |= PCGCTL_STOPPCLK; @@ -5609,11 +5636,18 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); dwc2_writel(hsotg, hr->hcfg, HCFG); - /* De-assert Wakeup Logic */ + /* Reset ULPI latch */ gpwrdn = dwc2_readl(hsotg, GPWRDN); - gpwrdn &= ~GPWRDN_PMUACTV; + gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; dwc2_writel(hsotg, gpwrdn, GPWRDN); - udelay(10); + + /* De-assert Wakeup Logic */ + if (!(rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) { + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + } hprt0 = hr->hprt0; hprt0 |= HPRT0_PWR; @@ -5638,6 +5672,13 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, hprt0 |= HPRT0_RES; dwc2_writel(hsotg, hprt0, HPRT0); + /* De-assert Wakeup Logic */ + if ((rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) { + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + } /* Wait for Resume time and then program HPRT again */ mdelay(100); hprt0 &= ~HPRT0_RES; diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c index 6b4d825e97a2..994a78ad084b 100644 --- a/drivers/usb/dwc2/hcd_ddma.c +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -559,7 +559,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg, idx = qh->td_last; inc = qh->host_interval; hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); - cur_idx = dwc2_frame_list_idx(hsotg->frame_number); + cur_idx = idx; next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed); /* @@ -866,20 +866,27 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, { struct dwc2_dma_desc *dma_desc; struct dwc2_hcd_iso_packet_desc *frame_desc; + u16 frame_desc_idx; + struct urb *usb_urb; u16 remain = 0; int rc = 0; if (!qtd->urb) return -EINVAL; + usb_urb = qtd->urb->priv; + dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx * sizeof(struct dwc2_dma_desc)), sizeof(struct dwc2_dma_desc), DMA_FROM_DEVICE); dma_desc = &qh->desc_list[idx]; + frame_desc_idx = (idx - qtd->isoc_td_first) & (usb_urb->number_of_packets - 1); - frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; + frame_desc = &qtd->urb->iso_descs[frame_desc_idx]; + if (idx == qtd->isoc_td_first) + usb_urb->start_frame = dwc2_hcd_get_frame_number(hsotg); dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset); if (chan->ep_is_in) remain = (dma_desc->status & HOST_DMA_ISOC_NBYTES_MASK) >> @@ -900,7 +907,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, frame_desc->status = 0; } - if (++qtd->isoc_frame_index == qtd->urb->packet_count) { + if (++qtd->isoc_frame_index == usb_urb->number_of_packets) { /* * urb->status is not used for isoc transfers here. The * individual frame_desc status are used instead. @@ -1005,11 +1012,11 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, return; idx = dwc2_desclist_idx_inc(idx, qh->host_interval, chan->speed); - if (!rc) + if (rc == 0) continue; - if (rc == DWC2_CMPL_DONE) - break; + if (rc == DWC2_CMPL_DONE || rc == DWC2_CMPL_STOP) + goto stop_scan; /* rc == DWC2_CMPL_STOP */ diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 0d4495c6b9f7..238c6fd50e75 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/dma-mapping.h> #include <linux/io.h> +#include <linux/seq_buf.h> #include <linux/slab.h> #include <linux/usb.h> @@ -360,41 +361,6 @@ static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg, #ifdef DWC2_PRINT_SCHEDULE /* - * cat_printf() - A printf() + strcat() helper - * - * This is useful for concatenating a bunch of strings where each string is - * constructed using printf. - * - * @buf: The destination buffer; will be updated to point after the printed - * data. - * @size: The number of bytes in the buffer (includes space for '\0'). - * @fmt: The format for printf. - * @...: The args for printf. - */ -static __printf(3, 4) -void cat_printf(char **buf, size_t *size, const char *fmt, ...) -{ - va_list args; - int i; - - if (*size == 0) - return; - - va_start(args, fmt); - i = vsnprintf(*buf, *size, fmt, args); - va_end(args); - - if (i >= *size) { - (*buf)[*size - 1] = '\0'; - *buf += *size; - *size = 0; - } else { - *buf += i; - *size -= i; - } -} - -/* * pmap_print() - Print the given periodic map * * Will attempt to print out the periodic schedule. @@ -416,9 +382,7 @@ static void pmap_print(unsigned long *map, int bits_per_period, int period; for (period = 0; period < periods_in_map; period++) { - char tmp[64]; - char *buf = tmp; - size_t buf_size = sizeof(tmp); + DECLARE_SEQ_BUF(buf, 64); int period_start = period * bits_per_period; int period_end = period_start + bits_per_period; int start = 0; @@ -442,19 +406,19 @@ static void pmap_print(unsigned long *map, int bits_per_period, continue; if (!printed) - cat_printf(&buf, &buf_size, "%s %d: ", - period_name, period); + seq_buf_printf(&buf, "%s %d: ", + period_name, period); else - cat_printf(&buf, &buf_size, ", "); + seq_buf_puts(&buf, ", "); printed = true; - cat_printf(&buf, &buf_size, "%d %s -%3d %s", start, - units, start + count - 1, units); + seq_buf_printf(&buf, "%d %s -%3d %s", start, + units, start + count - 1, units); count = 0; } if (printed) - print_fn(tmp, print_data); + print_fn(seq_buf_str(&buf), print_data); } } diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 13abdd5f6752..fe8c87a2f8b5 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -11,6 +11,7 @@ #define HSOTG_REG(x) (x) #define GOTGCTL HSOTG_REG(0x000) +#define GOTGCTL_EUSB2_DISC_SUPP BIT(28) #define GOTGCTL_CHIRPEN BIT(27) #define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) #define GOTGCTL_MULT_VALID_BC_SHIFT 22 @@ -98,6 +99,17 @@ #define GRSTCTL_AHBIDLE BIT(31) #define GRSTCTL_DMAREQ BIT(30) #define GRSTCTL_CSFTRST_DONE BIT(29) +#define GRSTCTL_CLOCK_SWITH_TIMER_MASK (0x7 << 11) +#define GRSTCTL_CLOCK_SWITH_TIMER_SHIFT 11 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_19 0x0 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_15 0x1 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_147 0x2 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_50 0x3 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_100 0x4 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_125 0x5 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_200 0x6 +#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_DIS 0x7 +#define GRSTCTL_CLOCK_SWITH_TIMER(_x) ((_x) << 11) #define GRSTCTL_TXFNUM_MASK (0x1f << 6) #define GRSTCTL_TXFNUM_SHIFT 6 #define GRSTCTL_TXFNUM_LIMIT 0x1f @@ -332,6 +344,8 @@ #define GLPMCFG_LPMCAP BIT(0) #define GPWRDN HSOTG_REG(0x0058) + +#define GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY BIT(29) #define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) #define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 #define GPWRDN_ADP_INT BIT(23) @@ -698,7 +712,7 @@ #define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) #define TXSTS_QTOP_TOKEN_SHIFT 25 #define TXSTS_QTOP_TERMINATE BIT(24) -#define TXSTS_QSPCAVAIL_MASK (0xff << 16) +#define TXSTS_QSPCAVAIL_MASK (0x7f << 16) #define TXSTS_QSPCAVAIL_SHIFT 16 #define TXSTS_FSPCAVAIL_MASK (0xffff << 0) #define TXSTS_FSPCAVAIL_SHIFT 0 diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index eb677c3cfd0b..5a1500d0bdd9 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -201,6 +201,25 @@ static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; } +static void dwc2_set_cv1800_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_caps.hnp_support = false; + p->otg_caps.srp_support = false; + p->host_dma = false; + p->g_dma = false; + p->speed = DWC2_SPEED_PARAM_HIGH; + p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; + p->phy_utmi_width = 16; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; +} + static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -295,6 +314,8 @@ const struct of_device_id dwc2_of_match_table[] = { .data = dwc2_set_amlogic_a1_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params }, + { .compatible = "sophgo,cv1800-usb", + .data = dwc2_set_cv1800_params }, { .compatible = "st,stm32f4x9-fsotg", .data = dwc2_set_stm32f4x9_fsotg_params }, { .compatible = "st,stm32f4x9-hsotg" }, @@ -475,6 +496,7 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) dwc2_set_param_lpm(hsotg); p->phy_ulpi_ddr = false; p->phy_ulpi_ext_vbus = false; + p->eusb2_disc = false; p->enable_dynamic_fifo = hw->enable_dynamic_fifo; p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo; @@ -737,6 +759,25 @@ static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) } } +static void dwc2_check_param_eusb2_disc(struct dwc2_hsotg *hsotg) +{ + u32 gsnpsid; + + if (!hsotg->params.eusb2_disc) + return; + gsnpsid = dwc2_readl(hsotg, GSNPSID); + /* + * eusb2_disc not supported by FS IOT devices. + * For other cores, it supported starting from version 5.00a + */ + if ((gsnpsid & ~DWC2_CORE_REV_MASK) == DWC2_FS_IOT_ID || + (gsnpsid & DWC2_CORE_REV_MASK) < + (DWC2_CORE_REV_5_00a & DWC2_CORE_REV_MASK)) { + hsotg->params.eusb2_disc = false; + return; + } +} + #define CHECK_RANGE(_param, _min, _max, _def) do { \ if ((int)(hsotg->params._param) < (_min) || \ (hsotg->params._param) > (_max)) { \ @@ -765,6 +806,8 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) dwc2_check_param_speed(hsotg); dwc2_check_param_phy_utmi_width(hsotg); dwc2_check_param_power_down(hsotg); + dwc2_check_param_eusb2_disc(hsotg); + CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo); CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo); CHECK_BOOL(i2c_enable, hw->i2c_enable); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index b1d48019e944..7b84416dfc2b 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -331,7 +331,7 @@ static void dwc2_driver_remove(struct platform_device *dev) /* Exit clock gating when driver is removed. */ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && - hsotg->bus_suspended) { + hsotg->bus_suspended && !hsotg->params.no_clock_gating) { if (dwc2_is_device_mode(hsotg)) dwc2_gadget_exit_clock_gating(hsotg, 0); else |