Chipselect Aussetzer beim Schreiben auf SPI NOR des IMX-28
Für einen Kunden pflege ich eine Linux Umgebung (inclusive Kernel) für ein kundenspezifischen Board das den IMX-28 ARM Prozessor verwendet. Für den User Space verwende ich ELBE, das Embedded Linux Build Environment das es erlaubt, das User Space Dateisystem aus eine Debian Linux Release zu bauen. Bei der Migration von einer früheren Debian Version zum derzeitigen stabilen Release mit dem Code-Namen "Bookworm" brauchte es ein Kernel Upgrade weil Systemd einige Dinge benötigt die nicht in früheren Kernels enthalten sind.
Nach dem Upgrade von Kernel 4.14 auf 6.1 (beide eine longterm Support Version) funktionierte unser SPI NOR Flash nicht mehr. Das JFFS2 Dateisystem produzierte Fehlermeldungen die nahelegten dass wir ein Problem beim Schreiben in das NOR Flash hatten (Einige der Fehlermeldungen zeigten dass eine 0 erwartet aber 0xFF gelesen wurde was zum Schluss führte dass diese Bytes nicht geschrieben wurden).
Nach Überprüfung der Taktrate des SPI Busses beim Schreiben des Flash mit der Beobachtung dass sich diese nicht geändert hatte schaute ich mir die Signale des SPI Busses an.
In den Oszilloscop Bildern (Blau ist Chip Select, Gelb ist der SPI Takt) sieht man dass es einen kurzen Aussetzer im Chip Select gibt. Das hat den Effekt dass das "Page Program" Kommando an das NOR Flash frühzeitig beendet wird. Aus dem Bild mit der höheren zeitlichen Auflösung sieht man dass dieser Aussetzer mitten in einem Bit der Übertragung stattfindet. Die exakte Position variiert leicht von Übertragung zu Übertragung (Der Aussetzer trifft nicht jedesmal das selbe Bit aber er trifft das selbe Byte).
Weiter Experimente zeigten dass das Problem nur bei Übertragungen mit
direktem Speicherzugriff (DMA) auftritt, nicht wenn kein DMA
(sondern programmierter Input-Output, PIO) verwendet wurde. Der SPI Treiber
optimiert Übertragungen: Nur längere Übertragungen werden mit DMA
durchgeführt, kürzere Übertragungen verwenden PIO weil der DMA Overhead
dann den Zeit-Gewinn überwiegt. Bei genauerer Inspektion der DMA
Implementierung für den IMX-28 in drivers/dma/mxs-dma.c
stellte sich
heraus dass es eine Änderung bei den Flags des DMA Transfers gab, die
alte Version hat:
if (flags & DMA_CTRL_ACK) ccw->bits |= CCW_WAIT4END;
Die neue Version hat:
if (flags & MXS_DMA_CTRL_WAIT4END) ccw->bits |= CCW_WAIT4END;
Das Bit CCW_WAIT4END
(Übersetzt aus der IMX-28 Prozessor Referenz):
"Der Wert eins gibt an dass der Kanal auf das Ende des
Kommandosignalsvom APBH Device zum DMA wartet bevor ein neues DMA
Kommando gestartet wird". Dieses Flag wird für den DMA Transfer zum NOR
Flash gesetzt. Eine Besonderheit des IMX-28 ist dass auch Register
Einstellungen über DMA übertragen werden können. Vor dem eigentlichen
Speichertransfer finden ein Register Transfer statt der das
IGNORE_CRC
Bit im Prozessor SSP Register setzt. Aus der Prozessor
Referenz zum IGNORE_CRC
Bit (übersetzt): "Im SPI/SSI Modus: Wenn auf
1 gesetzt, setze den Chip Select (SSn) Pin zurück nachdem das Kommando
ausgeführt wurde".
Was also passiert wenn das CCW_WAIT4END
nicht gesetzt wird, ist dass
der Prozessor das Chip-Select zu früh ausschaltet.
Die DMA Übertragung die vom SPI Treiber in drivers/spi/spi-mxs.c
initiiert wird setzt das neue MXS_DMA_CTRL_WAIT4END
nicht, so dass
das flag CCW_WAIT4END
für die Übertragung nicht gesetzt wird.
Der Fix ist damit offensichtlich, wir müssen nur dieses Flag bei der
Initiierung der DMA-Übertragung im SPI Treiber in
drivers/spi/spi-mxs.c
setzen.