From 3fd2f53c681521124e117e2082daa2fbf578dae2 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Tue, 17 Dec 2024 14:31:25 +0100 Subject: [PATCH] fix(usb/host): Fix reaction on High-Speed NYET packet In Scatter-Gather DMA mode, the USB-DWC will automatically enable PING protocol if an OUT packet is NACKed by the High-Speed device. The PING bit must be manually reset. --- .../hal/esp32p4/include/hal/usb_dwc_ll.h | 34 +++++++---- .../hal/esp32s2/include/hal/usb_dwc_ll.h | 57 +++++++------------ .../hal/esp32s3/include/hal/usb_dwc_ll.h | 57 +++++++------------ components/hal/usb_dwc_hal.c | 15 +++-- 4 files changed, 71 insertions(+), 92 deletions(-) diff --git a/components/hal/esp32p4/include/hal/usb_dwc_ll.h b/components/hal/esp32p4/include/hal/usb_dwc_ll.h index fd0685a50d..6bb1b67480 100644 --- a/components/hal/esp32p4/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32p4/include/hal/usb_dwc_ll.h @@ -790,23 +790,33 @@ static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan chan->hctsiz_reg.val = hctsiz.val; } -static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan) +/** + * @brief Perform PING protocol + * + * PING protocol is automatically enabled if High-Speed device responds with NYET in Scatter-Gather DMA mode. + * The application must disable PING for next transfer. + * Relevant only for OUT transfers. + * + * @param[in] chan Channel registers + * @param[in] enable true: Enable PING, false: Disable PING + */ +static inline void usb_dwc_ll_hctsiz_set_dopng(volatile usb_dwc_host_chan_regs_t *chan, bool enable) { - usb_dwc_hctsiz_reg_t hctsiz; - hctsiz.val = chan->hctsiz_reg.val; - hctsiz.dopng = 0; //Don't do ping - /* - Set SCHED_INFO which occupies xfersize[7:0] - It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels - */ - hctsiz.xfersize |= 0xFF; - chan->hctsiz_reg.val = hctsiz.val; + chan->hctsiz_reg.dopng = (uint32_t)(enable && !chan->hcchar_reg.epdir); } +/** + * @brief Set scheduling info for Periodic channel + * + * @attention This function must be called for each periodic channel! + * @see USB-OTG databook: Table 5-47 + * + * @param[in] chan Channel registers + * @param[in] tokens_per_frame HS: Number of tokens per frame FS: Must be set 8 + * @param[in] offset Offset of the channel + */ static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset) { - // @see USB-OTG databook: Table 5-47 - // This function is relevant only for HS usb_dwc_hctsiz_reg_t hctsiz; hctsiz.val = chan->hctsiz_reg.val; uint8_t sched_info_val; diff --git a/components/hal/esp32s2/include/hal/usb_dwc_ll.h b/components/hal/esp32s2/include/hal/usb_dwc_ll.h index 7426be2efa..940497d2e8 100644 --- a/components/hal/esp32s2/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32s2/include/hal/usb_dwc_ll.h @@ -791,50 +791,33 @@ static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan chan->hctsiz_reg.val = hctsiz.val; } -static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan) +/** + * @brief Perform PING protocol + * + * @note This function is here only for compatibility reasons. PING is not relevant on FS only targets + * @param[in] chan Channel registers + * @param[in] enable true: Enable PING, false: Disable PING + */ +static inline void usb_dwc_ll_hctsiz_set_dopng(volatile usb_dwc_host_chan_regs_t *chan, bool enable) { - usb_dwc_hctsiz_reg_t hctsiz; - hctsiz.val = chan->hctsiz_reg.val; - hctsiz.dopng = 0; //Don't do ping - /* - Set SCHED_INFO which occupies xfersize[7:0] - It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels - */ - hctsiz.xfersize |= 0xFF; - chan->hctsiz_reg.val = hctsiz.val; } +/** + * @brief Set scheduling info for Periodic channel + * + * @note ESP32-S2 is Full-Speed only, so SCHED_INFO is always set to 0xFF + * @attention This function must be called for each periodic channel! + * @see USB-OTG databook: Table 5-47 + * + * @param[in] chan Channel registers + * @param[in] tokens_per_frame Ignored + * @param[in] offset Ignored + */ static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset) { - // @see USB-OTG databook: Table 5-47 - // This function is relevant only for HS usb_dwc_hctsiz_reg_t hctsiz; hctsiz.val = chan->hctsiz_reg.val; - uint8_t sched_info_val; - switch (tokens_per_frame) { - case 1: - offset %= 8; // If the required offset > 8, we must wrap around to SCHED_INFO size = 8 - sched_info_val = 0b00000001; - break; - case 2: - offset %= 4; - sched_info_val = 0b00010001; - break; - case 4: - offset %= 2; - sched_info_val = 0b01010101; - break; - case 8: - offset = 0; - sched_info_val = 0b11111111; - break; - default: - abort(); - break; - } - sched_info_val <<= offset; - hctsiz.xfersize &= ~(0xFF); - hctsiz.xfersize |= sched_info_val; + hctsiz.xfersize |= 0xFF; chan->hctsiz_reg.val = hctsiz.val; } diff --git a/components/hal/esp32s3/include/hal/usb_dwc_ll.h b/components/hal/esp32s3/include/hal/usb_dwc_ll.h index 7426be2efa..29a9787eba 100644 --- a/components/hal/esp32s3/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32s3/include/hal/usb_dwc_ll.h @@ -791,50 +791,33 @@ static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan chan->hctsiz_reg.val = hctsiz.val; } -static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan) +/** + * @brief Perform PING protocol + * + * @note This function is here only for compatibility reasons. PING is not relevant on FS only targets + * @param[in] chan Channel registers + * @param[in] enable true: Enable PING, false: Disable PING + */ +static inline void usb_dwc_ll_hctsiz_set_dopng(volatile usb_dwc_host_chan_regs_t *chan, bool enable) { - usb_dwc_hctsiz_reg_t hctsiz; - hctsiz.val = chan->hctsiz_reg.val; - hctsiz.dopng = 0; //Don't do ping - /* - Set SCHED_INFO which occupies xfersize[7:0] - It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels - */ - hctsiz.xfersize |= 0xFF; - chan->hctsiz_reg.val = hctsiz.val; } +/** + * @brief Set scheduling info for Periodic channel + * + * @note ESP32-S3 is Full-Speed only, so SCHED_INFO is always set to 0xFF + * @attention This function must be called for each periodic channel! + * @see USB-OTG databook: Table 5-47 + * + * @param[in] chan Channel registers + * @param[in] tokens_per_frame Ignored + * @param[in] offset Ignored + */ static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset) { - // @see USB-OTG databook: Table 5-47 - // This function is relevant only for HS usb_dwc_hctsiz_reg_t hctsiz; hctsiz.val = chan->hctsiz_reg.val; - uint8_t sched_info_val; - switch (tokens_per_frame) { - case 1: - offset %= 8; // If the required offset > 8, we must wrap around to SCHED_INFO size = 8 - sched_info_val = 0b00000001; - break; - case 2: - offset %= 4; - sched_info_val = 0b00010001; - break; - case 4: - offset %= 2; - sched_info_val = 0b01010101; - break; - case 8: - offset = 0; - sched_info_val = 0b11111111; - break; - default: - abort(); - break; - } - sched_info_val <<= offset; - hctsiz.xfersize &= ~(0xFF); - hctsiz.xfersize |= sched_info_val; + hctsiz.xfersize |= 0xFF; chan->hctsiz_reg.val = hctsiz.val; } diff --git a/components/hal/usb_dwc_hal.c b/components/hal/usb_dwc_hal.c index 3f43762637..c7a0637356 100644 --- a/components/hal/usb_dwc_hal.c +++ b/components/hal/usb_dwc_hal.c @@ -312,7 +312,6 @@ bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan usb_dwc_ll_haintmsk_en_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx); usb_dwc_ll_hcintmsk_set_intr_mask(chan_obj->regs, CHAN_INTRS_EN_MSK); //Unmask interrupts for this channel usb_dwc_ll_hctsiz_set_pid(chan_obj->regs, 0); //Set the initial PID to zero - usb_dwc_ll_hctsiz_init(chan_obj->regs); //Set the non changing parts of the HCTSIZ registers (e.g., do_ping and sched info) return true; } @@ -383,8 +382,10 @@ void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t hal->periodic_frame_list[index] |= 1 << chan_obj->flags.chan_idx; } // For HS endpoints we must write to sched_info field of HCTSIZ register to schedule microframes + // For FS endpoints sched_info is always 0xFF + // LS endpoints do not support periodic transfers + unsigned int tokens_per_frame = 0; if (ep_char->periodic.is_hs) { - unsigned int tokens_per_frame; if (ep_char->periodic.interval >= 8) { tokens_per_frame = 1; // 1 token every 8 microframes } else if (ep_char->periodic.interval >= 4) { @@ -394,8 +395,8 @@ void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t } else { tokens_per_frame = 8; // 1 token every microframe } - usb_dwc_ll_hctsiz_set_sched_info(chan_obj->regs, tokens_per_frame, ep_char->periodic.offset); } + usb_dwc_ll_hctsiz_set_sched_info(chan_obj->regs, tokens_per_frame, ep_char->periodic.offset); } } @@ -403,12 +404,14 @@ void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t void usb_dwc_hal_chan_activate(usb_dwc_hal_chan_t *chan_obj, void *xfer_desc_list, int desc_list_len, int start_idx) { - //Cannot activate a channel that has already been enabled or is pending error handling + // Cannot activate a channel that has already been enabled or is pending error handling HAL_ASSERT(!chan_obj->flags.active); - //Set start address of the QTD list and starting QTD index + // Make sure that PING is not enabled from previous transaction + usb_dwc_ll_hctsiz_set_dopng(chan_obj->regs, false); + // Set start address of the QTD list and starting QTD index usb_dwc_ll_hcdma_set_qtd_list_addr(chan_obj->regs, xfer_desc_list, start_idx); usb_dwc_ll_hctsiz_set_qtd_list_len(chan_obj->regs, desc_list_len); - usb_dwc_ll_hcchar_enable_chan(chan_obj->regs); //Start the channel + usb_dwc_ll_hcchar_enable_chan(chan_obj->regs); // Start the channel chan_obj->flags.active = 1; }