Skip to content

Commit 991f433

Browse files
authored
🐛 Fix hangs in DUE native USB (MarlinFirmware#26572)
1 parent 54b7da1 commit 991f433

File tree

3 files changed

+100
-9
lines changed

3 files changed

+100
-9
lines changed

Marlin/src/HAL/DUE/usb/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# USB Files Source Documentation
2+
3+
## Source
4+
5+
We sourced the USB files in Marlin from the Atmel ASF (Advanced Software Framework). The framework provides a variety of examples which were utilized in this project.
6+
7+
Atmel doesn't provide these files in a source repository but they can be extracted from ASF, which can be downloaded from Atmel.
8+
9+
[Advanced Software Framework](https://www.microchip.com/en-us/tools-resources/develop/libraries/advanced-software-framework)
10+
11+
## Modifications
12+
13+
The files are mostly unmodified except for minor cosmetic changes but some more significant changes were needed.
14+
15+
The changes that prompted the addition of this README file are listed below. Other changes may have been made prior to this.
16+
17+
1. Modified `uotghs_device_due.c` to resolve race conditions that could leave interrupts asserted when freezing the peripheral clock, resulting in hangs and watchdog resets due to the ensuing interrupt storm.
18+
19+
## Version Information
20+
21+
We don't know the exact version of ASF used as the source. However, the copyright information in the files indicates they are from 2015.
22+
23+
## Upgrade Considerations
24+
25+
We looked at the ASF 3.52.0 files released in 2022 but saw no immediate benefits to justify an upgrade. It's important to note that the files in Marlin don't follow the same folder structure as the files in ASF, which complicates the process of comparing and applying updated files.
26+
27+
When these files are updated it's important to carefully compare them to Marlin's versions so any improvements in the Marlin sources are brought forward.
28+
29+
It would be best to make Marlin's directory structure align with ASF or at least document the source of each file to ease future updates.

Marlin/src/HAL/DUE/usb/uotghs_device_due.c

+70-9
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,23 @@
116116
//#define dbg_print printf
117117
#define dbg_print(...)
118118

119+
// Marlin modification: Redefine the otg_freeze_clock and otg_unfreeze_clock macros
120+
// to add memory barriers to ensure that any accesses to USB registers aren't re-ordered
121+
// to occur while the clock is frozen.
122+
#undef otg_freeze_clock
123+
#undef otg_unfreeze_clock
124+
125+
#define otg_freeze_clock() do { \
126+
__DSB(); \
127+
Set_bits(UOTGHS->UOTGHS_CTRL, UOTGHS_CTRL_FRZCLK); \
128+
} while (0)
129+
130+
#define otg_unfreeze_clock() \
131+
do { \
132+
Clr_bits(UOTGHS->UOTGHS_CTRL, UOTGHS_CTRL_FRZCLK); \
133+
__DSB(); \
134+
} while (0)
135+
119136
/**
120137
* \ingroup udd_group
121138
* \defgroup udd_udphs_group USB On-The-Go High-Speed Port for device mode (UOTGHS)
@@ -611,6 +628,18 @@ ISR(UDD_USB_INT_FUN)
611628
// The wakeup interrupt is automatic acked when a suspend occur
612629
udd_disable_wake_up_interrupt();
613630
udd_enable_suspend_interrupt();
631+
632+
// Marlin modification: The RESET, SOF, and MSOF interrupts were previously
633+
// enabled in udd_attach, which caused a race condition where they could
634+
// be raised and unclearable with the clock is frozen. They are now
635+
// enabled here, after the clock has been unfrozen in response to the wake
636+
// interrupt.
637+
udd_enable_reset_interrupt();
638+
udd_enable_sof_interrupt();
639+
#ifdef USB_DEVICE_HS_SUPPORT
640+
udd_enable_msof_interrupt();
641+
#endif
642+
614643
udd_sleep_mode(true); // Enter in IDLE mode
615644
#ifdef UDC_RESUME_EVENT
616645
UDC_RESUME_EVENT();
@@ -776,6 +805,27 @@ void udd_disable(void)
776805
cpu_irq_restore(flags);
777806
}
778807

808+
// Marlin modification: The original implementation did not use a memory
809+
// barrier between disabling and clearing interrupts. This sometimes
810+
// allowed interrupts to remain raised and unclearable after the clock
811+
// was frozen. This helper was added to ensure that memory barriers
812+
// are used consistently from all places where interrupts are disabled.
813+
static void disable_and_ack_sync_interrupts()
814+
{
815+
// Disable USB line events
816+
udd_disable_reset_interrupt();
817+
udd_disable_sof_interrupt();
818+
#ifdef USB_DEVICE_HS_SUPPORT
819+
udd_disable_msof_interrupt();
820+
#endif
821+
__DSB();
822+
udd_ack_reset();
823+
udd_ack_sof();
824+
#ifdef USB_DEVICE_HS_SUPPORT
825+
udd_ack_msof();
826+
#endif
827+
__DSB();
828+
}
779829

780830
void udd_attach(void)
781831
{
@@ -796,17 +846,16 @@ void udd_attach(void)
796846
udd_attach_device();
797847

798848
// Enable USB line events
799-
udd_enable_reset_interrupt();
800849
udd_enable_suspend_interrupt();
801850
udd_enable_wake_up_interrupt();
802-
udd_enable_sof_interrupt();
803-
#ifdef USB_DEVICE_HS_SUPPORT
804-
udd_enable_msof_interrupt();
805-
#endif
806-
// Reset following interrupts flag
807-
udd_ack_reset();
808-
udd_ack_sof();
809-
udd_ack_msof();
851+
852+
// Marlin modification: The RESET, SOF, and MSOF interrupts were previously
853+
// enabled here, which caused a race condition where they could be raised
854+
// and unclearable with the clock is frozen. They are now enabled in the
855+
// wake interrupt handler, after the clock has been unfrozen. They are now
856+
// explicitly disabled here to ensure that they cannot be raised before
857+
// the clock is frozen.
858+
disable_and_ack_sync_interrupts();
810859

811860
// The first suspend interrupt must be forced
812861
// The first suspend interrupt is not detected else raise it
@@ -824,6 +873,12 @@ void udd_detach(void)
824873

825874
// Detach device from the bus
826875
udd_detach_device();
876+
877+
// Marlin modification: Added the explicit disabling of the RESET, SOF, and
878+
// MSOF interrupts here, to ensure that they cannot be raised after the
879+
// clock is frozen.
880+
disable_and_ack_sync_interrupts();
881+
827882
otg_freeze_clock();
828883
udd_sleep_mode(false);
829884
}
@@ -2043,6 +2098,12 @@ static bool udd_ep_interrupt(void)
20432098
dbg_print("I ");
20442099
udd_disable_in_send_interrupt(ep);
20452100
// One bank is free then send a ZLP
2101+
2102+
// Marlin modification: Add a barrier to ensure in_send is disabled
2103+
// before it is cleared. This was not an observed problem, but
2104+
// other interrupts were seen to misbehave without this barrier.
2105+
__DSB();
2106+
20462107
udd_ack_in_send(ep);
20472108
udd_ack_fifocon(ep);
20482109
udd_ep_finish_job(ptr_job, false, ep);

buildroot/tests/DUE_archim

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ exec_test $1 $2 "Archim 1 base configuration" "$3"
1616
# Test Archim 2
1717
#
1818
use_example_configs UltiMachine/Archim2
19+
opt_enable USB_FLASH_DRIVE_SUPPORT
1920
exec_test $1 $2 "Archim 2 base configuration" "$3"
2021

2122
restore_configs

0 commit comments

Comments
 (0)