Skip to content

Commit f7d1c10

Browse files
committed
Add support for EPX preemption on RP2350 during NAK conditions
1 parent 0c6fa9b commit f7d1c10

File tree

1 file changed

+88
-24
lines changed

1 file changed

+88
-24
lines changed

src/portable/raspberrypi/rp2040/hcd_rp2040.c

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) {
108108

109109
// forward declaration
110110
static void __tusb_irq_path_func(edpt_schedule_next)(void);
111+
TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value);
112+
static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len);
111113

112114
static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) {
113115
// Mark transfer as done before we tell the tinyusb stack
@@ -222,29 +224,79 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
222224
}
223225
}
224226

225-
// if (status & USB_INTS_EP_STALL_NAK_BITS) {
226-
// const uint32_t ep_stall_nak = usb_hw->ep_status_stall_nak;
227-
// usb_hw->ep_status_stall_nak = ep_stall_nak; // clear by writing back (WC)
228-
//
229-
// // Preempt non-control EPX bulk transfer when a different ep is pending
230-
// if (epx->active && tu_edpt_number(epx->ep_addr) != 0) {
231-
// for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) {
232-
// hw_endpoint_t *ep = &ep_pool[i];
233-
// if (ep->pending && ep != epx) {
234-
// // Stop current transaction
235-
// usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS;
236-
//
237-
// // Mark current EPX as pending to resume later
238-
// epx->pending = 1;
239-
// epx->active = false;
240-
//
241-
// // Start the next pending transfer
242-
// edpt_schedule_next();
243-
// break;
244-
// }
245-
// }
246-
// }
247-
// }
227+
#if defined(PICO_RP2350) && PICO_RP2350 == 1
228+
if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) {
229+
// RP2350: EPX transfer stopped due to NAK from device.
230+
// Clear EPX_STOPPED_ON_NAK status (WC)
231+
usb_hw->nak_poll |= USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS;
232+
233+
bool preempted = false;
234+
235+
// Only preempt non-control endpoints
236+
if (epx->active && tu_edpt_number(epx->ep_addr) != 0) {
237+
// Find the next pending transfer (different from the current epx)
238+
for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) {
239+
hw_endpoint_t *ep = &ep_pool[i];
240+
if (ep->pending && ep != epx) {
241+
// NAK means no data transferred. Restore remaining_len from buffer control
242+
// so edpt_schedule_next can properly resume this transfer later.
243+
const uint16_t buf0_len = usbh_dpram->epx_buf_ctrl & USB_BUF_CTRL_LEN_MASK;
244+
epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len);
245+
epx->next_pid ^= 1u; // undo PID toggle from hwbuf_prepare
246+
if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) {
247+
epx->user_buf -= buf0_len; // undo buffer advance for OUT
248+
}
249+
250+
// Mark current EPX as pending to resume later
251+
epx->pending = 1;
252+
epx->active = false;
253+
254+
// Clear EPX buffer control - AVAILABLE is still set from the NAK'd transfer
255+
usbh_dpram->epx_buf_ctrl = 0;
256+
257+
// Start the found pending transfer directly
258+
if (ep->pending == 2) {
259+
// Pending setup: DPRAM already has the setup packet
260+
ep->ep_addr = 0;
261+
ep->remaining_len = 8;
262+
ep->xferred_len = 0;
263+
ep->active = true;
264+
ep->pending = 0;
265+
266+
epx = ep;
267+
usb_hw->dev_addr_ctrl = ep->dev_addr;
268+
269+
const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS |
270+
(ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
271+
sie_start_xfer(sc);
272+
} else {
273+
// Pending data transfer: preserve partial progress
274+
uint16_t prev_xferred = ep->xferred_len;
275+
ep->pending = 0;
276+
edpt_xfer(ep, ep->user_buf, NULL, ep->remaining_len);
277+
epx->xferred_len += prev_xferred;
278+
}
279+
280+
preempted = true;
281+
break;
282+
}
283+
}
284+
}
285+
286+
if (!preempted && epx->active) {
287+
// No preemption needed: disable stop-on-NAK and restart the transaction.
288+
// Buffer control still has AVAILABLE set, just re-trigger START_TRANS.
289+
uint32_t nak_poll = usb_hw->nak_poll;
290+
nak_poll &= ~USB_NAK_POLL_STOP_EPX_ON_NAK_BITS;
291+
usb_hw->nak_poll = nak_poll;
292+
293+
const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr);
294+
const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) |
295+
(epx->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
296+
sie_start_xfer(sie_ctrl);
297+
}
298+
}
299+
#endif
248300

249301
if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) {
250302
usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
@@ -344,10 +396,14 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
344396
USB_INTE_HOST_RESUME_BITS |
345397
USB_INTE_STALL_BITS |
346398
USB_INTE_TRANS_COMPLETE_BITS |
347-
// USB_INTE_EP_STALL_NAK_BITS |
348399
USB_INTE_ERROR_RX_TIMEOUT_BITS |
349400
USB_INTE_ERROR_DATA_SEQ_BITS ;
350401

402+
#if defined(PICO_RP2350) && PICO_RP2350 == 1
403+
// RP2350: Enable EPX stopped-on-NAK interrupt (feature is enabled dynamically when transfers are pending)
404+
usb_hw_set->inte = USB_INTE_EPX_STOPPED_ON_NAK_BITS;
405+
#endif
406+
351407
return true;
352408
}
353409

@@ -557,6 +613,10 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b
557613
ep->user_buf = buffer;
558614
ep->remaining_len = buflen;
559615
ep->pending = 1;
616+
#if defined(PICO_RP2350) && PICO_RP2350 == 1
617+
// RP2350: Enable stop-on-NAK so current EPX transfer can be preempted
618+
usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS;
619+
#endif
560620
return true;
561621
}
562622

@@ -589,6 +649,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet
589649
// If EPX is busy, mark as pending setup (DPRAM already has the packet)
590650
if (epx->active) {
591651
ep->pending = 2;
652+
#if defined(PICO_RP2350) && PICO_RP2350 == 1
653+
// RP2350: Enable stop-on-NAK so current EPX transfer can be preempted
654+
usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS;
655+
#endif
592656
return true;
593657
}
594658

0 commit comments

Comments
 (0)