Skip to content

stm32f0/l0: Fix HSI48 USB clock recovery configuration. - #19904

Merged
xiaoxiang781216 merged 5 commits into
apache:masterfrom
ArrestedLightning:usb/hsi48-crs-standalone
Aug 20, 2026
Merged

stm32f0/l0: Fix HSI48 USB clock recovery configuration.#19904
xiaoxiang781216 merged 5 commits into
apache:masterfrom
ArrestedLightning:usb/hsi48-crs-standalone

Conversation

@jsanchez-2g

Copy link
Copy Markdown
Contributor

Summary

Fix HSI48 and clock recovery system configuration required for crystal-less USB
device operation on STM32F0 and STM32L0.

The fixes:

  • Enable the CRS peripheral clock whenever HSI48 is in use, while retaining
    support for boards that explicitly select CONFIG_STM32_CRS.
  • Enable the CRS frequency error counter together with automatic trimming.
  • Enable the SYSCFG clock when VREFINT is enabled on STM32L0.
  • Write SYSCFG_CFGR3 after setting ENBUFVREFINTHSI48; previously the HSI48
    voltage-reference bit was updated only in a local variable.
  • Select USB SOF as the CRS synchronization source for NUCLEO-L073RZ.

Before these changes, USB device registers could appear correctly configured,
and the host could detect the D+ pull-up, but the 48 MHz clock was not usable:
the controller did not latch USB reset in USB_ISTR and never raised its USB
interrupt.

Impact

  • New feature added: NO. Bug fixes only.
  • User adaptation required: NO.
  • Build impact: NO.
  • Hardware impact: YES. STM32F0, STM32L0, and the shared HSI48 M0 helper,
    which also supports STM32U0.
  • Documentation update required: NO. Board configuration documentation is
    included with the corresponding board-support PRs.
  • Security impact: NO.
  • Compatibility impact: Existing HSI48 users retain their selected behavior;
    the CRS is now correctly enabled and able to trim the oscillator.

Testing

Build host:

  • Linux 6.18.33.2-microsoft-standard-WSL2
  • arm-none-eabi-gcc 13.2.1

Hardware:

  • ST NUCLEO-F072RB
  • ST NUCLEO-L073RZ

Before:

  • On NUCLEO-L073RZ, SYSCFG_CFGR3 read 0x00000000 because SYSCFG was
    unclocked. USB_ISTR never latched host reset, and USB IRQ 47 never fired.
  • Both board USB-device configurations failed enumeration when tested from
    pristine apache/master plus board support.

After:

  • NUCLEO-L073RZ: SYSCFG_CFGR3 enables VREFINT and the HSI48 reference;
    CRS_CR enables CEN and AUTOTRIMEN; NSH and USB CDC/ACM enumeration pass.
  • NUCLEO-F072RB: NSH and USB CDC/ACM enumeration pass.
  • Both results were reproduced from a clean upstream NuttX plus upstream
    NuttX Apps checkout after the signed histories were rebuilt.

Validation:

  • ./tools/checkpatch.sh -g apache/master...usb/hsi48-crs-standalone
  • File-mode checkpatch passed for all changed files.
  • Real hardware build, flash, NSH, and enumeration tests passed on both boards.

PR verification Self-Check

  • This PR introduces one functional area: HSI48/CRS clock recovery fixes.
  • All commits include a description, Signed-off-by, and Assisted-by trailers.
  • Source passes checkpatch.
  • Real hardware build and runtime testing was completed.
  • This PR is ready for review.

The clock recovery system peripheral clock was enabled only when
CONFIG_STM32_CRS was selected.  The CRS is required by any board that
uses HSI48 as the 48MHz clock source, because HSI48 must be trimmed from
an external synchronisation event to stay within the tolerance demanded
by USB full speed operation.

STM32_USE_HSI48 is the condition under which the remaining HSI48 and CRS
setup is compiled in, see stm32_enable_hsi48(), so accept that condition
here as well.  Without the peripheral clock the CRS registers are
inaccessible and automatic trimming silently does nothing.

CONFIG_STM32_CRS is retained so that boards selecting the CRS directly
are unaffected.

Assisted-by: GitHub Copilot:claude-opus-5
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
The SYSCFG peripheral clock was gated on CONFIG_STM32_SYSCFG, but that
symbol is not defined by the STM32L0 Kconfig (only stm32h7 declares it),
so RCC_APB2ENR_SYSCFGEN was never set on this chip.

With SYSCFG unclocked every write performed by vrefint_enable() is
discarded and SYSCFG_CFGR3 reads back as 0x00000000.  VREFINT and, in
particular, the ENBUFVREFINTHSI48 reference for the HSI48 oscillator are
therefore never enabled.  HSI48 still reports HSI48RDY, but it runs
without its voltage reference and the 48MHz clock supplied to the USB
device controller is unusable: the controller cannot sample the bus,
never latches a reset condition in USB_ISTR, and never raises its
interrupt.  The result is a USB device that is configured correctly in
every visible register yet never enumerates.

VREFINT is configured exclusively through SYSCFG_CFGR3, so enable the
SYSCFG clock whenever CONFIG_STM32_VREFINT is selected.

Observed on NUCLEO-L073RZ (STM32L073RZ):

  before: SYSCFG_CFGR3 = 0x00000000, no enumeration
  after:  SYSCFG_CFGR3 non-zero, device enumerates as CDC/ACM

Assisted-by: GitHub Copilot:claude-opus-5
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
stm32_enable_hsi48() set CRS_CR_AUTOTRIMEN but left CRS_CR_CEN clear.
AUTOTRIMEN only instructs the hardware to apply corrections derived from
the frequency error counter; the counter itself is enabled by CEN.  With
CEN clear no error measurement is ever produced and the HSI48 TRIM value
stays at its reset default, so the oscillator is never disciplined to the
synchronisation source.

Set both bits, matching the value the STM32 ROM bootloader programs when
it runs its own crystal-less USB stack (CRS_CR = 0x1a60, i.e.
CEN | AUTOTRIMEN with a trimmed TRIM field).

This file is shared by the M0 STM32 parts (F0, G0, C0, L0); all of them
require CEN for automatic trimming to function.

Assisted-by: GitHub Copilot:claude-opus-5
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
STM32_HSI48_SYNCSRC was SYNCSRC_NONE, which makes stm32_enable_hsi48()
return before configuring the CRS at all.  HSI48 then free runs at its
untrimmed factory frequency, which is not accurate enough for USB full
speed operation.

The board has no CRS_SYNC pin wired and does not fit an LSE crystal for
this purpose, so the USB start of frame packet is the available
synchronisation source.  This is the intended configuration for
crystal-less USB on this part.

Assisted-by: GitHub Copilot:claude-opus-5
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
vrefint_enable() sets SYSCFG_CFGR3_ENBUFVREFINTHSI48 in its local copy
of the register value but never writes that value back to the hardware.
The VREFINT reference for the HSI48 scaler is therefore never enabled.

Only the earlier ENVREFINT update reaches the register, so VREFINT
itself is enabled while the HSI48 reference is not.  HSI48 then runs
without its voltage reference and the 48MHz clock is not accurate enough
to be used by the USB device controller.

Write the register after applying the HSI48 reference bit.

Assisted-by: GitHub Copilot:claude-opus-5
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Size: S The size of the change in this PR is small Board: arm labels Aug 20, 2026
@github-actions

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@xiaoxiang781216
xiaoxiang781216 merged commit a5477dc into apache:master Aug 20, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Board: arm Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants