今年の5月にsys/dev/gpio/gpiospi.cというコードがheadに入りました。このコードはZRouterのrayさんが作ったもので、gpioiicと同様にgpioを使ってbitbangでspibusを実現するコードになります。
今のところ、このコードはhintsベースで作られていてFDT対応はおこなわれていません。
利用するにはカーネルのコンフィグレーションに以下を追加します。
device gpiospi
とりあえずこのコードをFON2100用のビルドにつっこんでみました。
なぜFON2100をターゲットしたかというとちょうど4本のGPIOのパターンが基板にあるからです。もちろん自分がポートしたar531xってのもあります。
ビルドしたカーネルを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がきっちり出るようにしてみました。
でも、まだうまくいきません。
カーネル内での確認は手間がかかるのでユーザーランドのmrubyのスクリプトでgpioを直接叩いて確認してみる事にしました。これでもうまくいきません。SPIスレーブの機器はArduinoで動作を確認していたので、もはや電気的な問題ぐらいしか考えられなく、プルアップなども試しましたがうまくいかなくて、バッファに74HC126を入れてMOSIをプルアップしたら動くようになりました。
もともとFON2100のGPIO1などは10Kでプルダウンされているのですが、これを外してしまっていたために、プルアップが必要になりました。
余談ですが、gpio上のデバイスの排他制御はおこなわれていないので、gpiospiが組み込まれていても、そのピンをユーザランドからアクセスできます。通常は危険なのでやらない方が良いです。
ユーザーランドでうまくいったので、カーネルで試したら以下を設定したらうまくいきました。
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の親が変わってしまい、ちょっと不便です。逆にする方法を考えてみたのですが、良い解決策が思いつきません。
ワイヤリングはこんな感じにしてみました。
なぜこれが必要だったかというとKSZ8995MAのeherswitchのコードを作ろうと思ったからです。今年中に作れるかな。
この修正のレビュー出してみました。
Aitendoの液晶も試してみました。Onion Omegaに小型液晶をつないでみた