> For the complete documentation index, see [llms.txt](https://docs.morsemicro.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.morsemicro.com/application-notes/appnote-51-linux-spi-driver-porting-guide.md).

# APPNOTE-51 Linux SPI Driver Porting Guide

## 1 Scope

This application note guides a software engineer through every step required to port the Morse Micro Linux kernel driver to use the SPI host interface on MM6108 and MM8108 Wi-Fi HaLow chip families.

It covers:

* Hardware pin mapping, electrical requirements, and SPI bus timing constraints
* SPI controller capabilities required on the host
* Device tree bindings and kernel configuration
* Module parameters for bring-up tuning
* The complete bring-up sequence as executed by morse\_spi\_probe()
* Low-level SDIO-over-SPI protocol details (CMD63 fast-init, CMD53 data transfers, inter-block padding)
* Platform-specific notes for common host SoCs
* Known issues, workarounds, and a debug checklist

Readers should already be familiar with the Linux SPI subsystem (drivers/spi/), device-tree SPI node configuration, and the basics of the Morse Micro driver source tree. For SDIO-based porting, refer to the standard Morse Micro SDK integration guide.

## 2 System Overview

### 2.1 SPI as a Host Interface

The MM6108 and the MM8108 SoC integrate a single physical host-interface block that implements both an SDIO 2.0 slave and a SPI slave on the same physical pins. After powering on, the block is in SDIO mode. The host switches it to SPI mode by sending CMD63 with the chip-select asserted. Once in SPI mode, the device stays in SPI mode until power is cycled or a hardware reset is applied.

Both MM6108 and MM8108 can only operate in  SPI mode 0 (CPOL=0, CPHA=0). The driver configures the SPI controller to 8-bit mode, with a maximum clock of 50 MHz enforced in software. In practice, the usable frequency depends almost entirely on signal integrity; see Section 10 for guidance on frequency selection.  For best performance on the SPI bus, Morse Micro recommends not sharing the bus with other peripheral devices.

### 2.2 SPI-over-SDIO Protocol

The SPI interface is a thin wrapper around the SDIO digital block. This has a critical implication for driver porting: the device does not implement a pure SPI slave. It speaks the SDIO command/data protocol over SPI-compatible physical lines. Consequently:

* Commands are framed as SDIO CMD packets (6-byte command + R1/R4/R7 response).
* Data transfers use CMD53 read/write with 512-byte block granularity (MMC\_SPI\_BLOCKSIZE).
* The device's internal address space is 17 bits and is expanded to 32 bits via keyhole registers identical to those in SDIO mode.
* Data blocks are validated with CRC16-CCITT. The kernel CRC\_ITU\_T module must be enabled.

Timing restrictions that apply to the SDIO block also apply in SPI mode, since the same digital block is clocked.

## 3 Hardware Interface

### 3.1 Pin Mapping

The SPI interface reuses the physical SDIO pins. The table below shows the mappings between the MM6108/MM8108 SDIO signal names, the SPI signal names, and the Linux SPI/GPIO driver properties.

| MM6108/MM8108 | SPI Signal | Linux SPI Name  | Notes                                         |
| ------------- | ---------- | --------------- | --------------------------------------------- |
| SDIO\_CLK     | SCLK       | spi-sclk / CLK  | Clock, driven by the host                     |
| SDIO\_CMD     | MOSI       | spi-mosi / TX   | Host → chip data                              |
| SDIO\_D0      | MISO       | spi-miso / RX   | Chip → host data                              |
| SDIO\_D3      | CS\_N      | spi-cs / CE     | Active-low chip select                        |
| SDIO\_D1      | IRQ        | spi-irq-gpios   | Open-drain, active-low; needs pull-up on host |
| SDIO\_D2      | —          | —               | Unused; pull HIGH to VDDIO (10 kΩ)            |
| RESET\_N      | nRESET     | reset-gpios     | Driver: assert low ≥80 ms, then release       |
| WAKE\_UP      | WAKE       | power-gpios\[0] | Optional power-save; host output              |
| BUSY          | BUSY       | power-gpios\[1] | Optional power-save; chip output              |

**Table 1: Pin mapping**

Note: SDIO\_D2 is unused in SPI mode and must be pulled to VDDIO (nominally 3.3 V) via 10 kΩ resistors on the PCB to prevent the internal SDIO logic from detecting bus-conflict events.

### 3.2 Electrical Requirements

All SPI signals are digital I/O operating at VDDIO (2.25 to 3.6 V, typically 3.3 V). If the host SPI controller operates at 1.8 V logic levels, uni-directional level shifters will need to be inserted. For more detailed information, refer to  MM8108-MF15457 Hardware Design Guide.

### 3.3 SPI Bus Timing

The timing parameters are derived from the SDIO bus timing specification in the MM6108 / MM8108 datasheet.

![Figure 1: SDIO device bus input timing diagram](/files/7b0cdd82294a5bf65e4b3117fffd5dd181d0fe23)

*Figure 1: SDIO device bus input timing diagram*

![Figure 2: SDIO device bus output timing diagram](/files/d1b22b86005dc783ebfe6b6a6ac0f4f267911ad6)

*Figure 2: SDIO device bus output timing diagram*

The table below lists all parameters relevant to the Linux SPI controller configuration.

| Parameter                       | Min   | Max    | Notes                                              |
| ------------------------------- | ----- | ------ | -------------------------------------------------- |
| Clock frequency                 | 0 MHz | 50 MHz | Hard ceiling in the driver                         |
| CPOL / CPHA                     | 0 / 0 | 0 / 0  | SPI Mode 0; clock idles low, sample on rising edge |
| MOSI setup to SCLK rise (tISU)  | 6 ns  | —      | From the SDIO host-mode timing spec                |
| MOSI hold after SCLK rise (tIH) | 2 ns  | —      |                                                    |
| SCLK rise to MISO valid (tODLY) | —     | 14 ns  | At chip pins; tightens at > 50 MHz                 |
| CS\_N assert to first SCLK      | 0 ns  | —      | Keep CS asserted for the full transaction          |
| Inter-block delay for MM6108    | 40 µs | 130 ms | Abort if > 130 ms                                  |
| Inter-block delay for MM8108    | 5 µs  | 130 ms | Abort if > 130 ms                                  |
| Signal capacitance              | —     | 40 pF  | Exceeding this limit requires series termination   |

**Table 2: Bus timing specifications**

### 3.4 Reset Sequencing

The driver controls nRESET via the reset-gpios device-tree property. On probe, morse\_spi\_reset() asserts nRESET low and holds it low for 80 ms before releasing it. This single timing covers both the RESET\_N hold requirement and sufficient time for all chip power rails and the crystal oscillator to stabilize before the first SPI command.

The complete board-level power-on sequence is:

1. Assert nRESET low.
2. Ensure all chip power rails (VDD, VDDIO) are within specification.
3. The driver will release nRESET after 80 ms. If your power ramp or crystal startup is longer than 80 ms, delay the module load accordingly.
4. After nRESET is released, the driver waits for the chip's SPI state machine to enter the idle state, then begins the training sequence (Section 9.1.6).

## 4 SPI Controller Requirements

The driver calls spi\_setup() with the following fixed settings:

* mode: SPI\_MODE\_0  (CPOL=0, CPHA=0): recommended
* bits\_per\_word: 8: mandatory; 16- or 32-bit modes are not supported
* Max clock: 50 MHz: hard ceiling clipped in morse\_spi\_setup() (spi.c:202)

Additional controller requirements and recommendations:

* A DMA-capable controller is strongly recommended. The driver issues single transactions up to 8 KB and total transfers up to 64 KB.
* CS must remain asserted (low) throughout each transaction. The driver uses a single shared 8 KB DMA buffer for both TX and RX. Do not configure the controller to release CS between words or between DMA segments.
* During the 74-clock training sequence, the driver temporarily asserts SPI\_CS\_HIGH to clock out 18 bytes of 0xFF with CS deasserted. Host controllers should support this. If not,  the driver logs "can't change chip-select polarity" and skips the sequence. In this case, the chip's subsequent behavior is not deterministic.

### 4.1 Transaction Size Limits

* SPI\_MAX\_TRANSACTION\_SIZE = 8 KB: maximum single SPI transaction
* SPI\_MAX\_TRANSFER\_SIZE  = 64 KB: maximum single bus read/write
* MMC\_SPI\_BLOCKSIZE = 512 B: SDIO block size

If your SPI controller cannot sustain 8 KB bursts without inserting long inter-block gaps, throughput will suffer, but the link will remain functional.

## 5 Key Specifications

All values below are derived from #define constants in spi.c.

| Parameter                       | Value                                                                                         |
| ------------------------------- | --------------------------------------------------------------------------------------------- |
| Maximum SPI clock               | 50 MHz (hard ceiling, driver clips higher values)                                             |
| SPI mode                        | Mode 0 (CPOL=0, CPHA=0)                                                                       |
| Bits per word                   | 8                                                                                             |
| SDIO block size                 | 512 bytes                                                                                     |
| Driver DMA buffer               | 8 KB (single shared TX/RX buffer)                                                             |
| Maximum single SPI transaction  | 8 KB                                                                                          |
| Maximum single bus read/write   | 64 KB                                                                                         |
| Reset assert / hold / release   | Drive nRESET low, hold ≥80 ms, release                                                        |
| 74-clock training burst         | 18 bytes of 0xFF with CS deasserted (CS high)                                                 |
| Default inter-block delay       | <p>\~40 µs for MM6108<br>\~5 µs for MM8108<br>(expressed as padding bytes at clock speed)</p> |
| Default post-write status bytes | 4                                                                                             |
| Data integrity CRC              | CRC16-CCITT                                                                                   |
| IRQ trigger (default)           | IRQF\_TRIGGER\_LOW \| IRQF\_ONESHOT                                                           |
| IRQ trigger (edge mode)         | IRQF\_TRIGGER\_FALLING \| IRQF\_ONESHOT                                                       |

**Table 3: Key specifications**

## 6 Device Tree Bindings

### 6.1 Compatible Strings

* "morse,mm610x-spi": for MM6108 family (includes MM6108)
* "morse,mm810x-spi": for MM8108 family (includes MM8108)

The driver's OF match table is at spi.c:192, and its non-DT SPI ID table is at spi.c:184.

### 6.2 Required Properties

* compatible: as above
* reg: chip-select index on the parent SPI bus (not a memory address)
* spi-max-frequency: SPI bus clock in Hz; must be ≤ 50000000
* reset-gpios: single GPIO specifier for nRESET (active-low). Probe fails if absent (unless mmc-pwrseq is used; see Section 11)
* spi-irq-gpios: single GPIO specifier for the chip IRQ output (active-low). Probe fails if absent

### 6.3 Optional Properties

* power-gpios: pair of GPIO specifiers (wake, busy) as a single property with two cells. Required only for power-save support. If only one GPIO is listed, the driver logs a warning and disables power-save
* interrupts / interrupt-parent: not required; the driver calls gpio\_to\_irq() on spi-irq-gpios internally

### 6.4 Example Device Tree Node

A complete working example for the RPi Broadcom SPI host:

/dts-v1/;

/plugin/;

/ {

&#x20; compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709", "brcm,bcm2711";

&#x20; fragment\@0 {

&#x20;    target = <\&spi0>;

&#x20;    frag0: **overlay** {

&#x20;       pinctrl-0 = <\&spi0\_pins \&spi0\_cs\_pins>;

&#x20;       cs-gpios = <\&gpio 8 1>;

&#x20;       \#address-cells = <1>;

&#x20;       \#size-cells = <0>;

&#x20;       status = "okay";

&#x20;       mm6108: mm6108\@0 {

&#x20;          compatible = "morse,mm610x-spi";

&#x20;          reg = <0>;  /\* CE0 \*/

&#x20;          reset-gpios = <\&gpio 5 0>;

&#x20;          power-gpios = <\&gpio 3 0>, <\&gpio 7 0>;

&#x20;          spi-irq-gpios = <\&gpio 25 0>;

&#x20;          spi-max-frequency = <50000000>;

&#x20;          status = "okay";

&#x20;       };

&#x20;       spidev0: spidev\@0 {

&#x20;          reg = <0>;  /\* CE0 \*/

&#x20;          status = "disabled";

&#x20;       };

&#x20;       spidev1: spidev\@1 {

&#x20;          reg = <1>;  /\* CE1 \*/

&#x20;          status = "disabled";

&#x20;       };

&#x20;    };

&#x20; };

&#x20; fragment\@1 {

&#x20;    target = <\&gpio>;

&#x20;    **overlay** {

&#x20;       spi0\_cs\_pins: spi0\_cs\_pins {

&#x20;          brcm,pins = <8>;

&#x20;          brcm,function = <1>; /\* BCM2835\_FSEL\_GPIO\_OUT \*/

&#x20;          brcm,pull = <2>;   /\* SET SPI PINS AS PULL HIGH \*/

&#x20;       };

&#x20;       spi0\_pins: spi0\_pins {

&#x20;          brcm,pull = <2 2 2>; /\* SET SPI PINS AS PULL HIGH \*/

&#x20;       };

&#x20;    };

&#x20; };

};

Notes:

* Developers should not set spi-cpol or spi-cpha in the DT node. Mode 0 is the correct default. Setting either flag selects a different SPI mode, and the chip will not respond.
* SDIO\_D2 pull-ups should be placed on the PCB, not controlled by software. Confirm the schematic includes 10 kΩ pull-up resistors on these two lines.

## 7 Kernel Configuration

### 7.1 Morse Driver Kconfig Options

* CONFIG\_MORSE\_SPI=y (or =m) — builds the SPI bus support. Required.
* CONFIG\_MORSE\_SPI\_RK3288=y — Rockchip RK3288 platforms only (see Section 12).
* CONFIG\_MORSE\_DEBUG\_MASK= — debug verbosity bitmask: 1=debug, 2=info, 4=warning, 8=error (default). Set to 15 (0x0F) during bring-up for maximum verbosity.
* CONFIG\_MORSE\_ENABLE\_TEST\_MODES=y — enables the test\_mode module parameter used by the bus integrity test (Section 12).

### 7.2 Required Kernel Subsystems

* CONFIG\_SPI — SPI master framework
* CONFIG\_OF — device tree support
* CONFIG\_OF\_GPIO — GPIO bindings used by morse\_of\_probe()
* CONFIG\_CRC\_ITU\_T — CRC16-CCITT  used by the SPI data integrity check

## 8 Module Parameters (Bring-Up Tuning)

All parameters may be passed on the insmod / modprobe command line or persisted in /etc/modprobe.d/morse.conf using the options directive.

| Parameter                       | Default    | Description                                                                                                            |
| ------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| spi\_clock\_speed               | 0 (use DT) | Override spi-max-frequency from the device tree. Values above 50 MHz are clipped.                                      |
| spi\_inter\_block\_delay\_bytes | auto       | Override auto-calculated inter-block delay bytes. Raise if CRC errors occur.                                           |
| spi\_post\_write\_status\_bytes | 4          | Additional 0xFF cycles after CMD53 write for chip response token. Raise to 8 or 16 on older kernels.                   |
| spi\_use\_edge\_irq             | 0          | 0 = IRQF\_TRIGGER\_LOW (level). 1 = IRQF\_TRIGGER\_FALLING (edge). Use the edge only if the host cannot level-trigger. |
| enable\_ext\_xtal\_init         | 0          | Set to 1 on boards booting from a slow (e.g., 32 kHz) external crystal. Adds +2 KB 0xFF padding to writes.             |

**Table 4: Bring-up tuning parameters**

Examples:

`# insmod — full set of bring-up overrides`

insmod morse.ko spi\_clock\_speed=5000000 spi\_use\_edge\_irq=1 enable\_ext\_xtal\_init=1

`# modprobe.d — persist a 25 MHz clock`

echo "options morse spi\_clock\_speed=25000000" >> /etc/modprobe.d/morse.conf

## 9 Bring-Up Sequence

This section describes exactly what happens when morse\_spi\_probe() runs (spi.c:1387). You can correlate kernel log output with each phase.

### 9.1 Step-by-Step Probe Sequence

#### 9.1.1 Step 1: Compatible / ID Match

The driver matches "morse,mm610x-spi" or "morse,mm810x-spi" from the DT, or "mm610x-spi" / "mm810x-spi" from the non-DT SPI id table, and selects the chip series.

#### 9.1.2 Step 2: SPI Controller Setup

Calls morse\_spi\_setup(): sets Mode 0, 8 bpw, clips clock to 50 MHz, and calls spi\_setup(). Failure here means the host controller rejected the settings.

#### 9.1.3 Step 3: Driver Structures and DMA Buffer

Allocates an 8 KB buffer used as a shared TX/RX DMA region. Initializes the spi\_message / spi\_transfer structures used for every transaction.

#### 9.1.4 Step 4: Device Tree GPIO Parse

morse\_of\_probe()  reads reset-gpios, spi-irq-gpios, and optional power-gpios.

#### 9.1.5 Step 5: Hardware Reset

Calls morse\_spi\_reset (asserts nRESET low, waits 80 ms, releases).

#### 9.1.6 Step 6: 74-Clock Training Sequence (skipped on RK3288)

Temporarily asserts SPI\_CS\_HIGH and clocks out 18 bytes of 0xFF (≥ 74 clock pulses) with CS deasserted to satisfy the MMC/SD requirement before CMD0. Then restores CS polarity.

#### 9.1.7 Step 7: CMD63 SPI Mode Switch

Issues CMD63 (SD\_IO\_MORSE\_INIT) — the vendor-specific command that switches the chip from SDIO mode to SPI mode. On failure, falls back to CMD0 (SD\_IO\_RESET) and retries. Up to 3 attempts total. If all attempts fail, log "failed to init SPI with CMD63 (ret:)."

#### 9.1.8 Step 8: Chip Detection

Reads the chip ID register and validates that it matches the compatible string. On success logs "Morse Micro SPI device found, chip ID=0x". A return of all 0xFF or all 0x00 indicates the initialization sequence failed; see Section 13.

#### 9.1.9 Step 9: Inter-Block Delay Configuration

Configures inter-block timing based on clock speed (or spi\_inter\_block\_delay\_bytes if overridden). On success logs "clock= MHz, delay bytes=, max block count=".

#### 9.1.10 Step 10: Firmware Load

Loads the firmware image.

#### 9.1.11 Step 11: IRQ Setup

Calls morse\_spi\_setup\_irq():

* gpio\_request() and gpio\_direction\_input() on spi-irq-gpios
* gpio\_to\_irq() to obtain the Linux IRQ number
* CMD52 writes to SDIO CCCR registers 0x04 (IEN) and 0x07 (BIC) to enable chip-side interrupts
* request\_threaded\_irq() with IRQF\_TRIGGER\_LOW | IRQF\_ONESHOT (or IRQF\_TRIGGER\_FALLING if spi\_use\_edge\_irq=1)

#### 9.1.12 Step 12: MAC Registration

Registers the 802.11 MAC layer and starts the health check thread. Beyond this point, the SPI link is up, and the driver is in the MAC domain.

Note: If the probe fails, the driver reverses each step in order, unless the chip supports the fast re-probe (reattach) path, which keeps the firmware loaded.

### 9.2 Command Protocol Details

#### 9.2.1 0xFF Byte Before Each Command

Each SDIO command must be immediately preceded by a single 0xFF byte sent with CS asserted. This is the mandatory 'clock-before-command' byte required by the SDIO-over-SPI protocol, so the chip can detect the command start bit. Omitting it causes intermittent failures, especially on USB-to-SPI bridges with non-deterministic inter-transaction timing.

#### 9.2.2 0xFF Padding After Commands

After transmitting an SDIO command (while CS is still asserted), include a single 0xFF byte in the same SPI transfer message before polling for the device response. Sending it as a separate transfer can introduce enough delay for MISO to take significantly longer to return high.

#### 9.2.3 0xFF Padding for CMD53 Data Phase

For CMD53 read/write operations, up to four additional 0xFF bytes may be required between the command status response and the data token (0xFE for single block, 0xFC for multi-block write). The driver must keep sending 0xFF and monitoring MISO for the data start token within this window.

### 9.3 Multi-Block Transfers

Multi-block transfers require special handling because the chip needs inter-block time to process received data:

* After each block's status/acknowledgment, continue sending 0xFF on MOSI until MISO goes high (the chip is not busy).
* If MISO has not returned high within 130 ms, the block transfer has failed — abort and retry from the start of the CMD53 sequence.
* The entire multi-block transfer (command + all blocks + padding) must be a single SPI transaction with CS asserted throughout. Toggling CS between blocks causes the chip to lose block-boundary tracking.
* Since the inter-block delay is constant, the number of 0xFF inter-block padding bytes varies with clock speed. At lower clocks (e.g., 12.5 MHz), fewer bytes are needed than at 50 MHz.

## 10 Frequency Selection Guidance

The absolute maximum SPI clock the driver accepts is 50 MHz. In practice, the usable frequency depends almost entirely on signal integrity: trace length, trace matching, connector quality, and the host SPI controller's output slew rate.

Recommended bring-up strategy:

1. First boot: 1 to 5 MHz — at this speed, electrical issues are eliminated as a cause of failure. Start here.
2. Functional test: 10 to 20 MHz — once chip ID reads back correctly and firmware loads, step up to a mid-range clock. Run iperf or the bus test mode (Section 12) for several minutes and watch for CRC errors in dmesg.
3. Target operation: 20 to 25 MHz — the sweet spot for most production designs.
4. Above 25 MHz — possible on well-engineered PCBs with short length-matched traces, clean grounds, and controllers with fast output slew. Validate with an oscilloscope on the MISO/MOSI/SCLK pins.

Inter-block delay tuning: the default inter-block delay (40 µs for MM6108 and 5 µs for MM8108) is internally represented as a count of 0xFF padding bytes at the configured clock rate. If you see sporadic "SPI response missing" or CRC errors at a given clock, raise spi\_inter\_block\_delay\_bytes before dropping the clock.

Note: Reducing the clock speed does not always improve reliability. At very low clocks (< 12.5 MHz), the inter-block gap is proportionally shorter, and the number of required padding bytes decreases. If debugging SPI issues, test at both the configured speed and at 25 MHz before concluding the issue is clock-rate-independent.

## 11 Tips and Tricks

* reset-gpios and spi-irq-gpios must be on host pins that have no bootstrap or alternate function that could drive them during reset. A bootstrap pin forced high can prevent the chip from releasing reset or mask the chip's IRQ.
* The chip IRQ (IRQ\_N) is an open-drain, active-low line. Ensure the host input is not configured with an internal pull-down — the interrupt will never deassert.
* The IRQ line should be routed to a GPIO that supports wake-from-suspend if system-wide power management is required.
* If using mmc-pwrseq to control power and reset instead of reset-gpios, the driver tolerates a missing reset-gpios property and logs an info message rather than failing probe.
* On boards with a 32 kHz sleep oscillator as the only clock reference at boot, set enable\_ext\_xtal\_init=1. The driver will pad every write with +2 KB of 0xFF bytes to give the chip time to respond at the slow clock rate.
* The driver's DMA buffer is a single 8 KB buffer used for both TX and RX. CS must be held for the full transaction; do not configure the controller to release CS between words.
* When swapping between SDIO and SPI on the same hardware, remember SPI requires both reset-gpios and spi-irq-gpios; SDIO requires neither.
* For development, always start with spi\_clock\_speed set to a value lower than your DT value. Once the link is stable, remove the override and rely on spi-max-frequency.

## 12 Platform-Specific Notes

Raspberry Pi (BCM2835 /  2837 / 2711)

Works out of the box at 25 MHz with the standard spi0 or spi1 master. Use a DT overlay to add the morse\_wifi\@0 node. No special Kconfig options are required.

Rockchip RK3288

Requires CONFIG\_MORSE\_SPI\_RK3288=y. The driver skips the 74-clock CS-high training sequence (the controller cannot drive CS high) and applies a 1-bit right shift to all received data to compensate for a known controller off-by-one bug.

NXP i.MX (i.MX 6 / 7 / 8)

Works at Mode 0 on all ECSPI variants. Do not set spi-cpol or spi-cpha in your DT — the defaults (both 0) are correct. The ECSPI maximum usable clock is typically well below 50 MHz, so the driver ceiling is not the limiting factor.

TI AM335x / AM57x (McSPI)

Check the McSPI cs-word / cs-release behavior. The driver requires CS to be held for the full transaction; verify that the controller does not release CS between DMA segments.

Controllers with Long First-Byte Post-CS Delay

Some controllers insert gaps after asserting CS. Raise spi\_post\_write\_status\_bytes from 4 to 8 or 16.

Any Controller Without 8-bit BPW Support

Not supported. The driver requires 8 bits per word.

## 13 Known Issues and Workarounds

| Symptom                                 | Cause                                                            | Workaround                                                                                                       |
| --------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| All MISO bytes are 0xFF after CMD63     | CMD63 not accepted; chip still in SDIO mode                      | Verify 18-byte 0xFF training with CS high was sent; confirm reset timing (≥80 ms); check SDIO\_D1/D2 pull-ups    |
| MISO data shifted by exactly 1 bit      | Unexpected chip reset (JTAG\_RST, supply drop, GPIO glitch)      | Ensure JTAG\_RST is not driven by host tools; check VDD  stays above 3.0 V; driver must detect and re-init       |
| Intermittent CMD53 read failures        | Insufficient inter-block padding or single-transaction violation | Raise spi\_inter\_block\_delay\_bytes; ensure entire multi-block transfer uses a single CS assertion             |
| Truncated / corrupted reads at < 25 MHz | Inter-block gap longer at low clock; DMA buffer too small        | Increase driver DMA receive buffer; add extra 0xFF padding to single-block reads                                 |
| "SPI response missing" errors           | Chip not emitting R1 token in the expected window                | Raise spi\_post\_write\_status\_bytes (try 8, then 16); reduce clock speed                                       |
| FTDI JTAG\_RST assertion on connect     | FT2232H D2XX driver asserts ADBUS7 (JTAG\_RST) at init           | Disconnect JTAG\_RST or deassert ADBUS7 before SPI communication begins                                          |
| "can't change chip-select polarity"     | SPI controller cannot drive CS high for the training burst       | Informational only; driver proceeds. If CMD63 also fails, emit 74 clocks externally or use the RK3288 workaround |
| CRC errors above 25 MHz                 | Signal integrity: long traces, capacitance, slew mismatch        | Add 22–33 Ω series terminators near the chip; reduce the clock; validate with a scope at chip pins               |

**Table 5: Known issues and workarounds**

## 14 Troubleshooting

### 14.1 Verify the Driver Loaded and Matched

Run dmesg | grep -i morse and look for:

"Reading gpio pins configuration from device tree"

Then follow the step-by-step messages from Section 9 to identify which probe step failed.

### 14.2 Increase Verbosity

Build with CONFIG\_MORSE\_DEBUG\_MASK=0xF or override at runtime via the driver's module parameter  debug\_mask=0xF.

### 14.3 Common Error Messages

* "Required property reset-gpios not found in device tree" → Add reset-gpios to your DT node. Verify the loaded DTB with /proc/device-tree/.
* "Required property spi-irq-gpios not found in device tree" → Add spi-irq-gpios to your DT node.
* "Failed to acquire spi irq gpio" → Another driver already claimed the GPIO, or the GPIO number is invalid. Run gpioinfo  (libgpiod) to identify the current consumer of the line. See Section 14.5 for more details.
* "failed to init SPI with CMD63 (ret:)" → Chip not responding to the init command. Verify SPI wiring, power rails, crystal, nRESET timing (≥ 80 ms), SPI Mode 0, and try spi\_clock\_speed=1000000 to rule out signal integrity.
* "SPI response missing" → Chip not emitting R1 response. Try raising spi\_post\_write\_status\_bytes (e.g., 8), then spi\_inter\_block\_delay\_bytes. If persistent, lower the clock.
* "SPI response bit shifted" → MISO sampled one bit early/late. On RK3288, fixed by CONFIG\_MORSE\_SPI\_RK3288=y. On other controllers, check CPHA and controller RX sample-delay settings.
* "SPI response error" → R1 byte invalid; likely noise or bad CRC. Lower the clock and scope MISO at the chip pins.
* "can't change chip-select polarity" → Informational; controller does not support SPI\_CS\_HIGH. Training sequence is skipped. Usually harmless.
* "SPI clock MHz is not supported by this chip, clipping to 50 MHz" → Your DT or module param exceeds 50 MHz; the driver clips it.

### 14.4 Physical Layer Sanity Checks

With a scope or logic analyzer on the SPI pins measured at the chip:

* Confirm SCLK idles low and samples on the rising edge (Mode 0).
* Confirm CS is driven low for the entire transaction, not just the first byte.
* Confirm the 18-byte 74-clock CS-high training burst appears on MOSI with CS high (absent on RK3288 targets).
* Confirm the first CMD bytes match SDIO framing (0x40 | cmd\_index on MOSI).

### 14.5 GPIO Sanity Checks

Use gpioinfo (libgpiod) to verify:

* reset-gpios: output, currently high after reset release.
* spi-irq-gpios: input, currently high when idle (chip releases line when no IRQ pending). Verify that there is no internal pull-down on the host input.
* power-gpios wake: output, host-controlled.
* power-gpios busy: input, chip-driven.

### 14.6 Confirm the Clock Actually Used

Check dmesg | grep "clock=". You should see: "clock=25 MHz, delay bytes=, max block count=". If the clock is lower than your DT setting, a spi\_clock\_speed module parameter may be overriding it.

### 14.7 Bus Integrity Test

Build with CONFIG\_MORSE\_ENABLE\_TEST\_MODES=y and load the module with test\_mode=MORSE\_CONFIG\_TEST\_MODE\_BUS. This runs an internal pattern test over the SPI link, exercising the CRC path independent of firmware. A clean pass indicates the bus is healthy.

Use MORSE\_CONFIG\_TEST\_MODE\_BUS\_PROFILE to measure throughput versus block size, which is useful for tuning spi\_inter\_block\_delay\_bytes.

## 15 Revision History

| Release Number | Release Date | Release Notes   |
| -------------- | ------------ | --------------- |
| Version 1      | 29 May 2026  | Initial release |

Morse Micro provides this information "as is" without warranties of any kind, express or implied. No guarantee is made as to the accuracy, completeness, or suitability of this information or Morse Micro’s products for any specific purpose. Use of this information and products is at the user’s sole risk. Morse Micro products are not designed or tested for use in mission-critical systems, and should not be used in such applications. Performance specifications are based on internal testing and are believed to be reliable; however, they are not guaranteed. It is the Buyer’s responsibility to test and validate all product performance, compatibility, and compliance, both in isolation and within end applications. Morse Micro assumes no liability for the use or application of any product, circuit, or information described herein. No license or other rights—express or implied—are granted under Morse Micro’s intellectual property. This document contains proprietary information of Morse Micro and is subject to change without notice. Wi-Fi®, Wi-Fi HaLow™, and the Wi-Fi logo are trademarks of Wi-Fi Alliance. ZigBee™ and Z-Wave™ are trademarks of their respective owners. All other trademarks are the property of their respective owners.

| Linux SPI Driver Porting Guide v1 | morsemicro.com  \| |
| --------------------------------- | ------------------ |
