search
LoginSignup
0
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

FreeBSD Advent Calendar 2016 Day 12

posted at

updated at

gpiospiを使ってみた

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

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

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

device      gpiospi

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

なぜFON2100をターゲットしたかというとちょうど4本のGPIOのパターンが基板にあるからです。もちろん自分がポートしたar531xってのもあります。
FON2100_GPIO1.jpg
ビルドしたカーネルを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
delay.jpg
でも、まだうまくいきません。

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

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

余談ですが、gpio上のデバイスの排他制御はおこなわれていないので、gpiospiが組み込まれていても、そのピンをユーザランドからアクセスできます。通常は危険なのでやらない方が良いです。
FON2100_SPI.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
なぜこれが必要だったかというとKSZ8995MAのeherswitchのコードを作ろうと思ったからです。今年中に作れるかな。

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

Aitendoの液晶も試してみました。Onion Omegaに小型液晶をつないでみた

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0
Help us understand the problem. What are the problem?