ZYNQのSPIをLinuxから使うspidevの解析
ユーザモードドライバのspidevがZYNQ Linuxでも動くことがわかったので、中身を解析してみましょう。
spidevというドライバのソースは、Linuxカーネルの drivers/spi/spidev.cにあります。このドライバの全体的な構造を次の図に示します。
spidev.cは、ユーザプログラムからのioctl()によるリクエストを受け付けて、それをメッセージ化して、バスドライバであるspi.cに渡します。
spi.cは、SPIへのメッセージ化された要求を、低レベルな物理ドライバへ投げるということを行っています。
実際にZYNQのレジスタを操作しているのは、spi-cadence.cという低レベルな物理ドライバです。cadenceというのは、IPコアで有名なケイデンス社のことでしょう。ZYNQのSPIはcadence社のIPコアを元に作られていると推定されます。spi_cadence.cはLinux起動時にcdns_*で始まる関数をspi.cに登録し、処理が必要なときに呼び出されるようになっています。
登録されている関数の機能を調べてみました。
- cdns_prepare_transfer_hardware()
- ZYNQのレジスタを操作して、SPIを有効にする
- cdns_unprepare_transfer_hardware()
- ZYNQのレジスタを操作して、SPIを無効にする
- cdns_prepare_message()
- クロックモード(SPIモード0~3)の設定
- cdns_transfer_one()
- クロック周波数を設定する
- FIFOに送信データを詰めて、送信を行う
- cdns_spi_chipselect()
- CSを上げ下げする
- is_decoded_csが0ならば、全チャネルを有効にする
- cdns_spi_irq()
- 受信割込み処理
spidevで送受信を要求するとSPIのメッセージが作られて、それを元にspi-cadenceドライバが動きます。
具体的な動作としては、メッセージを処理する前にcdns_spi_chipselect()が呼ばれてSSを0にし、メッセージにしたがって複数のSPIトランザクションを送受信し、すべてのメッセージが処理されたらSSを1に戻します。
送信と受信は同時に行われますが、送受信の完了は割込みを使って判断しています。そのため、割込みが発生しないと適当な時間でタイムアウトするようになっています。
spidevにはいくつかのオプションが設定できますが、SPI_IOC_WR_LSB_FIRSTは効きません。それどころか、
uint8_t lsbfirst = 1; ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsbfirst);
というコードを実行すると、
spidev spi1.1: setup: unsupported mode bits 8
というエラーが表示されます。(bit 8というのは、LSB_FIRSTというモードのことを言っているので、8bitモードとか16bitモードということではありません。)
また、SPI_IOC_WR_BITS_PER_WORDというオプションはZYNQのSPIでは効果はなく、常に8bit単位となるようです。
また、複数の長さのメッセージを送っても、CSの上げ下げは1回しか起こりません。メッセージを処理するたびにCSを上げ下げするわけではないようです。
最初の1バイトはコマンド、2バイト目からは受信と送信が切り替わる、というのは実現できないので、3-WireのSPIは扱えません。
結局のところ、使えるオプションは、クロックの速度設定と、SPIモード0~3の設定のみでした。
spidevを使おうとして困るのは、デバイスの名前がspidev32766.0というふうに大きな数字になってしまうことです。しかも、この値はシステムによって変わります。
この値をユーザが決めるには、device treeでaliasを設定すればよいようです。
aliases { spi1 = "/amba/spi@e0006000"; };
とすれば、ZYNQ PSのSPI0が/dev/spidev1.0 として認識されるようになります。(spi0はqspi用に既に使っている)
ZYNQのSPIには3個のSSがあって、デフォルトのSSではなく、SS1やSS2を使いたい場合はdevicetreeに
spidev@0x01 {
compatible = "spidev";
spi-max-frequency = <1000000>;
reg = <1>;
};
を追加すればよいようです。
こうしてみたところ、起動時にspi1.0、spi1.1、spi1.2が登録されました。
しかし、割込みが発生しないので、現時点では使えないようです。
送信してみると、
spidev spi1.1: SPI transfer timed out
というエラーが出てしまいます。
まとめると、ZYNQのSPIをspidevで操作するときには、
- 4線式のスタンダードなSPI。3線式はサポートしない。
- CSはSS0のみ使用可能
- LSB Firstにはできない
- クロック周波数は変更可能
- SPIモードの設定は可能
- 常に8bit単位
- device treeにaliasを設定すれば、/dev/spidev1.0でアクセスできる
ということでした。
| 固定リンク
コメント