みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

秋月の「アレ」をYoctoProjectで遊ぶ(SPIフラッシュ)

はじめに

これまでの「アレ」

あまり情報がなかったSPIフラッシュを使えるようにしてみる。

SPIフラッシュ

シルクを読むと、25Q128JVSQで有ることがわかる。

データシートから図を引用する。

基板に実装されているSPIフラッシュはSOPで、十分にオシロやテスタでピンにアクセスすることができる位置に実装されている。

libgpiod-toolsを追加

新しい端末を開く場合は、下記を実行する。

$ cd ~/yocto/aki-soc
$ source poky/oe-init-build-env
$ bitbake-layers layerindex-fetch meta-oe

conf/local.confに下記を追加する。

IMAGE_INSTALL:append = " libgpiod-tools"

pin確認用スクリプト

実機の/tmp/test.shとかのファイル名で下記のスクリプトを作成する。

#!/bin/sh

for i in $(seq 0 3); do
    for j in $(seq 0 31); do
        echo gpioset -m wait /dev/gpiochip${i} ${j}=1
        gpioset -m wait /dev/gpiochip${i} ${j}=1
    done
done

実行すると、ピンをHIに出力する毎にEnter入力待ちになるので、 調べたいピンにオシロのプローブをあてた状態で、Enterを連打していく。 オシロが反応したらその時のピン番号をメモる。 つまり虱潰し。

Flash PIN Flash Signal SoC GPIO
1 /CS 2-17
2 DO(IO1) 2-16
3 /WP(IO2) 2-21
4 GND ??
5 DI(IO0) 2-15
6 CLK 2-14
7 /HOLD or /RESET 2-19
8 VCC ??

4ピンと8ピンはGPIOに該当しないはず。これでピンがわかったのでdtsを設定していく。 しかし、ピンファンクションにSPIの割当が無いなど、若干おかしい点があるため調べてみると、 カーネルのバージョンが5.10以前とそれ以降でGPIOピンのバンクの扱いが異なっているらしいことがわかった。

<= 5.10:
gpio0 == gpiochip0
gpio1 == gpiochip32
gpio2 == gpiochip64
gpio3 == gpiochip96

> 5.10
gpio0 == gpiochip96
gpio1 == gpiochip0
gpio2 == gpiochip32
gpio3 == gpiochip64

This is the same for /dev/gpiochipX

This is how it looks since 5.11(using 5.18.16):

/dev/gpiochipXとdtsのgpioXのずれ方は下記のほうが直感的に理解しやすい。

https://forum.digikey.com/t/am335x-device-tree-leds-and-sys-kernel-debug-gpio/26487/2

gpiochip0 = GPIO1
gpiochip1 = GPIO2
gpiochip2 = GPIO3
gpiochip3 = GPIO0

つまり/dev/gpiochip2のオフセット17のピンは、実際にはgpio3のオフセット17になる。

これはgpiochipXのプローブが(高速化のために)非同期になったため、本来はランダムになってしまったらしい。 しかし、処理結果はだいたいいつも同じずれ方になっているとのこと。

下記の情報によると、「dtsでaliasを定義すれば固定できるようになるはず」とのこと。ただし、「GPIOのピン番号の通番を気にするのは(廃止予定の)sysfsだけだろうし、メインラインでは対処されないと思う」ということも言っている。

https://forum.beagleboard.org/t/wrong-order-of-gpiochip0-3/33852/4

とはいえ、dts上のバンク+オフセットとlibgpiod上でのバンク+オフセットが異なってしまうのはかなり不便ではないか? これは修正されるべき問題だと思うが。。。

毎回同じ結果になるように見えるとは言え、仕組み的にはランダムに割当たる可能性があるということなので、、gpioinfoの表示結果とam335x-boneblack.dtsの下記の部分を比較してピンと比較し、一致したピンのGPIO番号をdtsに設定することにした。

&gpio0 {
    gpio-line-names =
        "[mdio_data]",
        "[mdio_clk]",
        "P9_22 [spi0_sclk]",
        "P9_21 [spi0_d0]",
        "P9_18 [spi0_d1]",
        "P9_17 [spi0_cs0]",
        "[mmc0_cd]",
        "P8_42A [ecappwm0]",
        "P8_35 [lcd d12]",
        "P8_33 [lcd d13]",
        "P8_31 [lcd d14]",
        "P8_32 [lcd d15]",
        "P9_20 [i2c2_sda]",
        "P9_19 [i2c2_scl]",
        "P9_26 [uart1_rxd]",
        "P9_24 [uart1_txd]",
        "[rmii1_txd3]",
        "[rmii1_txd2]",
        "[usb0_drvvbus]",
        "[hdmi cec]",
        "P9_41B",
        "[rmii1_txd1]",
        "P8_19 [ehrpwm2a]",
        "P8_13 [ehrpwm2b]",
        "NC",
        "NC",
        "P8_14",
        "P8_17",
        "[rmii1_txd0]",
        "[rmii1_refclk]",
        "P9_11 [uart4_rxd]",
        "P9_13 [uart4_txd]";
};

&gpio1 {
    gpio-line-names =
... snip ...
};

&gpio2 {
    gpio-line-names =
... snip ...
};

&gpio3 {
    gpio-line-names =
... snip ...
};

その結果、SPIフラッシュとSoCのピンの関係は下記の様になった。

Flash PIN Flash Signal SoC GPIO
1 /CS 3-17
2 DO(IO1) 3-16
3 /WP(IO2) 3-21
4 GND ??
5 DI(IO0) 3-15
6 CLK 3-14
7 /HOLD or /RESET 3-19
8 VCC ??

SPIフラッシュのためのdtsの設定は下記のようになる。

/* SPI Flash */
&am33xx_pinmux {
    spi1_pins: pinmux_spi_gpio {
        pinctrl-single,pins = <
            AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT_PULLUP, MUX_MODE3)     /* sck */
            AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT_PULLUP, MUX_MODE3)       /* miso */
            AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT_PULLUP, MUX_MODE3)       /* mosi */
            AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_INPUT_PULLUP, MUX_MODE3)    /* /cs */
        >;
    };
};

&spi1{
    pinctrl-names = "default";
    pinctrl-0 = <&spi1_pins>;
    status = "okay";
    ti,pindir-d0-out-d1-in;

    serial_flash: m25p80@0 {
        compatible = "jedec,spi-nor";
        spi-max-frequency = <48000000>;
        reg = <0x0>;
        m25p,fast-read;
        status = "okay";
        #address-cells = <1>;
        #size-cells = <1>;
    };
};

/RESETと/WPのピンもケアするべきかもしれないが、現状動作しているのでとりあえずこのまま実装する。 ケアするとしたら、該当のピンをpinmuxでMODE 7に設定して、HOGで踏みっぱなしにするんだと思う。

パッチの作成

下記の内容で/tmp/0001-dts-Add-the-spi-nor-node.patchを作成する。

From 86cbfc26264a74a6d466516d2e14dd034c3f08d5 Mon Sep 17 00:00:00 2001
From: Yusuke Mitsuki <mickey.happygolucky@gmail.com>
Date: Thu, 23 Mar 2023 16:29:30 +0900
Subject: [PATCH] dts: Add the spi-nor node

Signed-off-by: Yusuke Mitsuki <mickey.happygolucky@gmail.com>
---
 arch/arm/boot/dts/am3352-aki.dts | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/arm/boot/dts/am3352-aki.dts b/arch/arm/boot/dts/am3352-aki.dts
index 47b7ff75b8cf..ada682fd5cef 100644
--- a/arch/arm/boot/dts/am3352-aki.dts
+++ b/arch/arm/boot/dts/am3352-aki.dts
@@ -5,3 +5,32 @@
 &pruss_tm {
    status = "disabled";
 };
+
+/* SPI Flash */
+&am33xx_pinmux {
+   spi1_pins: pinmux_spi_gpio {
+       pinctrl-single,pins = <
+           AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT_PULLUP, MUX_MODE3)     /* sck */
+           AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT_PULLUP, MUX_MODE3)       /* miso */
+           AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT_PULLUP, MUX_MODE3)       /* mosi */
+           AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_INPUT_PULLUP, MUX_MODE3)    /* /cs */
+       >;
+   };
+};
+
+&spi1{
+   pinctrl-names = "default";
+   pinctrl-0 = <&spi1_pins>;
+   status = "okay";
+   ti,pindir-d0-out-d1-in;
+
+   serial_flash: m25p80@0 {
+       compatible = "jedec,spi-nor";
+       spi-max-frequency = <48000000>;
+       reg = <0x0>;
+       m25p,fast-read;
+       status = "okay";
+       #address-cells = <1>;
+       #size-cells = <1>;
+   };
+};
-- 
2.34.1

cfgファイルの作成

下記の内容で/tmp/spinor.cfgを作成する。

CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set
CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y
# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set
CONFIG_SPI_MEM=y

レシピの修正

次のコマンドを実行する。

$ recipetool appendsrcfile ./meta-am3352-aki virtual/kernel /tmp/0001-dts-Add-the-spi-nor-node.patch
$ recipetool appendsrcfile ./meta-am3352-aki virtual/kernel /tmp/spinor.cfg

1回のコマンドで複数のソースファイルを追加できるappendsrcfilesも存在するが、出来上がりのファイルがキレイじゃないので筆者はあまり使用しない。

動作確認

mtd-toolsを追加

SPIフラッシュの動作を確認するためにmtd-toolsのパッケージを追加する。そのためにはconf/local.confに下記の行を追加する。

IMAGE_INSTALL:append = " mtd-utils"

読み書き確認

作成したイメージでターゲットを起動する。

root@am3352-aki:~# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 01000000 00001000 "spi1.0"

フラッシュのサイズが16MiBなので次のようにしてテスト用データを作成する。

# dd if=/dev/random of=/tmp/test.dat bs=1M count=16

テスト用データをライト、リードして比較する。

$ flashcp /tmp/test.dat /dev/mtd0
$ mtd_debug read /dev/mtd0 0 16777216 /tmp/read.dat
$ cmp /tmp/test.dat /tmp/read.dat

差分がない場合は何も出力されない。逆に差分がある場合は差分のある位置が表示される。 書き込んだものと読み込んだものに差分がなければ、そのストレージデバイスは問題なく動いていると言える。

まとめ

SPI Flashが使用できるようになった。

  • libgpiod-toolsのgpiosetは便利
  • ただし、AM335xのGPIOはバンク番号に注意が必要
  • /RESETと/WPはケアが必要かもしれない
  • 手元では問題なく読み書きができている