gpiospiを使ってみた

今年の5月にsys/dev/gpio/gpiospi.cというコードがheadに入りました。このコードはZRouterのrayさんが作ったもので、[gpioiic](http://qiita.com/yamori813/items/1d1c40ce69fe6e60ce1c)と同様にgpioを使ってbitbangでspibusを実現するコードになります。

今のところ、このコードはhintsベースで作られていてFDT対応はおこなわれていません。

利用するにはカーネルのコンフィグレーションに以下を追加します。

```
device      gpiospi
```

とりあえずこのコードをFON2100用のビルドにつっこんでみました。

なぜFON2100をターゲットしたかというとちょうど4本のGPIOのパターンが基板にあるからです。もちろん自分がポートしたar531xってのもあります。
![FON2100_GPIO1.jpg](https://qiita-image-store.s3.amazonaws.com/0/104066/9a735462-1469-f267-b0bf-b1a405ee1ab3.jpeg)
ビルドしたカーネルをredbootでloadしてexecすると、ar531xのspiの前にgpiospiがprobeされspibusがaddされてしまいmx25lが見えなくなりrootfsがマウントできなくなりました。いろいろhintsを書いて試したのですがうまくいきません。

spibus1がでてきません。spibusはspiのコードで直にdevice_add_childされています。よく見直してみると自分の書いたar5315_spi.cに問題がありました。

```
       device_add_child(dev, "spibus", 0);
```

これだとspibus0として追加される事になりgpiospiが先にspibus0を追加しているので追加できません。マイナーナンバーをインクリメントしてaddするには第三引数を-1にする必要がありました。SOCではbusが増える事は想定していないで、決めうちにしちゃってるんですが、こういう問題が起きちゃうんですね。

これでhintsのmx25lをspibus1にしてgpiospiを追加して、mx25lが見えてrootfsのマウントができるようになりました。

gpiospiのhintsの書き方は以下のようになります。

```
hint.gpiospi.0.at="gpiobus0"
hint.gpiospi.0.pins=0x9a
hint.gpiospi.0.cs0=0
hint.gpiospi.0.sclk=3
hint.gpiospi.0.mosi=1
hint.gpiospi.0.miso=2
```

pinsはビットフィールドでcs0などはビットの順番になります。gpioiicと同じです。

これで作ったカーネルを起動してもcs0などが全く出力されません。調べてみるとgpiospi.cのGPIOBUS_PIN_SETFLAGSしているところで、GPIO_PIN_PULLUPなどを指定していて、実際に呼び出されるgpiobus.cを読むと、これに対応していないar5315_spi.cでは設定がおこなわれないためでした。このためとりあえずGPIO_PIN_PULLUPなどを外しました。

信号線は出るようになったのですがSPIスレーブからの返答がありません。オシロで信号線を見たところCLKがLOに落ちる前にHIになっているような状態で、奇麗な波形になっていません。モード0の場合にsc_sclkを0にした後にgpio_delay(sc)を入れてHI/LOがきっちり出るようにしてみました。
![nodelay.jpg](https://qiita-image-store.s3.amazonaws.com/0/104066/a14693a1-82a0-79d8-083c-98705f2ee65b.jpeg)
![delay.jpg](https://qiita-image-store.s3.amazonaws.com/0/104066/2a9973c2-377f-cbb7-24cf-c3c7365c708b.jpeg)
でも、まだうまくいきません。

カーネル内での確認は手間がかかるのでユーザーランドのmrubyの[スクリプト](https://gist.github.com/yamori813/40efce7ddd4d0b0bb9da557a0367a8a1)でgpioを直接叩いて確認してみる事にしました。これでもうまくいきません。SPIスレーブの機器はArduinoで動作を確認していたので、もはや電気的な問題ぐらいしか考えられなく、プルアップなども試しましたがうまくいかなくて、バッファに74HC126を入れてMOSIをプルアップしたら動くようになりました。

もともとFON2100のGPIO1などは10Kでプルダウンされているのですが、これを外してしまっていたために、プルアップが必要になりました。

余談ですが、gpio上のデバイスの排他制御はおこなわれていないので、gpiospiが組み込まれていても、そのピンをユーザランドからアクセスできます。通常は危険なのでやらない方が良いです。
![FON2100_SPI.png](https://qiita-image-store.s3.amazonaws.com/0/104066/4c7d0d17-1469-ba64-539e-eff369e129e6.png)
ユーザーランドでうまくいったので、カーネルで試したら以下を設定したらうまくいきました。

```
hint.gpiospi.0.freq=100000000
```

bitbangのクロック（スピード）はデバイスやホストによって変わってくるのでトライ＆エラーになってしまいます。gpiospi.cの中のgpio_delay()は上記のhintの値を10000000で割っているのでDELAY(10)になるのですが、ちょっと分かりにくいですね。

起動ログはこんな感じでspibus0にもデバイスが追加できる状態になります。

```
gpio0: <Atheros AR531x GPIO driver> on apb0
gpio0: [GIANT-LOCKED]
gpio0: gpio pinmask=0x7fffff
gpiobus0: <GPIO bus> on gpio0
gpioled2: <GPIO led> at pin 2 on gpiobus0
gpiospi0: <GPIO SPI bit-banging driver> at pins 1,3-4,7 on gpiobus0
spibus0: <spibus bus> on gpiospi0
spibus0: <unknown card> at cs 0 mode 0
gpioc0: <GPIO controller> on gpio0
ar5315_wdog0: <Atheros AR531x watchdog timer> on apb0
spi0: <AR5315 SPI> at mem 0x11300000-0x1130000b on nexus0
spibus1: <spibus bus> on spi0
mx25l0: <M25Pxx Flash Family> at cs 0 mode 0 on spibus1
mx25l0: m25p64, sector 65536 bytes, 128 sectors
```

カーネル内でSPIをreadするコードはこんな風になります。

```
        uint8_t txBuf[8], rxBuf[8];
        struct spi_command cmd;
        int err;

        memset(&cmd, 0, sizeof(cmd));
        memset(txBuf, 0, sizeof(txBuf));
        memset(rxBuf, 0, sizeof(rxBuf));  

        /* read spi */
        txBuf[0] = 0x03;
        txBuf[1] = 0x00;
        cmd.tx_cmd = &txBuf;
        cmd.rx_cmd = &rxBuf;
        cmd.tx_cmd_sz = 3;
        cmd.rx_cmd_sz = 3;
        err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
        if (err)
                return(0);
 
        printf("SPI Trans %x¥n", rxBuf[2]);
```

spiがあるデバイスでgpiospiを追加すると、元々のspiがspibus1となりgpiospiがspibus0となります。これだとflashのmx25lの親が変わってしまい、ちょっと不便です。逆にする方法を考えてみたのですが、良い解決策が思いつきません。

ワイヤリングはこんな感じにしてみました。
![写真（2018-09-23 9.30） #2.jpg](https://qiita-image-store.s3.amazonaws.com/0/104066/64bfc14b-a9b8-aa23-484d-fb66e1918a9b.jpeg)
なぜこれが必要だったかというとKSZ8995MAのeherswitchのコードを作ろうと思ったからです。今年中に作れるかな。

この修正のレビュー出してみました。

https://reviews.freebsd.org/D8749

Aitendoの液晶も試してみました。[Onion Omegaに小型液晶をつないでみた](http://qiita.com/yamori813/items/ae66330ccbf6fe6bd5ab)