Chipselect Glitch with SPI NOR on IMX-28
For a customer I'm maintaining a Linux environment (including kernel) for a custom board that uses a module based on the IMX-28 ARM processor. For the user space I'm using ELBE, the Embedded Linux Build Environment that allows building a user space filesystem from a Debian Linux release. Migrating from an earlier Debian version to the current stable release code-named Bookworm we needed a kernel upgrade because systemd requires some features not present in earlier kernels.
After upgrading from a Kernel in the 4.14 longterm support series to the 6.1 longterm support series our SPI NOR flash memory was no longer working. The JFFS2 file system we're using produced errors that indicated that we had a problem writing to the NOR flash (some error messages indicated that the file system expected bytes to be 0 which were 0xFF which lead to the conclusion that these bytes had not been written).
After verifying that the clock rate of the SPI bus when writing to the flash device was unchanged between kernel versions I looked at the signals of the SPI bus.
From the Oszilloscope pictures (blue is the chip select, yellow is the SPI clock) it can be seen that there is a short glitch in the chip select line. This has the effect of terminating the page program command to the NOR flash prematurely. From the zoomed-in version it can be seen that the chip-select has a glitch in the middle of a bit during the transfer. The exact position changes slightly from transfer to transfer (it doesn't hit the same bit every time but it does hit the same byte).
Further experimentation showed that the problem occurs only when the
SPI transfer is initiated via direct memory access (DMA), not when the
non-DMA version (programmed input-output, PIO) was used. The SPI driver
optimizes transfers: Only longer transfers are done via DMA, short
transfers use PIO because the DMA overhead exceeds the gain. When
looking into the DMA implementation of the IMX-28 in drivers/dma/mxs-dma.c
it turns out that there was a change in a flag passed to the DMA
transfer, the old version has:
if (flags & DMA_CTRL_ACK) ccw->bits |= CCW_WAIT4END;
The new version has:
if (flags & MXS_DMA_CTRL_WAIT4END) ccw->bits |= CCW_WAIT4END;
The bit CCW_WAIT4END
according the the IMX-28 processor reference:
"A value of one indicates that the channel waits for the end of command
signal to be sent from the APBH device to the DMA before starting the
next DMA command." This flag is set on the actual DMA transfer to the
NOR flash. The IMX-28 is special in that it can transfer register
settings also via DMA. Prior to the memory transfer a register transfer
is initiated that sets the IGNORE_CRC
bit in the processors SSP
register. From the processor reference on the IGNORE_CRC
bit: "In
SPI/SSI modes: When set to 1, deassert the chip select (SSn) pin after
the command is executed".
So what happens when the CCW_WAIT4END
is not set is that the chip
select is deasserted too early.
The DMA transfer initiated from the SPI driver in
drivers/spi/spi-mxs.c
does not set the MXS_DMA_CTRL_WAIT4END
flag, so the CCW_WAIT4END
is not set on the transfer.
Now the fix is obvious, we simply need to set this flag on the DMA
transfer initiated by the SPI driver in drivers/spi/spi-mxs.c
.