nRF54L15: SERIAL20/SERIAL21 (16 MHz PERI) peripherals init OK but produce no clock/output; SERIAL00 and SERIAL30 work fine

On a custom nRF54L15 (QFAA) board, SERIAL20 and SERIAL21 initialize successfully (device_is_ready() = true) but drive no pin activity / no bus traffic at all. On the same build/chip, SERIAL00 (SPIM NOR flash) and SERIAL30 (TWIM PMIC + temp sensor) work perfectly, and BLE + the system timer run fine. The failure splits cleanly by peripheral-power domain: the 16 MHz "20/21/22" group is dead; high-speed (00) and low-power (30) are healthy.

Environment. nRF54L15 QFAA app core · Zephyr v4.3.0 (bare Zephyr) · nRF VDD = 1.8 V from external PMIC (BUCK1), vregmain DCDC · HFXO 32 MHz load-capacitance-femtofarad=15000 · LFXO 32.768 kHz =9000, K32SRC_XTAL.

Works vs dead

Peripheral Instance Use Result
SPIM00 SERIAL00 (high-speed) NOR flash (SFDP+JEDEC) WORKS
TWIM30 SERIAL30 (low-power) nPM1304 + TMP117 WORKS
UARTE20 SERIAL20 (16 MHz PERI) console TX P1.04 / RX P1.05 DEAD
SPIM21 SERIAL21 (16 MHz PERI) BMI270 + AS7058 DEAD

Radio/BLE works (HFXO oscillates); LED/GRTC run (LFCLK runs).

Cleanest evidence — UARTE20: device_is_ready(uart20)=true; a raw uart_poll_out(0x55) loop in an early SYS_INIT (bypassing the shell) yields nothing; a logic analyzer on the SoC TX pin (P1.04) shows it park idle-high (1.8 V) and never toggle across a full boot. So the pin is claimed/routed but the peripheral never clocks a transfer. poll_out returns (doesn't hang).

Ruled out: pin routing (P1.04/05 valid for UARTE20, no conflicts) · instance collision (spi20/i2c20 disabled) · pinctrl (idle-high seen) · Zephyr init (ready=true) · RAM/EasyDMA (same FLPR-reclaim firmware runs fine on a sibling board that consoles over SERIAL30) · LF clock source (tried XTAL and RC) · board HW (LA on the SoC pin; two boards identical).

Questions: (1) What clocks/powers the SERIAL20/21/22 group, and what makes them pass device_is_ready() yet emit no clock while SERIAL00/30 work? (2) Any power-domain enable or clock request specific to the 20/21/22 group we could be missing at SoC/DT/Kconfig level? (3) Relation to VDD=1.8 V / DCDC vregmain? (4) Known errata matching this? Plus: which CLOCK/POWER registers to read to confirm the 20/21/22 domain is actually clocked.

  • Errata 39 looked like a strong match on the domain split, so I dug in, but I don't think the workaround resolves it, and I'd like to confirm whether 39 is actually the cause.

    The errata's runtime gate returns true on our die. Reading the FICR words nrf54l_errata_39() checks (via devmem over a working BLE shell):

    • 0x00FFC340 = 0x1C
    • 0x00FFC344 = 0x02

    Per nrf54l_erratas.h that hits case 0x02: return true, so nrf54l_errata_39() == true. We're on nrfx 3.12.1 (which contains the anomaly-39 CLOCK workaround), and NRF54L15_XXAA makes NRF54L_ERRATA_39_ENABLE_WORKAROUND = 1. So the PLLSTART-before-XOSTART pairing in nrfx_clock's HFCLK-start path should already be firing — and clock_control_nrf.c (CONFIG_CLOCK_CONTROL_NRF=y, Zephyr v4.3.0) does call nrfx_clock_hfclk_start(). BLE works, which means that HFCLK path is exercised.

    Despite that, the peripheral domain is dead. Symptom, precisely:

    • UARTE20 (console, TX P1.04): device_is_ready() == true, but a raw uart_poll_out() produces nothing. A logic analyzer on the SoC TX pin shows it park at idle-high (1.8 V) and never toggle across a full boot. poll_out returns (doesn't hang).
    • SPIM21 (BMI270/AS7058): chip-ID reads fail at init → not ready.
    • SPIM00 (SERIAL00) and TWIM30 (SERIAL30) work perfectly (ext NOR flash + PMIC/TMP117). Radio/BLE and GRTC run fine.

    Things we tried, all with SERIAL20/21 still dead:

    1. Relying on the automatic nrfx workaround (gate confirmed true, above).
    2. Explicit nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART) kept running, at both PRE_KERNEL_1 and POST_KERNEL (after clock_control init), before first CPU idle.
    3. Holding HFCLK on permanently via onoff_request(z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF), …) so the XO never restarts unpaired during sleep.

    Environment: nRF54L15 QFAA, app core, VDD = 1.8 V from an external PMIC (DCDC vregmain), HFXO 15 pF / LFXO 9 pF crystals, K32SRC_XTAL. Bare Zephyr v4.3.0 (no NCS).

    Questions:

    1. With nrf54l_errata_39() returning true and the nrfx pairing active, why would UARTE20/SPIM21 still get no clock? Is the nrfx_clock HFCLK-start pairing sufficient, or does the errata require something else (e.g., pairing every automatic XO restart, or a specific sleep/PM config)?
    2. Is there additional PERI-domain (SERIAL20/21/22) clock or power-domain setup required on nRF54L15 that the MCU-domain (SERIAL00) and low-power-domain (SERIAL30) instances don't need — something NCS does implicitly that bare Zephyr does not?
    3. Is this consistent with errata 39 at all, or does "peripheral inits (device_is_ready true) but never clocks its output, while SERIAL00/SERIAL30 work" point to a different root cause?
    4. Which CLOCK/POWER/LRCCONF registers should we dump (we can read live via devmem) to confirm the PLL and the PERI-domain clock state?
  • Update: we measured the clock state directly via devmem over BLE. The HFXO and PLL are both running:

    • CLOCK.XO.STAT (0x5010E40C) = 0x10001 → XO STATE = running
    • CLOCK.PLL.RUN (0x5010E428) = 0x1 → PLL running
    • CLOCK.PLL.STAT (0x5010E42C) = 0x10000

    So this does not appear to be errata 39 — the PLL is up, yet UARTE20/SPIM21 still get no clock/output while SPIM00 (SERIAL00) and TWIM30 (SERIAL30) work. device_is_ready() is true for UARTE20 and its TX pin sits idle-high but never toggles.

    Revised question: with HFXO and PLL both confirmed running, and MCU-domain (SERIAL00) + low-power-domain (SERIAL30) peripherals fully functional, what makes the PERI-domain SERIAL20/21/22 peripherals get no clock? Is there a per-domain clock-source request (LRCCONF REQCLKSRC?), a power-domain enable, or a peripheral-clock step specific to the 20/21/22 instances that bare Zephyr v4.3.0 might not be performing (that NCS does implicitly)? Which CLOCK/LRCCONF/POWER registers should we read to see whether the PERI-domain PCLK is actually being requested/delivered?

  • Further narrowing — the peripheral is enabled and configured, just unclocked.

    Following up on the PLL/XO measurements: I read the UARTE20 peripheral's own state on the affected (dev) build. The peripheral is fully up, it simply produces nothing:

    • UARTE20.ENABLE (0x500C6500) = 0x8 → peripheral enabled.
    • device_is_ready() = true; pinctrl applied (TX P1.04 sits at UARTE idle-high).
    • Raw uart_poll_out()  zero toggling on the SoC pin (logic analyzer directly on P1.04). poll_out returns, doesn't hang.

    Combined with the earlier reads — CLOCK.XO.STAT = 0x10001 (XO running), CLOCK.PLL.RUN = 0x1, CLOCK.PLL.STAT = 0x10000 (PLL running) — the picture is now unambiguous:

    The SERIAL20/21/22 (PERI-domain, 16 MHz) instances are ENABLED and CONFIGURED, and the HFXO + PLL are RUNNING, yet the peripherals receive no clock and emit nothing — while SERIAL00 (MCU domain) and SERIAL30 (low-power domain) work perfectly on the same build.

    So this isn't clock generation, the errata-39 workaround (confirmed active), the peripheral being disabled, pins, or config — it's specifically PERI-domain clock delivery/gating to the 20/21/22 group.

    Questions:

    1. With the peripheral enabled and the PLL/HFXO running, what step actually delivers the 16 MHz PCLK to SERIAL20/21/22? Is there a per-domain clock-source request (LRCCONF REQCLKSRC), a power-domain enable, or a peripheral-clock request that bare Zephyr v4.3.0's clock_control_nrf doesn't perform but NCS does?
    2. Which LRCCONF / CLOCK / POWER registers should I read (I can dump them live over a working BLE shell) to confirm whether the PERI-domain PCLK is being requested and delivered?
    3. Anything specific to running the part at VDD = 1.8 V with DCDC vregmain that would leave the PERI-domain peripheral clock ungated while the MCU/LP domains are fine?
  • The debug rx and tx pins were labeled backwards in the schematic...

Related