MT-158113: imx8mm-evk-spi-transport devicetree overlay - #46
MT-MichaelLoh wants to merge 35 commits into
Conversation
…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.
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.
There was a problem hiding this comment.
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;
- 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.
There was a problem hiding this comment.
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>;
};
- 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.
There was a problem hiding this comment.
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
allOfschemas in place,additionalProperties: falsetends to be too strict because it can reject properties coming from the referenced schemas;unevaluatedProperties: falseis the more typical pattern for DT schema composition in this tree (e.g.Documentation/devicetree/bindings/spi/spi-mux.yaml:52). Also,spi-max-frequencyis 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 enablingCONFIG_SPI_MT_TRANSPORToutside 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: falsebut 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 (seeDocumentation/devicetree/bindings/spi/spi-mux.yaml:32). Consider adding a$reftospi-peripheral-props.yamlso 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.
There was a problem hiding this comment.
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
> 0will 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,
- 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.
There was a problem hiding this comment.
🔵 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>
There was a problem hiding this comment.
🟡 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
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).
There was a problem hiding this comment.
🟡 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
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.
There was a problem hiding this comment.
🟡 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
…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.
There was a problem hiding this comment.
🔵 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.
There was a problem hiding this comment.
🔵 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>
|
Pushed
Still WIP, not in this push: the Compile-verified locally via Replied inline on thread #1 ( |
There was a problem hiding this comment.
🔵 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
|
Flagging explicitly so this doesn't surprise anyone on a fresh build or CI run: this branch does not build standalone at HEAD (
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 |
|
Firmware-repo counterpart is up: https://github.com/MultiTracksDotCom/firmware/pull/880 (MT-158113, adds Once that merges, |
|
@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 ( This PR's 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. |
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'sCODING_STANDARDS.md§7 forbids GPL/LGPL code there. Found by Copilot's review on firmware PR #785; relicensing wasn't an option sinceMODULE_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(+Makefileentry) -- the devicetree overlay:multitracks,spi-transportbinding 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.cglue,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-nostdincbuild), moved here verbatim from the firmware repo, content unchanged. Plus the matchingdrivers/spi/Kconfig(CONFIG_SPI_MT_TRANSPORT) anddrivers/spi/Makefilehooks, andarch/arm64/configs/imx_v8_defconfig's=mline.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'smeta-mt-transport-evk/recipes-kernel/linux-imx/linux-imx_%.bbappendfetches it fresh from the firmware repo at Yocto build time (a second, separately-namedSRC_URIentry) and stages it intodrivers/spi/spi-mt-transport/core/via ado_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 populatesdrivers/spi/spi-mt-transport/core/(5.cfiles, 6 headers) in the shared kernel source tree.bitbake linux-imx-- in-tree driver compiles clean, packages automatically askernel-module-spi-mt-transport-<kernel-ver>(standard in-tree convention -- no manualIMAGE_INSTALLentry needed, unlike the old out-of-tree recipe).bitbake imx-image-core-- fresh manifest confirmed the module package present.uname -rconfirms the new kernel,modprobe spi_mt_transportprobes cleanly,link_state: connected,event_countersclean.mismatchcounter incrementing exactly once per write.🤖 Generated with Claude Code