Summary
I'm trying to bring up two external MCP2515 CAN controllers (Waveshare 2‑CH CAN HAT, confirmed working on a genuine Raspberry Pi) on the FRDM‑IMX93 board's P11 EXPI header, using LPSPI3 on GPIO_IO08‑11 (the pins that match the Raspberry‑Pi‑compatible header positions per UM12181 Table 21).
After fixing every device‑tree‑level issue I could find (power, pinmux, chip‑select, pinctrl‑assert‑gpios), the SPI clock (SCK) never toggles on the physical header pin, and a direct register read of the LPSPI3 block causes a Bus error. A different, known‑working peripheral (LPUART1) reads back fine from the same address space. This points to a resource/bus access restriction (RDC or similar) rather than anything fixable from the Linux device tree.
Board / software
- Board: FRDM‑IMX93 (NXP), SoC: MIMX9352CVVXMAB
- BSP: NXP i.MX Release Distro, kernel 6.18.2-1.0.0-gf49f45233f7b
- Target peripheral: lpspi3 (spi@42550000, aliased spi2 in /aliases, real dts label confirmed via /proc/device-tree/__symbols__ to be lpspi3)
- External device under test: 2× MCP2515 (Waveshare 2‑CH CAN HAT) on CS0/CS1
What's already confirmed working (ruled out)
- Power to the header. reg_vexp_3v3 / reg_vexp_5v are regulator-fixed nodes that default to disabled (regulator_summary showed use=0). Added regulator-always-on + regulator-boot-on via overlay fragments targeting ®_vexp_3v3 / ®_vexp_5v (confirmed real labels via __symbols__). Verified 3.3 V / 5 V physically present on P11 afterwards.
- Pinmux. GPIO_IO08‑11 correctly muxed to LPSPI3_PCS0/SIN/SOUT/SCK using the official macros from imx93-pinfunc.h. input_reg/input_val are 0x0000/0x0 for these three functions, so no DAISY‑chain select is required (ruled out via macro inspection, no separate register needed).
- Chip‑select. cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>, <&gpio2 7 GPIO_ACTIVE_LOW>; (bit‑banged GPIO CS, matching NXP's own upstream board‑support pattern for this node). Confirmed via cat /sys/kernel/debug/gpio and multimeter/logic‑analyzer: CS0 toggles correctly and physically reaches the header pin.
- pinctrl-assert-gpios. NXP's own upstream &lpspi3 reference for this board includes pinctrl-assert-gpios = <&pcal6408 0 GPIO_ACTIVE_HIGH>; (this line does not appear in most public guides, only in the board's own dts patch). Added it; confirmed via /proc/device-tree/__symbols__ that pcal6408 resolves, and via cat /sys/kernel/debug/gpio that the GPIO is asserted (out hi) once lpspi3's default pinctrl state is requested.
- Overlay applies cleanly. No FDT_ERR_NOTFOUND from U‑Boot's fdt apply; /sys/bus/spi/devices/ shows spi2.0 and spi2.1 registered by the SPI core.
- ERR051608 (LPSPI TCR[PRESCALE] errata). Checked spi-fsl-lpspi.c history — the fix (prescale_max = 1 for fsl,imx93-spi) landed in mainline Aug 2024 / stable 6.6.51 & 6.10.10 Sept 2024, well before this kernel version (6.18.2), and the compatible string (fsl,imx93-spi) is already present in the base board dts, so the driver should auto‑apply this limit. Not ruled out with certainty (no direct register confirmation of the actual PRESCALE field written), but timeline strongly suggests it's already handled.
The actual symptom
With CS0/CS1, MISO, MOSI, SCK probed on the physical P11 pins (multimeter, then a logic analyzer at 10–20 MSa/s triggered on CS0 falling edge) while continuously retrying a transfer (spidev_test in a loop, and separately by repeatedly forcing mcp251x re‑probe via echo spi2.0 > /sys/bus/spi/drivers/mcp251x/bind):
- CS0 toggles. Confirmed on both multimeter and logic analyzer.
- SCK never toggles. Flat, no activity, regardless of continuous transfer attempts.
- MOSI never toggles.
- Both MCP2515s fail identically: mcp251x spi2.0/spi2.1: MCP251x didn't enter in conf mode after reset / Probe failed, err=110 (ETIMEDOUT) — i.e. the driver's own SPI‑level reset+read‑CANSTAT sequence gets no response, consistent with SCK never leaving the SoC.
Root cause narrowed to clock / bus access, not device tree
# clk_enable_count is 0 despite the controller actively being used
$ cat /sys/kernel/debug/clk/clk_summary | grep -i lpspi3
lpspi3_root 0 0 0 50000000 0 0 50000 N deviceless no_connection_id
lpspi3 0 0 0 50000000 0 0 50000 N 42550000.spi perlpspi3's functional (per) clock shows enable_count=0 even while 42550000.spi (the real, bound consumer) is actively attempting transfers in a loop — it isn't simply "unused" (compare to lpspi1/2/4, which are deviceless and also show 0, so that alone isn't conclusive).
The decisive test — direct register read via devmem:
$ devmem 0x44380000 32 # LPUART1 base — known-good peripheral
0x04040007
$ devmem 0x42550000 32 # LPSPI3 base (VERID register)
Bus error (core dumped)
A completely unrelated, working peripheral (LPUART1, used for the debug console) reads back fine from the same CPU/bus context. LPSPI3's base address faults on a plain 32‑bit read, before any clock‑gating or pinmux consideration even matters. This looks like a Resource Domain Controller (RDC) — or similar access‑control mechanism — blocking the Cortex‑A55 (Linux) domain from this peripheral's address range at the hardware level, independent of anything configurable from the Linux device tree.
Questions for NXP
- Is LPSPI3 on the FRDM‑IMX93 intentionally reserved for another domain (e.g. Cortex‑M33 / secure world) in the board's default RDC configuration, making it unusable from Linux without additional SPL/ATF/TF‑A level reconfiguration?
- If so, is there a documented way to reassign LPSPI3 bus access to the Cortex‑A55 non‑secure domain (RDC config in U‑Boot SPL, or an OP‑TEE/TF‑A change), or is this simply not supported as an external SPI on this board's P11 header despite GPIO_IO08‑11 being broken out there?
- Is this specific to my board/kernel build, or a known characteristic of the default FRDM‑IMX93 BSP? A reference imx93-11x11-frdm-lpspi.dts (analogous to imx93-11x11-evk-lpspi.dts for the EVK) would be very helpful if one exists.
How to reproduce
- Base board dtb: stock imx93-11x11-frdm.dtb shipped with the i.MX Release Distro image (unmodified NXP lpspi3 + pcal6408 + reg_vexp_3v3/reg_vexp_5v nodes — all confirmed present via __symbols__).
- Apply the overlay described above (regulator always‑on, pinctrl + cs‑gpios + pinctrl‑assert‑gpios on &lpspi3).
- Bind spidev (built‑in, CONFIG_SPI_SPIDEV=y) manually to spi2.0:
echo spidev > /sys/bus/spi/devices/spi2.0/driver_override
echo spi2.0 > /sys/bus/spi/drivers/spidev/bind
- spidev_test -D /dev/spidev2.0 -s 1000000 -v — transfer "succeeds" (no I/O error) but RX data is not a loopback of TX even with MOSI/MISO physically shorted.
- devmem 0x42550000 32 → Bus error.
Happy to provide the full overlay source, dmesg, and clk_summary/gpio dumps if useful.