Skip to content

MT-158113: imx8mm-evk-spi-transport devicetree overlay - #46

Open
MT-MichaelLoh wants to merge 35 commits into
MultiTracksDotCom:developfrom
MT-MichaelLoh:MT-158113
Open

MT-MichaelLoh wants to merge 35 commits into
MultiTracksDotCom:developfrom
MT-MichaelLoh:MT-158113

Conversation

@MT-MichaelLoh

@MT-MichaelLoh MT-MichaelLoh commented Aug 19, 2026

Copy link
Copy Markdown

MT-158113

Summary

Scope grew 2026-08-19: this PR originally added just the devicetree overlay, with the driver itself living out-of-tree in the firmware repo. The driver's Linux adapter (drivers/spi/spi-mt-transport/) is now back in-tree here instead -- it's genuinely GPL (calls GPL-only-exported kernel symbols core to its function: devm_gpiod_get, gpiod_get_value/gpiod_set_value/gpiod_to_irq, spi_async, spi_slave_abort, dev_err_probe, sysfs_emit -- confirmed against the actual built kernel source, not assumed), and the firmware repo's CODING_STANDARDS.md §7 forbids GPL/LGPL code there. Found by Copilot's review on firmware PR #785; relicensing wasn't an option since MODULE_LICENSE("GPL") is required for these symbols to resolve at all, not a style choice.

What's in this PR now:

  • arch/arm64/boot/dts/freescale/imx8mm-evk-spi-transport.dts (+ Makefile entry) -- the devicetree overlay: multitracks,spi-transport binding on ECSPI2 (SCLK/MOSI/MISO/SS0) plus multitracks,nss-gpios = <&gpio5 13 GPIO_ACTIVE_HIGH>; multitracks,nrdy-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>; properties for the protocol's NSS/NRDY handshake lines. Unchanged from this PR's original scope.
  • drivers/spi/spi-mt-transport/ -- the Linux Host-role driver adapter (spi_mt_transport_drv.c glue, spi_transport_hw_linux.*/spi_transport_os_linux.* HW/OS adapters, kernel-compat/* shims for the portable core's hosted-C11 headers under the kernel's -nostdinc build), moved here verbatim from the firmware repo, content unchanged. Plus the matching drivers/spi/Kconfig (CONFIG_SPI_MT_TRANSPORT) and drivers/spi/Makefile hooks, and arch/arm64/configs/imx_v8_defconfig's =m line.

What's deliberately NOT in this PR: the portable protocol core (spi_transport.c/spi_transport_channel.c/spi_transport_frame.c/spi_transport_crc16.c/spi_transport_hw.c + their headers). That stays the firmware repo's single source of truth -- it's plain C11, calls no kernel APIs, and doesn't need to be GPL. imx8mmini-bb-evk's meta-mt-transport-evk/recipes-kernel/linux-imx/linux-imx_%.bbappend fetches it fresh from the firmware repo at Yocto build time (a second, separately-named SRC_URI entry) and stages it into drivers/spi/spi-mt-transport/core/ via a do_patch[postfuncs] hook -- gitignored here, never committed, so there's no duplication between the two repos. This mirrors the original pre-restructuring layout's directory shape exactly, just populated at build time instead of vendored into git history.

Test plan

  • bitbake -c patch linux-imx -f -- confirmed the core-staging postfunc correctly populates drivers/spi/spi-mt-transport/core/ (5 .c files, 6 headers) in the shared kernel source tree.
  • bitbake linux-imx -- in-tree driver compiles clean, packages automatically as kernel-module-spi-mt-transport-<kernel-ver> (standard in-tree convention -- no manual IMAGE_INSTALL entry needed, unlike the old out-of-tree recipe).
  • bitbake imx-image-core -- fresh manifest confirmed the module package present.
  • Flashed real hardware: uname -r confirms the new kernel, modprobe spi_mt_transport probes cleanly, link_state: connected, event_counters clean.
  • Write + read smoke test against the real STM32F723-DISCO Client: write near-instant (ring-queue design unaffected by this move), read delivered real traffic-generator bytes, delivery confirmed via the STM32 peer's mismatch counter incrementing exactly once per write.
  • Full fault-injection matrix, two multi-hour soaks, and the TX throughput rework were all already hardware-validated before this move (see firmware #785 and #786) -- this move only relocates files, no functional change to the driver itself.

🤖 Generated with Claude Code

…m EVK

Kernel port of the SPI transport protocol (firmware-common/spi-transport,
vendored from MultiTracksDotCom/firmware @ 9923f343) as a Host-role SPI
platform driver, for kernel<->kernel SPI transport testing between the
iMX8MM-BB EVK and an STM32F723-DISCO client. Binds to ecspi2 via a new
imx8mm-evk-spi-transport.dts overlay, with NSS/NRDY handshake lines owned
directly by the driver (see dts comment) rather than SPI-core cs-gpios.
…l builds

ccflags-y used -I\$(src)/core/include, which resolves against \$(objtree)
under Yocto's O= kernel builds. These vendored core/ headers only exist in
the source tree, so every TU failed with "spi_transport/spi_transport.h: No
such file or directory". Prefix with $(srctree) to point at the actual
source location.
The vendored core also includes these standard hosted-C11 headers, none of
which resolve under -nostdinc (this cross-compiler's own freestanding
headers aren't on the search path either). Same pattern as the existing
string.h shim: redirect to the kernel's own equivalents.
…imeout

mt_hw_abort() was a log-only stub, but the core's disconnect watchdog
(SPI_TRANSPORT_DISCONNECT_MS, 1500ms) fires before spi_imx_calculate_timeout()'s
unconditional >=2000ms floor can possibly elapse. That let a retry
reinitialize the shared ctx->msg/ctx->xfer via mt_hw_transfer_start() while
spi_imx was still blocked inside its own wait_for_completion_timeout()
referencing that same memory -- corrupting the SPI core's message queue and
scatterlist state. Reproduced live on the EVK as a NULL deref in
spi_imx_dma_transfer()'s sg_last(), triggered by repeated DMA TX timeouts
with no peer wired up yet.

pAbort() has no return value (must be safe to call whether or not anything
is armed, and the core proceeds regardless), so the only correct fix is to
make it actually block until the in-flight transfer's completion has fired
(or spi-imx's own bounded recovery should have finished), via a completion
tracked across mt_hw_transfer_start()/mt_hw_spi_complete()/mt_hw_abort().
mt_transport_event_callback() only logged via dev_dbg(), invisible in
dmesg without dynamic debug explicitly enabled -- confirmed live during
hardware fault-injection testing against a real STM32F723-DISCO Client:
zero log output across ~50 real connect/disconnect cycles and dozens of
DMA-failure/timeout injections, even though the callback was firing
correctly the whole time.

Add per-event atomic counters and a new /sys/.../event_counters
attribute, field-named to match the STM32 Client harness's own [DBG]
conn=/disc=/hdrCrc=/payCrc=/seq=/dmaFail=/dmaTo= counters (see the
firmware repo's test/stm32-disco/app/), so a fault-injection run's
peer-side verdict (per that harness's docs/TestPlan.md) can actually be
read off this Host instead of only inferred from the absence of a crash.
…module

Deletes drivers/spi/spi-mt-transport/ (the driver plus its vendored copy
of the SPI transport core library) and the in-tree Kbuild/Kconfig hooks
(drivers/spi/Makefile's obj-y line, drivers/spi/Kconfig's
CONFIG_SPI_MT_TRANSPORT entry, imx_v8_defconfig's =m line).

The driver now builds as an out-of-tree Yocto kernel module, sourced
directly from the firmware repo's firmware-common/spi-transport/ (see
imx8mmini-bb-evk's meta-mt-transport-evk/recipes-kernel/spi-mt-transport/
spi-mt-transport_git.bb). This removes the real protocol implementation
from this more public-facing repo entirely, and removes the vendored-copy
duplication (previously tracked via core/PROVENANCE.md) -- the core is
now referenced in place from a single source of truth.

This branch's only remaining unique content vs. develop is the one
devicetree overlay (imx8mm-evk-spi-transport.dts) -- everything else
here is the pre-existing, driver-independent v6.6.36-vs-v6.18
kernel-version retarget.
Copilot AI lite review requested due to automatic review settings August 19, 2026 02:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@MT-MichaelLoh
MT-MichaelLoh marked this pull request as draft August 19, 2026 04:19
@MT-MichaelLoh
MT-MichaelLoh marked this pull request as ready for review August 19, 2026 16:54
@MT-MichaelLoh
MT-MichaelLoh marked this pull request as draft August 19, 2026 18:30
Reverses commit 2b60361's removal for the Linux adapter files
only, not the vendored core it also deleted -- CODING_STANDARDS.md §7
in the firmware repo forbids GPL/LGPL code there, and this adapter
genuinely calls GPL-only-exported kernel symbols (devm_gpiod_get,
gpiod_get_value/set_value/to_irq, spi_async, spi_slave_abort,
dev_err_probe, sysfs_emit) core to its own function, so it can't be
relicensed -- it has to live somewhere GPL is already the designated
home, which per that same policy doc is this repo.

The portable protocol core (firmware-common/spi-transport/src/*.c,
inc/spi_transport/*.h in the firmware repo) is unaffected and stays
put -- it's plain C11, calls no kernel APIs, and doesn't need to be
GPL. It's also deliberately NOT vendored back into drivers/spi/
spi-mt-transport/core/ here (that's exactly the duplication problem
the original vendored version had) -- core/ is populated at Yocto
build time instead, fetched fresh from the firmware repo by
imx8mmini-bb-evk's meta-mt-transport-evk linux-imx_%.bbappend, and
gitignored here so it never gets committed.
@MT-MichaelLoh
MT-MichaelLoh marked this pull request as ready for review August 19, 2026 21:19
Copilot AI review requested due to automatic review settings August 19, 2026 21:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Suppressed comments (4)

drivers/spi/spi-mt-transport/spi_transport_hw_linux.c:128

  • Use gpiod_get_value_cansleep() here since this runs in process context (tick thread) and may be backed by a sleep-capable GPIO provider.
static bool mt_hw_ready_read(void *pContext)
{
	struct mt_transport_hw_ctx *ctx = pContext;

	return gpiod_get_value(ctx->nrdy_gpiod) ? true : false;
}

drivers/spi/spi-mt-transport/spi_transport_hw_linux.c:109

  • This path is not in atomic context (it runs from the protocol tick thread / process context), so using the *_cansleep GPIO accessor is safer for GPIO controllers that may sleep. This avoids potential sleeping-in-atomic bugs if the GPIO descriptor ends up being backed by a sleep-capable provider.

This issue also appears on line 123 of the same file.

static void mt_hw_select_assert(void *pContext, bool high)
{
	struct mt_transport_hw_ctx *ctx = pContext;

	gpiod_set_value(ctx->nss_gpiod, high ? 1 : 0);
}

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:7

  • The file header still says the driver “Builds as an out-of-tree Yocto kernel module”, but it is now wired in-tree under drivers/spi/ (and selected via Kconfig/Makefile). Update this description so it matches the current build/integration model (including the fact that only the portable core is staged at build time).
 * SPI transport protocol. Builds as an out-of-tree Yocto kernel module (see
 * imx8mmini-bb-evk's meta-mt-transport-evk recipes-kernel/spi-mt-transport/)
 * against the portable protocol core at ../../{,../inc/spi_transport}/ in
 * this same source tree -- referenced in place, not vendored/copied.

drivers/spi/spi-mt-transport/spi_transport_hw_linux.c:218

  • This handler is registered as a threaded IRQ (primary handler is NULL), so it runs in a context where sleeping is allowed. Using gpiod_get_value_cansleep() makes the GPIO read safe even if the GPIO provider can sleep.
irqreturn_t mt_transport_hw_linux_nrdy_irq(int irq, void *dev_id)
{
	struct mt_transport_hw_ctx *ctx = dev_id;
	bool high = gpiod_get_value(ctx->nrdy_gpiod) ? true : false;

Comment thread drivers/spi/spi-mt-transport/Makefile
Comment thread arch/arm64/configs/imx_v8_defconfig Outdated
- mt_hw_select_assert()/mt_hw_ready_read()/mt_transport_hw_linux_nrdy_irq()
  used plain gpiod_get_value()/gpiod_set_value() from non-atomic contexts
  (tick kthread, and a threaded IRQ handler that can sleep by definition)
  -- switched to the _cansleep variants, correct per the GPIO consumer
  API's own contract for sleepable callers, even though this board's
  actual GPIO controller happens to be non-sleeping today.
- Updated spi_mt_transport_drv.c's file banner: still described the
  driver as an out-of-tree Yocto module with a relative-path core
  reference and a relative ProtocolSpec.md path, both stale now that it's
  in-tree here with the core fetched from a separate repo.
- Documented the Makefile/defconfig's build-time dependency on the
  Yocto-staged core/ directory explicitly, so enabling
  CONFIG_SPI_MT_TRANSPORT outside the imx8mmini-bb-evk BSP pipeline fails
  with an understood, expected error instead of a cryptic one.

Found by Copilot's review of PR MultiTracksDotCom#46 after the adapter moved here.
Copilot AI review requested due to automatic review settings August 19, 2026 21:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Suppressed comments (5)

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:335

  • This uses '///' (C++-style) comments; prefer kernel-style /* */ comments for in-tree code.
/// @brief True if a new slot can be enqueued. Caller must already hold
///        tx_lock -- occupied total is tx_queued_count (not-yet-submitted
///        slots) plus one more if a slot is currently in flight
///        (tx_in_flight_idx >= 0), since that slot is still reserved even
///        though it doesn't count toward tx_queued_count.

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:351

  • This uses '///' (C++-style) comments; prefer kernel-style /* */ comments for in-tree code.
/// @brief wait_event_interruptible()'s condition check only -- takes and
///        releases tx_lock itself since it must be callable without
///        already holding it. mt_transport_misc_write()'s own room check
///        below calls mt_transport_tx_room_locked() directly instead
///        (already holding the lock at that point) rather than this
///        wrapper, and deliberately so: that check has to stay under the
///        *same* lock acquisition that immediately follows (the enqueue),
///        otherwise a second writer could take the now-free slot in the
///        gap between checking and re-locking.

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:554

  • gpiod_to_irq() can return -EPROBE_DEFER (or other negative errors). Treating all <=0 values as "no IRQ" can prevent proper probe deferral and permanently disables the IRQ optimization when the IRQ provider isn’t ready yet.
	priv->nrdy_irq = gpiod_to_irq(priv->nrdy_gpiod);
	if (priv->nrdy_irq > 0) {
		ret = devm_request_threaded_irq(dev, priv->nrdy_irq, NULL,
						 mt_transport_hw_linux_nrdy_irq,
						 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
							 | IRQF_ONESHOT,
						 DRIVER_NAME "-nrdy", &priv->hw_ctx);
		if (ret)
			dev_dbg(dev, "no NRDY IRQ (%d) -- falling back to tick-poll only\n", ret);
	} else {
		dev_dbg(dev, "NRDY line has no IRQ -- tick-poll only\n");
	}

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:234

  • This block uses '///' (C++-style) comments. The kernel generally expects /* / (or kernel-doc /* */ when appropriate) and checkpatch will flag '//' comments outside of the SPDX line.

This issue also appears in the following locations of the same file:

  • line 331
  • line 343
/// @brief Submit the oldest queued TX slot (if any) via spiTransportSend().
///        A success return proves the *previous* in-flight slot (if any) is
///        now done -- the core only accepts a new send once the last one's
///        final chunk is confirmed -- so that previous slot is freed right
///        here, not after a guessed timeout. Called once per tick thread

arch/arm64/boot/dts/freescale/imx8mm-evk-spi-transport.dts:37

  • This DTS introduces a new compatible string ("multitracks,spi-transport") and new vendor properties (mt-nss-gpios/mt-nrdy-gpios) but doesn’t add a devicetree binding document under Documentation/devicetree/bindings/. Without it, dtbs_check can’t validate the schema for these properties.
	mt_transport0: spi@0 {
		compatible = "multitracks,spi-transport";
		reg = <0>;
		spi-max-frequency = <500000>; /* matches mt-connect.dts; revisit once link timing is measured */

		mt-nss-gpios = <&gpio5 13 GPIO_ACTIVE_HIGH>;
		mt-nrdy-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
	};

Comment thread arch/arm64/configs/imx_v8_defconfig Outdated
- Converted three C++-style '///' Doxygen comment blocks in
  spi_mt_transport_drv.c to standard kernel /* */ style -- checkpatch
  flags '//' comments outside the SPDX line, and this is in-tree code
  now.
- gpiod_to_irq() can return -EPROBE_DEFER if the backing IRQ chip isn't
  ready yet, not just "no IRQ available" -- the old `if (nrdy_irq > 0)`
  check silently and permanently disabled the IRQ optimization on that
  race instead of deferring the whole probe(). Now propagates
  -EPROBE_DEFER explicitly.
- Removed CONFIG_SPI_MT_TRANSPORT=m from imx_v8_defconfig itself --
  defconfigs should be buildable from a plain checkout, and this one
  can't be (core/ only exists via Yocto staging). Moved to a proper
  Yocto config fragment instead (imx8mmini-bb-evk's
  meta-mt-transport-evk/recipes-kernel/linux-imx/files/
  spi-mt-transport.cfg, wired via SRC_URI).
- Added a devicetree binding doc
  (Documentation/devicetree/bindings/spi/multitracks,spi-transport.yaml)
  for the multitracks,spi-transport compatible string and its
  mt-nss-gpios/mt-nrdy-gpios properties, so dtbs_check can validate the
  schema.
- Fixed two more stale references in the .dts file's own comment
  ("vendored core", gpiod_get/set_value() without _cansleep).

Found by Copilot's second review pass on PR MultiTracksDotCom#46.
Copilot AI review requested due to automatic review settings August 19, 2026 21:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Documentation/devicetree/bindings/spi/multitracks,spi-transport.yaml:55

  • With allOf schemas in place, additionalProperties: false tends to be too strict because it can reject properties coming from the referenced schemas; unevaluatedProperties: false is the more typical pattern for DT schema composition in this tree (e.g. Documentation/devicetree/bindings/spi/spi-mux.yaml:52). Also, spi-max-frequency is generally expected to be specified for SPI peripherals; requiring it improves validation.
required:
  - compatible
  - reg
  - mt-nss-gpios
  - mt-nrdy-gpios

additionalProperties: false

drivers/spi/spi-mt-transport/Makefile:16

  • This module’s Makefile hard-requires a non-versioned, gitignored core/ directory that is absent from the tree, so enabling CONFIG_SPI_MT_TRANSPORT outside the Yocto staging flow fails with an opaque "No rule to make target" error. Even if the Yocto path is the intended one, it’s useful to fail fast with a clear, actionable error message when the core hasn’t been staged.
# hook *before* this Makefile ever runs. Enabling CONFIG_SPI_MT_TRANSPORT
# outside that pipeline will fail with a generic "No rule to make target
# core/spi_transport.o" -- that's expected, not a bug; get the core staged
# first (or just build via bitbake, which always does this automatically).
obj-$(CONFIG_SPI_MT_TRANSPORT) += spi-mt-transport.o

Documentation/devicetree/bindings/spi/multitracks,spi-transport.yaml:25

  • Building this schema with additionalProperties: false but without referencing the common SPI peripheral properties prevents use of standard SPI DT flags (e.g. spi-cpha, spi-cpol, etc.) and diverges from common SPI binding patterns (see Documentation/devicetree/bindings/spi/spi-mux.yaml:32). Consider adding a $ref to spi-peripheral-props.yaml so the binding can validate/allow standard SPI properties consistently.

This issue also appears on line 49 of the same file.


properties:

- multitracks,spi-transport.yaml: additionalProperties: false without
  referencing spi-peripheral-props.yaml prevented standard SPI
  properties (spi-cpha, spi-cpol, etc.) and diverged from this tree's
  own SPI binding convention (see spi-mux.yaml). Switched to allOf +
  $ref: spi-peripheral-props.yaml# + unevaluatedProperties: false, and
  added spi-max-frequency to required (generally expected for SPI
  peripherals).
- Makefile: added an explicit $(error ...) check for core/'s presence
  before the obj-y list, so a build attempted outside the Yocto
  pipeline fails immediately with a clear message instead of an opaque
  "No rule to make target core/spi_transport.o" deep in the build.

Found by Copilot's third review pass on PR MultiTracksDotCom#46.
Copilot AI review requested due to automatic review settings August 19, 2026 22:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:556

  • gpiod_to_irq() can legally return 0 as a valid IRQ number; checking > 0 will incorrectly treat IRQ0 as “no IRQ” and skip the threaded handler. Kernel drivers generally treat only negative values as errors (e.g. drivers/pps/clients/pps-gpio.c:175-180).
	if (priv->nrdy_irq == -EPROBE_DEFER)
		return dev_err_probe(dev, -EPROBE_DEFER, "NRDY IRQ not ready yet\n");
	if (priv->nrdy_irq > 0) {
		ret = devm_request_threaded_irq(dev, priv->nrdy_irq, NULL,

Comment thread drivers/spi/spi-mt-transport/spi_mt_transport_drv.c
- gpiod_to_irq() can legally return 0 as a valid IRQ number on some
  platforms/irqdomains; the probe()'s "if (nrdy_irq > 0)" check treated
  IRQ0 as "no IRQ available" and silently fell back to tick-poll only.
  Changed to ">= 0" per gpiod_to_irq()'s own contract (only negative
  values mean "no IRQ"), matching the convention in
  drivers/pps/clients/pps-gpio.c.

- mt_transport_remove() had a teardown race: the NRDY threaded IRQ is
  devm-managed, so it's only actually freed by the driver core *after*
  remove() returns -- it stays live through misc_deregister()/
  kthread_stop()/spiTransportStop(), and a real STM32 Client peer
  doesn't stop toggling NRDY just because this side is unbinding. If it
  fired during that window, mt_transport_hw_linux_nrdy_irq() could call
  priv->hw.pOnReadyEvent() into a core that's mid-teardown. Added a
  nrdy_irq_requested flag (set only on successful IRQ registration) and
  an explicit devm_free_irq() as the first action in remove() --
  devm_free_irq() blocks until any in-flight threaded-handler
  invocation finishes, so it can't race an in-flight callback either.

- Makefile: the core/ fail-fast guard's $(wildcard $(src)/...) check
  was missing the $(srctree)/ prefix that this same file's ccflags-y
  line already documented as required for Yocto's out-of-tree (O=)
  builds -- caused a false-positive "core/ is not staged" failure even
  when core/ was genuinely present. Added the missing prefix.
Copilot AI review requested due to automatic review settings August 19, 2026 22:42
Copilot AI review requested due to automatic review settings September 2, 2026 20:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The miscdevice single-open enforcement uses an atomic decrement pattern that can go negative under concurrent opens, potentially leaving the device permanently unopenable.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:424

  • The single-open enforcement uses atomic_dec_and_test()+atomic_inc(), which can drive available negative under concurrent failed opens (e.g., two -EBUSY opens racing while the device is already open). That can leave available stuck < 0 and make the device permanently unopenable until module reload. Use an atomic cmpxchg-based claim instead, which doesn't modify the counter on failure.
  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

…e stale-reuse theory

Fills ctx->xfer.rx_buf with 0x37 (a byte value the protocol never
legitimately sends) immediately before every spi_async() submission.
rx_buf is a single, fixed buffer reused across every transfer -- if a
completed transfer's CRC-failure dump still shows 0x37 anywhere, that
proves DMA never actually wrote those bytes (a short/partial transfer),
directly confirming or refuting the stale-buffer-reuse hypothesis from
the previous raw-dump capture.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 2, 2026 20:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The SPI completion callback currently reports xfer.len rather than the SPI core’s msg.actual_length, which can pass stale tail bytes to the transport core on short “successful” transfers.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread drivers/spi/spi-mt-transport/spi_transport_hw_linux.c
Neither wait_for_completion above proves the DMA engine actually
drained transfer->len bytes -- only that each channel's callback
fired. Checking dmaengine_tx_status()'s residue directly tests
whether short/partial DMA completions explain the CRC-corruption
investigation's symptom (frames with plausible-looking header/payload
content but a wrong header or payload CRC, isolated to the tail of
their respective checksummed regions).
Copilot AI review requested due to automatic review settings September 3, 2026 04:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The DTS nodes and binding example omit spi-no-cs; despite explicitly managing NSS outside the SPI core, which can allow unintended controller chip-select toggling and contradicts the intended handshake design.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

arch/arm64/boot/dts/freescale/imx8mm-evk-spi-transport.dts:38

  • This overlay deletes cs-gpios so NSS is managed by the driver via multitracks,nss-gpios, but the SPI device node does not set spi-no-cs; the SPI framework may still toggle the controller chip-select per-message, which undermines the stated design of keeping NSS under driver control across multi-step cycles. Add spi-no-cs to make the SPI core leave chip-select alone.
    arch/arm64/boot/dts/freescale/mt-connect.dts:537
  • The driver owns NSS via the separate multitracks,nss-gpios line and explicitly avoids cs-gpios, but this node does not set spi-no-cs; as a result the SPI framework may still try to assert/deassert the controller chip-select around each spi_message, which is at best misleading and can conflict with the protocol’s requirement to hold NSS across multi-step cycles. Add spi-no-cs to make the intent explicit and prevent unintended CS toggling.
    Documentation/devicetree/bindings/spi/multitracks,spi-transport.yaml:72
  • The binding describes NSS as being managed outside the SPI core (not cs-gpios), but the example node doesn’t show spi-no-cs; without it, the SPI framework may still try to toggle the controller chip-select around each transfer. Add spi-no-cs to the example so DTS authors don’t inadvertently enable CS toggling while also using multitracks,nss-gpios.
  • Files reviewed: 19/19 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread drivers/spi/spi-imx.c
Matches the STM32 side's commit a4a66d11 -- unconditional raw dumps
(not CRC-failure-gated) of the first 5 TX arms and first 5 RX
completions, for direct byte-for-byte comparison against the same
early exchange's fbs TX/RX dumps on that side.
Copilot AI review requested due to automatic review settings September 3, 2026 16:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new DMA residue instrumentation in drivers/spi/spi-imx.c uses dmaengine_submit() cookies without handling negative (error) returns, which can lead to invalid cookie use and unnecessary transfer timeouts.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

drivers/spi/spi-imx.c:1561

  • dmaengine_submit() can fail and return a negative cookie; with the new cookie tracking, a TX submit error would later be used in dmaengine_tx_status() and can also cause the subsequent wait_for_completion_timeout() to time out unnecessarily. Check dma_submit_error(tx_cookie) and terminate the already-armed RX channel before bailing out, similar to the existing desc_tx prep failure path.
	desc_tx->callback = spi_imx_dma_tx_callback;
	desc_tx->callback_param = (void *)spi_imx;
	tx_cookie = dmaengine_submit(desc_tx); /* MT-159369 bring-up instrumentation */
	reinit_completion(&spi_imx->dma_tx_completion);
	dma_async_issue_pending(controller->dma_tx);
  • Files reviewed: 19/19 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread drivers/spi/spi-imx.c
…arn/dev_err was saturating the console and causing the boot-time hang

mt_transport_event_callback() logged every single header/payload CRC
error, sequence gap, and DMA failure/timeout via plain dev_warn()/
dev_info() (changed from dev_dbg() during MT-158113 bring-up for
visibility during ~50 manually-supervised test cycles). Same for
mt_hw_spi_complete()'s slow-transfer warning and
mt_hw_transfer_start()'s still-in-flight rejection, both unconditional
per-frame/per-request.

Under a real link-quality storm (hundreds of events/sec instead of a
few dozen over a supervised session) this saturates
imx_uart_console_write(), which holds port.lock with local IRQs (and
its own RX-ready IRQ) disabled for the full synchronous, poll-driven
duration of each line at the console baud rate. Confirmed this is why
serial BREAK + magic sysrq got zero response during the hang -- not a
genuine deadlock, the RX path needed for that response was disabled by
this same code, repeatedly, with no gap. Also explains why softlockup/
hung-task detection produced nothing even after being enabled: their
own timer tick needs the same IRQs this was starving.

Switched all of these to their _ratelimited variants, matching the
precedent already set by this driver's own NRDY-read-failure logging
(dev_err_ratelimited in this same file). Counters (atomic_inc) stay
unthrottled -- only the console output is gated.
Copilot AI review requested due to automatic review settings September 5, 2026 20:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

mt_transport_misc_poll() currently can’t signal or wake writers (no tx waitqueue/EPOLLOUT), and there are also logging/perf concerns from leaving bring-up diagnostics in hot paths at high severity.

Review details

Suppressed comments (4)

Previously missed (3) — in code that hasn't changed since the last review.

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:661

  • mt_transport_misc_poll() only waits on rx_wq and never reports EPOLLOUT/EPOLLWRNORM or waits on tx_free_wq. As a result, userspace using poll()/select() to wait for write readiness will not be woken when TX space becomes available (and may block unexpectedly in write()). Add tx_free_wq to poll_wait() and report EPOLLOUT when the TX ring has room.
    drivers/spi/spi-mt-transport/spi_transport_hw_linux.c:124
  • The unconditional "RX raw" bring-up dump uses KERN_ERR severity even when there is no error. This will produce error-level log noise on every boot/module load (first few transfers) and can trigger monitoring/alerting. Consider using KERN_DEBUG (or a dev_dbg()-gated path) for this diagnostic-only dump.
    drivers/spi/spi-mt-transport/spi_transport_hw_linux.c:188
  • The unconditional "TX raw" bring-up dump uses KERN_ERR severity even when there is no error. This will emit error-level log lines during normal operation (first few transfers) and can confuse log triage. Consider using KERN_DEBUG (or a dev_dbg()-gated path) for this diagnostic-only dump.

drivers/spi/spi-imx.c:1591

  • This MT-159369 diagnostic block calls dmaengine_tx_status() and inspects dma_tx_state.residue after every successful spi-imx DMA transfer. This adds overhead in a hot path for all spi-imx users, and dma_tx_state.residue is only documented as meaningful for DMA_IN_PROGRESS/DMA_PAUSED (many DMA engines will report 0 for completed cookies), so it may provide false confidence. Consider gating this behind a debug option (or moving the residue/status logging into the existing timeout/error paths where the transfer is still in progress).
		/* MT-159369 bring-up instrumentation: both completions above
		 * only prove each channel's callback fired -- neither proves
		 * the engine actually drained transfer->len bytes. Check
		 * residue directly; a nonzero value here would mean the RX/TX
		 * buffer genuinely has fewer real bytes than the rest of this
  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

… on the edge directly

mt_transport_hw_linux_nrdy_irq() read the GPIO level and called
pOnReadyEvent() synchronously on every edge -- real per-transfer work
(hostArmTransferIfAcked()/hostIssueRequestIfReady(), frame build +
pTransferStart()/spi_async()) documented in the core as "a pure latency
optimization" over the tick-driven poll path, which is fully correct
and sufficient on its own.

Every threaded IRQ handler runs as SCHED_FIFO (kernel/irq/manage.c,
irq_thread() -> sched_set_fifo()), and this board's kernel disables
the normal RT-throttling cap (disable-rt-throttling.cfg) for real-time
audio. Doing real work here means a rapidly-toggling NRDY line during
a genuine link-quality storm turns into unbounded SCHED_FIFO CPU
consumption with nothing capping it -- the mechanism behind a
boot-time hang where even the softlockup/hung-task watchdogs (plain
SCHED_OTHER) never got to run, confirmed live.

NRDY is an edge-triggered GPIO used only to wake a consumer; its real
state lives in the level, not the edge. This handler now only wakes
the tick thread -- it neither reads the GPIO nor calls pOnReadyEvent()
-- which reads the *current* level itself via the existing
pReadyRead() poll path when it runs. No matter how fast NRDY bounces,
real work stays capped at the tick thread's own SCHED_OTHER pace
instead of scaling with edge frequency.
Copilot AI review requested due to automatic review settings September 5, 2026 21:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The miscdevice poll() path doesn’t report TX writability/readiness correctly, and the new DMA residue instrumentation should be guarded/ratelimited to avoid invalid status queries and log storms.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

drivers/spi/spi-mt-transport/spi_mt_transport_drv.c:660

  • mt_transport_misc_poll() only waits on rx_wq and never reports EPOLLOUT, so poll()/select() users can’t wait for TX queue space becoming available (writes may block even though the fd is writable). Register tx_free_wq with poll_wait() and set EPOLLOUT|EPOLLWRNORM when mt_transport_tx_has_room() is true.

drivers/spi/spi-imx.c:1602

  • The MT-159369 DMA residue instrumentation queries dmaengine_tx_status() using rx_cookie/tx_cookie without checking dma_submit_error(), and logs residue mismatches with dev_err() unthrottled. If submit failed the cookie may be invalid, and if residue is frequently nonzero this can flood the console (the same failure mode discussed elsewhere in this PR). Guard the status query with dma_submit_error() and ratelimit the error log.
			rx_dma_status = dmaengine_tx_status(controller->dma_rx, rx_cookie,
							     &rx_state);
			tx_dma_status = dmaengine_tx_status(controller->dma_tx, tx_cookie,
							     &tx_state);

  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

…hreads

- spi_transport_hw_linux.c: mt_hw_spi_complete() now uses
  min(msg.actual_length, xfer.len) instead of the bare requested
  xfer.len, so a short-but-"successful" DMA completion no longer hands
  the core stale/unwritten tail bytes.
- spi_mt_transport_drv.c: set SPI_NO_CS + spi_setup() in probe() (the
  DTS "spi-no-cs;" property proposed in review doesn't exist as a
  parsed binding in this kernel's SPI core -- verified against
  spi.c's of_spi_parse_dt() -- so this is the actual mechanism).
- spi-imx.c: dev_err_ratelimited() for the DMA-residue mismatch log
  (same unthrottled-hot-path-logging class as the confirmed
  boot-time-hang fix in d5d1f57); dma_submit_error() checks after
  both dmaengine_submit() calls, with cleanup matched to each call
  site's actual in-flight state (rx_cookie failure needs no channel
  cleanup; tx_cookie failure must terminate both channels since RX is
  already submitted+issued by then).

Compile-verified via `bitbake -c compile_kernelmodules -f linux-imx`
against the firmware repo's current core (see below) -- all 4 changed
objects (spi-imx.o, plus the 3 spi-mt-transport objects) compiled with
zero errors/warnings, spi-mt-transport.ko linked clean.

Also calls a new spiTransportDeinit() from every post-Init probe
failure path and from mt_transport_remove(), fixing the gInstances[]
pool leak (thread MultiTracksDotCom#2). That function is added on the firmware repo's
MT-159369 branch, still WIP/uncommitted there -- see PR comment.

Thread MultiTracksDotCom#1 (spi_mt_transport_drv.c:52, "where are these?") is
reply-only, no code change; answered directly on the review thread.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 8, 2026 14:56
@MT-MichaelLoh

Copy link
Copy Markdown
Author

Pushed 1f7da38b054f, addressing 5 of the 6 remaining unresolved threads:

  • spi_transport_hw_linux.c: mt_hw_spi_complete() short-transfer length fix (min(actual_length, xfer.len)).
  • spi_mt_transport_drv.c: SPI_NO_CS + spi_setup() in probe() — the DTS spi-no-cs; property doesn't exist as a parsed binding anywhere in this kernel's SPI core (checked spi.c's of_spi_parse_dt()), so setting it from the DTS would've been a silent no-op. This sets the mode bit directly on spi->mode instead.
  • spi-imx.c: ratelimited the DMA-residue mismatch log, and added dma_submit_error() checks after both dmaengine_submit() calls.

Still WIP, not in this push: the gInstances[] pool-leak fix (thread on spi_mt_transport_drv.c:805). The driver-side half is in this commit (spiTransportDeinit() called from every post-Init probe failure path + mt_transport_remove()), but it calls a new spiTransportDeinit() that only exists on the firmware repo's MT-159369 branch right now, still uncommitted there. That's a separate, future PR against the firmware repo (MT-159369) — once it lands, this recipe's SRCREV_spitransportcore pin needs bumping to pick it up before the module will actually build/load with this driver-side change. Flagging so this doesn't look silently broken if anyone builds against the currently-pinned core revision in the meantime.

Compile-verified locally via bitbake -c compile_kernelmodules -f linux-imx against my own uncommitted core copy (with spiTransportDeinit() added) — all 4 changed objects compiled clean, spi-mt-transport.ko linked.

Replied inline on thread #1 (spi_mt_transport_drv.c:52, "where are these?") — no code change needed there.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It introduces a substantial new SPI protocol driver and also modifies the generic spi-imx DMA hot path, so it warrants careful human review of performance/operational impact and teardown/concurrency correctness.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

drivers/spi/spi-mt-transport/spi_transport_hw_linux.c:199

  • The poison-sentinel comment says the pattern is “0xA5/0x5A”, but the code poisons the RX buffer with 0x37. Update the comment so the documented diagnostic pattern matches the implementation.

drivers/spi/spi-imx.c:1613

  • The MT-159369 “DMA residue” instrumentation calls dmaengine_tx_status() on every non-target-mode DMA transfer. That adds hot-path overhead to the generic spi-imx controller driver for all users, even when the MultiTracks transport isn’t in use; consider gating this behind a debug option (or at least CONFIG_SPI_MT_TRANSPORT) so production builds don’t pay the cost.
		/* MT-159369 bring-up instrumentation: both completions above
		 * only prove each channel's callback fired -- neither proves
		 * the engine actually drained transfer->len bytes. Check
		 * residue directly; a nonzero value here would mean the RX/TX
		 * buffer genuinely has fewer real bytes than the rest of this
  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@MT-MichaelLoh

Copy link
Copy Markdown
Author

Flagging explicitly so this doesn't surprise anyone on a fresh build or CI run: this branch does not build standalone at HEAD (1f7da38b054f) right now.

spi_mt_transport_drv.c calls the new spiTransportDeinit() (see the pool-leak thread above), which only exists in the firmware repo's uncommitted MT-159369 work -- not at the currently-pinned SRCREV_spitransportcore (ad136fd583a9) in linux-imx_%.bbappend. Building this exact commit via the normal recipe pin will hit an implicit-declaration warning on the call, then a hard link failure (undefined reference to 'spiTransportDeinit') when spi-mt-transport.ko links -- the symbol doesn't exist anywhere in the staged core at that pin.

My local compile-verify for this PR passed only because I manually overlaid my own uncommitted firmware-repo core edits into the build tree first; that state doesn't exist from git alone yet.

This resolves once the firmware-repo core PR (adding spiTransportDeinit(), MT-159369) lands and SRCREV_spitransportcore here gets bumped to pick it up -- tracked on the still-open pool-leak thread. Until then, treat this branch as not independently buildable.

@MT-MichaelLoh

Copy link
Copy Markdown
Author

Firmware-repo counterpart is up: https://github.com/MultiTracksDotCom/firmware/pull/880 (MT-158113, adds spiTransportDeinit(), branched fresh off current develop -- not the old stale/merged MT-158113 branch).

Once that merges, SRCREV_spitransportcore in linux-imx_%.bbappend needs bumping to pick it up before this branch will build standalone (see the earlier build-dependency comment above).

@MT-MichaelLoh

Copy link
Copy Markdown
Author

@audiffred-mt All review threads are now resolved. One more piece of context worth having on record before this lands: the old SPI IPC transport (connect's original spi.c/ipcspi.c path, using /dev/spidev1.0 on mt-connect.dts's ecspi2 bus) was officially retired in firmware PR #885 -- HDLC is now the default and only actively built/deployed transport, with SPI kept solely as a documented compile-time fallback that nothing currently builds or ships.

This PR's mt-connect.dts change replaces that same ecspi2 child node's binding (generic spidev/rohm,dh2228fv -> multitracks,spi-transport), which would otherwise remove /dev/spidev1.0 out from under the old transport. Since that transport is retired, this has no impact on current or future builds/operations.

Given that, this is good to land now -- it puts the driver and devicetree binding in place for whenever we revisit SPI transport testing in the future, with no downside since the old IPC-over-SPI path it would otherwise affect is already gone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants