Raspberry Pi 4のベアメタル環境でVideoCore V3Dレジスタを叩けるようにする

### まえがき
この記事は Raspberry Pi Advent Calendar 2020 の記事です。
https://adventar.org/calendars/5044

今年初頭Raspberry Pi 4 + ベアメタル環境で本格的に遊ぼうと思ったら見事にハマったのでメモ的に切り出して書きます。


* ※Raspberry Piはrpi, rpi4と記載することにします。
* ※raspbianって名前変わっちゃったんですね。Raspberry Pi OSって記載したいですが、便宜上raspbianと記載します...。
* ※資料(ref)は記事末尾付近にあります。

***
### 前提とか環境とか
* rpiシリーズにはVideoCoreというGPUが搭載されていて、やり取りするためのV3Dというブロックがあります。
* GPUを動かすためにはV3Dのレジスタとやり取りする必要があります。
* rpi4で自分で頑張ってkernel.imgを作って動かすような環境を対象としています。raspbianなど誰かが作ったドライバを使わないケースです。
* つまりkernel.imgからは自分で書いた何かしらの小さいプログラムで使います。Linuxにわたったらドライバがよろしくやってくれるでしょう。
* rpi4では従来のVideo Core 4からVideo Core 6に変わってます。この時点で制御方法が違うはずなので動かないのはある程度読み筋ではあったのですが...。
* (正確ではないのですが)2020年2, 3月位に調べていました。

***
### 問題 : rpi4においてfirmware ENABLE_QPUを発行しただけではV3Dが見えない
rpi2はmailbox(※ ref5, 6 参照)のENABLE_QPUリクエストでV3Dのレジスタが見えるようになります(挙動上)
rpi4で同様にENABLE_QPUリクエストを発行後V3D_IDENT0を32bit Readすると以下の通りです。

```
0xDEADBEEF
```
この0xDEADBEEFはレジスタのRead結果です。rpi2でV3DのENABLE_QPUを発行する前と同じ状況です。
私が書いたプログラム側の不定値としてこの値を入れているわけではありません。

つまりV3Dが見えていません。よってV3D側とのやり取り不能で、このままではV3Dを使ったベアメタル環境で遊べません。
記事中、見えないといった場合0xDEADBEEFの値が取れていることを指します。

rpi4の場合、ref8と方々を調べると期待値は以下の通り。

```
0x04443356 //rev4, V3D(LE:D3V)
```

***
### この記事の目標
V3D_IDENT0から期待値```0x04443356```を読めるようにしてV3Dとやり取りできる状態にすることです。






***

### 観察
まずは様子を見ます。

```
mailbox ENABLE QPUリクエストを発行すると
rpi4 : V3D_IDENT0が見えない。さらにmailbox SET_PDOMAINからV3D電源をいれる -> 0xDEADBEEF ※見えない
rpi3 : V3D_IDENT0が見えない。さらにmailbox SET_PDOMAINからV3D電源をいれる -> 0x02443356 ※rev2でみえる。正常。
rpi2 : mailbox ENABLE_QPU で見えるようになる
```

動きがrpiごとに結構違います。この辺りをraspbianは全部吸収しているのですね。

素直にraspbianの様子を見ます。

```
raspbian起動 : V3Dのレジスタが見えている(klogより)
kernel : 32bit / 64bitによらない
raspbian-dt : ~/git/RASPBIAN/linux/arch/arm/boot/dts/bcm2711-rpi.dtsi
  power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>;
  ↑をコメントアウトしてkernel make loadするとV3Dのprobeに失敗する(v3d_drv.c)
  つまりbcm2711-rpi.dtsiをもちろん見ている
vcdbg log msg : ↑のdtはロードされている模様
bcm2835-power.c : bcm2711の場合、is_2711のフラグが立てられて、以下define群で管理されている
BCM2835_POWER_DOMAIN_GRAFX_V3D : https://github.com/torvalds/linux/blob/master/include/dt-bindings/soc/bcm2835-pm.h
bcm2835_asb_power_onでonを行う。PMでreset解除 -> asb master slave stopをdeassertする。

```
という事で調べ方が間違ってなければ

* rpi4ではfirmware側でENABLE_QPUしただけではV3Dにアクセスできず
* ENABLE_QPUでは足らない前準備があって、
* それは、PMでV3Dのリセット解除を行い、さらにASB master, slaveでのアクセスを有効にしてあげることです。

***
### 解決方法
今のところ、firmware側ではV3D側に火をいれられなさそうなので、ARMに制御が渡った後
以下の通り、PM -> Video側ASBからのコントロールでめでたくV3D_IDENT0が見えるようになります。

```c

#define PM_MAGIC         (0x5A000000)
#define PM_GRAFX         (0xFE10010C) //SUBSYSTEM_BASE 0xFE000000
#define PM_GRAFX_V3DRSTN (0x40)
#define ASB_RPIVID_BASE  (SUBSYSTEM_BASE + 0xC11000)
#define ASB_RPIVID_M     (ASB_RPIVID_BASE + 0x8)
#define ASB_RPIVID_S     (ASB_RPIVID_BASE + 0xC)
#define ASB_RPIVID_MAGIC (0x5A000000)

void
v3d_power_on()
{

    uint32_t reg = PM_GRAFX;
    
    //V3DRSTN V3D deassert.
    IO_WRITE(reg, PM_MAGIC | IO_READ(reg) | PM_GRAFX_V3DRSTN);
    usleep(1000);
    
    //ASB Deassert Master Request Stop
    IO_WRITE(ASB_RPIVID_M, ASB_RPIVID_MAGIC | (IO_READ(ASB_RPIVID_M) & ~1));
    usleep(1000);

    //ASB Deassert Slave Request Stop
    IO_WRITE(ASB_RPIVID_S, ASB_RPIVID_MAGIC | (IO_READ(ASB_RPIVID_S) & ~1));
    usleep(1000);
}
```

* 参考ソースやref群を見ていろいろすっきりしたのでraspbian側のsequence + sourceを参考にしました。
* usleepは適宜作ってください...
* sleep時間はかなり大げさに見積もっています。sleep時間もref参考にしています。
* 閉じるときはidle状態の時に逆に操作をしていけば大丈夫なはず(未検証)
* 推測 : V3D側のInterfaceはPDF無いけど多分Video系統の下にぶら下がるようになったのかな...。
* 
これで以下の通りV3D_IDENT0が見えました。
![v3dpoweron.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34428/4d16a403-1556-a2e1-0b8c-ffcf6c4e870c.png)

サンプルは以下です。uart経由で一応ログを出すようにしています。
https://github.com/kumaashi/RaspberryPI/tree/master/RPI4/v3d_power_on

***
### その他
* 時間あればV3D Reset Enable Disable間で消費電力測りたかったな...
* 
rpi2の時は一度ENABLE_QPU叩くと非coldbootですでにV3Dにアクセスできましたが今回みたいにrpi4で簡単にはV3D動かせないことからfirmware単体ではONしないことによって消費電力周り系頑張った結果簡単には見えなくなったのかな...
(推測です。マジで減ってるかは不明だしほかのブロックはどうなってるかは全く調べてません)

***
### まとめとかきっかけとか
rpi4を入手してから[以前書いたポリゴン出してそこから何か遊ぶやつ](https://qiita.com/gyabo/items/f3a411a63d608d00b384)を動かしたら何も動きませんでした。
これが調べるきっかけでした。
今回調べてrpi4でも電源入れてポリゴン出て満足したので終わります。


***
### 資料

ref0 : https://qiita.com/eggman/items/402d8ba20fa021cc4531 ※大変参考にしました。ありがとうございます。
ref1 : https://github.com/raspberrypi/linux 
ref2 : https://github.com/torvalds/linux/
ref3 : https://elinux.org/BCM2835_registers#PM_GRAFX
ref4 : https://github.com/raspberrypi/linux/blob/75f1d14cee8cfb1964cbda21a30cb030a632c3c2/drivers/soc/bcm/bcm2835-power.c#L283
ref5 : https://github.com/raspberrypi/firmware/wiki/Mailboxes
ref6 : https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
ref7 : https://www.RaspberryPI.org/documentation/hardware/RaspberryPI/bcm2835/BCM2835-ARM-Peripherals.pdf
ref8 : https://www.broadcom.com/support/download-search?pg=Legacy+Products&pf=Legacy+Broadcom&pn=BCM21553&pa=Reference+Manual&po=&dk=
other : http://www.broadcom.com/docs/support/videocore/VideoCoreIV-AG100-R.pdf (リンク切れ)

先人の方に感謝です...

※調べ方が間違ってなければ、Broadcomから公式に提供されているRaspberry Pi4の公式なPeripheral系の詳細なドキュメントは今のところホビー向けに無料で参照できるものは無いようにみえます。もしありましたら、可能であればどなたか教えてください...
あと、ちゃんとPDF周りとか公開された暁にはこの記事修正します。

***

### おまけ

ちゃんとしたケースを買いなさいという話ですがrpi系はニベアクリームの缶にすっぽり入るようです。参考までに(?)

![niv.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34428/02f37c54-1239-0afc-446c-a9b1f9c09dd0.jpeg)



本当に終わり。