mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-31 11:17:20 +02:00
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.
This commit is contained in:
@ -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;
|
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;
|
chan->hctsiz_reg.dopng = (uint32_t)(enable && !chan->hcchar_reg.epdir);
|
||||||
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
|
||||||
|
*
|
||||||
|
* @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)
|
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;
|
usb_dwc_hctsiz_reg_t hctsiz;
|
||||||
hctsiz.val = chan->hctsiz_reg.val;
|
hctsiz.val = chan->hctsiz_reg.val;
|
||||||
uint8_t sched_info_val;
|
uint8_t sched_info_val;
|
||||||
|
@ -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;
|
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)
|
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;
|
usb_dwc_hctsiz_reg_t hctsiz;
|
||||||
hctsiz.val = chan->hctsiz_reg.val;
|
hctsiz.val = chan->hctsiz_reg.val;
|
||||||
uint8_t sched_info_val;
|
hctsiz.xfersize |= 0xFF;
|
||||||
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;
|
|
||||||
chan->hctsiz_reg.val = hctsiz.val;
|
chan->hctsiz_reg.val = hctsiz.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
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)
|
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;
|
usb_dwc_hctsiz_reg_t hctsiz;
|
||||||
hctsiz.val = chan->hctsiz_reg.val;
|
hctsiz.val = chan->hctsiz_reg.val;
|
||||||
uint8_t sched_info_val;
|
hctsiz.xfersize |= 0xFF;
|
||||||
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;
|
|
||||||
chan->hctsiz_reg.val = hctsiz.val;
|
chan->hctsiz_reg.val = hctsiz.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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_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_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_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;
|
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;
|
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 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) {
|
if (ep_char->periodic.is_hs) {
|
||||||
unsigned int tokens_per_frame;
|
|
||||||
if (ep_char->periodic.interval >= 8) {
|
if (ep_char->periodic.interval >= 8) {
|
||||||
tokens_per_frame = 1; // 1 token every 8 microframes
|
tokens_per_frame = 1; // 1 token every 8 microframes
|
||||||
} else if (ep_char->periodic.interval >= 4) {
|
} 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 {
|
} else {
|
||||||
tokens_per_frame = 8; // 1 token every microframe
|
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)
|
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);
|
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_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_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;
|
chan_obj->flags.active = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user