Raspberry Pi 3の SDHOSTを動かす

Raspberry Pi 1 /2 /3 が持っているSDホストコントローラの一つであるSDHOSTをベアメタルで使ってみました。

SDカードの最初のセクタをリードできました。

QEMU 2.12を使って動作確認しています。

ソースコード: https://github.com/eggman/raspberrypi/tree/master/qemu-raspi3/sdhost02

# SDHOST とは
Raspberry Pi 1 /2 /3 には二つのSDホストコントローラが載っています。一つはARASANのIPであるSDHCI(EMMC)で、もうひとつがSDHOSTです。

Raspberry Pi 3ではSDHOSTがSDカードスロット、SDHCI(EMMC)がWi-Fiチップに接続されています。

SDカードを読み書きする場合はSDHOSTの方が性能が良いらしいです。

SDHOSTのレジスタ仕様は公開されていませんが、ドライバのソースコードを見たり、SDHCIのレジスタ仕様を見れば、使い方は大体把握できました。。

# GPIOの設定

QEMUの場合、起動後のデフォルト設定ではSDHCIがSDカードと接続されています。

接続の状態はQEMUのモニタで確認できます。

```
$ qemu-system-aarch64 -M raspi3 -m 128 -nographic -S 
Ctrl-a c を入力してモニタモードへ入る
QEMU 2.12.0 monitor - type 'help' for more information
(qemu) info qtree
...
  dev: bcm2835-sdhost, id ""
    gpio-out "sysbus-irq" 1
    mmio ffffffffffffffff/0000000000001000
    bus: sd-bus
      type bcm2835-sdhost-bus
  dev: generic-sdhci, id ""
    gpio-out "sysbus-irq" 1
    sd-spec-version = 3 (0x3)
    uhs = 0 (0x0)
    capareg = 86062260 (0x52134b4)
    maxcurr = 0 (0x0)
    pending-insert-quirk = true
    dma = ""
    mmio ffffffffffffffff/0000000000000100
    bus: sd-bus
      type sdhci-bus
      dev: sd-card, id ""
        drive = "sd0"
        spi = false
...
```

SDHOSTにSDカードを接続するためにはGPIOの設定をします。

```c
#define GPFSEL4         ((volatile uint32_t*)(0x3F200010))
#define GPFSEL5         ((volatile uint32_t*)(0x3F200014))

    // GPIO_CLK, GPIO_CMD
    r=*GPFSEL4; r|=(4<<(8*3))|(4<<(9*3)); *GPFSEL4=r;

    // GPIO_DAT0, GPIO_DAT1, GPIO_DAT2, GPIO_DAT3
    r=*GPFSEL5; r|=(4<<(0*3)) | (4<<(1*3)) | (4<<(2*3)) | (4<<(3*3)); *GPFSEL5=r;
```

GPIOの設定後、モニタで確認すると、SDHOSTにSDカードが接続されています。

```
...
  dev: bcm2835-sdhost, id ""
    gpio-out "sysbus-irq" 1
    mmio ffffffffffffffff/0000000000001000
    bus: sd-bus
      type bcm2835-sdhost-bus
      dev: sd-card, id ""
        drive = "sd0"
        spi = false
  dev: generic-sdhci, id ""
    gpio-out "sysbus-irq" 1
    sd-spec-version = 3 (0x3)
    uhs = 0 (0x0)
    capareg = 86062260 (0x52134b4)
    maxcurr = 0 (0x0)
    pending-insert-quirk = true
    dma = ""
    mmio ffffffffffffffff/0000000000000100
    bus: sd-bus
      type sdhci-bus
...
```



# ハードウェアの初期化

* SDVDD に 0を書いて、1を書くとハードウェアがリセットされます。
* SDTOUTにタイムアウトの初期値を代入。
* HDSTSの使用するビットに1を書き込みクリア。
* 書き込み可能なレジスタに0を代入。

```
    // init hardware
    *SDVDD = SDVDD_POWER_OFF;
    *SDCMD = 0;
    *SDARG = 0;
    *SDTOUT = 0xF00000;
    *SDCDIV = 0;
    *SDHSTS = 0x7F9;
    *SDHCFG = 0;
    *SDHBCT = 0;
    *SDHBLC = 0;

    wait_msec(20);
    *SDVDD = SDVDD_POWER_ON;
    wait_msec(20);
```

# コマンドの送信

基本のSDのコマンド送信手順

* SDコマンドの引数をSDARGに代入
* SDコマンドのコマンド番号を下位6bitに、SDCMD_NEWフラグを立ててSDCMDに書き込むとコマンド送信を開始。

```
*SDARG = arg;
*SDCMD = code_num & SDCMD_CMD_MASK | SDCMD_NEW;
```

# レスポンスの受信

基本のレスポンス受信

* SDRSP0を読み出す。

```
r =  *SDRSP0;
```

# データの受信

SDカードのシングルリード CMD17の手順です。

ブロックサイズとブロック個数を設定します。QEMUの場合、この順番に意味があるので注意すること。SDHBLCを後にする。
ブロックサイズ 512、ブロック個数1の場合。

```
*SDHBCT = 512;
*SDHBLC = 1;
```

CMD17を、READフラグを立てて送信。

```
*SDARG = 0;
*SDCMD = 17 | SDCMD_READ | SDCMD_NEW;
```

*DATAから512バイト分のデータを読みこむ。*SDDATAは１回に４バイト読めるので、合計128回読み込む。

```
uint32_t buf[128];
for (int i; i<128; i++) {
    buf[i] = *SDDATA;
}
```

# SDカードの最初のセクタを読むまでのコマンドシーケンス

以下の順でコマンドを送信すると最初のセクタが読み出せます。

```
 CMD0   カードをIDLEにする
 CMD8
 CMD55
 ACMD41 
 CMD2   CIDを取得
 CMD3   RCAを取得
 CMD7   カードをセレクト
 CMD55
 ACMD51 ブロックサイズ8 ブロック個数1
 データを8バイト受信 これはSCR (SD Configuration Register)
 CMD17 ブロックサイズ512 ブロック個数1
 データを512バイト受信 これが最初のセクタ
```

# QEMUでSDカードとSDバスのトレース

QEMUのトレース機能を使うためには、コンパイルが必要です。

```
$ git clone git://git.qemu.org/qemu.git
$ cd qemu
$ git checkout -b v2.12.0-release refs/tags/v2.12.0
$ git submodule update --init

$./configure --prefix=`pwd`/build --target-list=arm-softmmu,aarch64-softmmu --audio-drv-list=alsa --enable-trace-backend=log

$ make
$ make install
```


```--enable-trace-backend=log``` だと、標準エラー出力にトレースが出力されます。

SDカードとSDバスのトレースをする設定をeventsファイルに記述します。

```:events
sdcard_*
sdbus_*
```


```-trace events=events``` をコマンドライン引数に追加して qemuを起動します。


```
qemu-system-aarch64 -M raspi3 -m 128 -serial mon:stdio -nographic -kernel kernel.elf -drive if=sd,id=sd0,format=raw,file=sd.img -trace events=events
7413@1527370601.895590:sdcard_reset 
7413@1527370601.896617:sdcard_reset 
sdhost02
flg  8400 cmd 0 arg 0
7413@1527370601.923282:sdbus_command @sd-bus CMD00 arg 0x00000000 crc 0x00
7413@1527370601.923562:sdcard_normal_command SD        GO_IDLE_STATE/ CMD00 arg 0x00000000 (state idle)
7413@1527370601.923584:sdcard_reset 
7413@1527370601.923596:sdcard_response RESP#0 (no response) (sz:0)
flg  8000 cmd 8 arg 1AA
7413@1527370601.923875:sdbus_command @sd-bus CMD08 arg 0x000001aa crc 0x00
7413@1527370601.923893:sdcard_normal_command SD         SEND_IF_COND/ CMD08 arg 0x000001aa (state idle)
7413@1527370601.923899:sdcard_response RESP#7 (operating voltage) (sz:4)
resp 0 0 0 1AA
state 0 APP_CMD 
flg  8000 cmd 37 arg 0
7413@1527370601.924490:sdbus_command @sd-bus CMD55 arg 0x00000000 crc 0x00
7413@1527370601.924503:sdcard_response RESP#1 (normal cmd) (sz:4)
resp 0 0 0 120
state 0 APP_CMD 
flg  8000 cmd 29 arg 51FF8000
7413@1527370601.924822:sdbus_command @sd-bus CMD41 arg 0x51ff8000 crc 0x00
7413@1527370601.924836:sdcard_app_command SD         SD_SEND_OP_COND/ACMD41 arg 0x51ff8000 (state idle)
7413@1527370601.924843:sdcard_powerup 
7413@1527370601.924851:sdcard_response RESP#3 (OCR reg) (sz:4)
resp 0 0 0 80FFFF00
flg  8200 cmd 2 arg 0
7413@1527370601.925200:sdbus_command @sd-bus CMD02 arg 0x00000000 crc 0x00
7413@1527370601.925212:sdcard_normal_command SD         ALL_SEND_CID/ CMD02 arg 0x00000000 (state ready)
7413@1527370601.925224:sdcard_response RESP#2 (CID reg) (sz:16)
resp AA585951 454D5521 1DEADBE EF006219
flg  8000 cmd 3 arg 0
7413@1527370601.925583:sdbus_command @sd-bus CMD03 arg 0x00000000 crc 0x00
7413@1527370601.925592:sdcard_normal_command SD   SEND_RELATIVE_ADDR/ CMD03 arg 0x00000000 (state identification)
7413@1527370601.925597:sdcard_response RESP#6 (RCA) (sz:4)
resp 0 0 0 45670500
state 2
flg  8800 cmd 7 arg 45670000
7413@1527370601.926026:sdbus_command @sd-bus CMD07 arg 0x45670000 crc 0x00
7413@1527370601.926036:sdcard_normal_command SD SELECT/DESELECT_CARD/ CMD07 arg 0x45670000 (state standby)
7413@1527370601.926044:sdcard_response RESP#1 (normal cmd) (sz:4)
resp 0 0 0 700
state 3
flg  8000 cmd 37 arg 45670000
7413@1527370601.926280:sdbus_command @sd-bus CMD55 arg 0x45670000 crc 0x00
7413@1527370601.926288:sdcard_response RESP#1 (normal cmd) (sz:4)
resp 0 0 0 920
state 4 APP_CMD 
flg  8040 cmd 33 arg 0
7413@1527370601.926687:sdbus_command @sd-bus CMD51 arg 0x00000000 crc 0x00
7413@1527370601.926695:sdcard_app_command SD                SEND_SCR/ACMD51 arg 0x00000000 (state transfer)
7413@1527370601.926738:sdcard_response RESP#1 (normal cmd) (sz:4)
7413@1527370601.926745:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926752:sdbus_read @sd-bus value 0x00
7413@1527370601.926758:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926762:sdbus_read @sd-bus value 0x25
7413@1527370601.926766:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926769:sdbus_read @sd-bus value 0x00
7413@1527370601.926774:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926779:sdbus_read @sd-bus value 0x00
7413@1527370601.926791:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926796:sdbus_read @sd-bus value 0x00
7413@1527370601.926801:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926811:sdbus_read @sd-bus value 0x00
7413@1527370601.926816:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926821:sdbus_read @sd-bus value 0x00
7413@1527370601.926825:sdcard_read_data SD             SEND_SCR/ CMD51 len 512
7413@1527370601.926829:sdbus_read @sd-bus value 0x00
resp 0 0 0 920
state 4 APP_CMD 
SCR 2500 0 
flg  8040 cmd 11 arg 0
7413@1527370601.927241:sdbus_command @sd-bus CMD17 arg 0x00000000 crc 0x40
7413@1527370601.927253:sdcard_normal_command SD    READ_SINGLE_BLOCK/ CMD17 arg 0x00000000 (state transfer)
7413@1527370601.927260:sdcard_response RESP#1 (normal cmd) (sz:4)
7413@1527370601.927267:sdcard_read_data SD         UNKNOWN_ACMD/ CMD17 len 512
7413@1527370601.927274:sdcard_read_block addr 0x0 size 0x200
7413@1527370601.929048:sdbus_read @sd-bus value 0x53
7413@1527370601.929077:sdcard_read_data SD         UNKNOWN_ACMD/ CMD17 len 512
```

このログで、```7413@``` で始まる行がトレースの出力です。　SDバス上のコマンドとデータのやりとりを観察することができます。

# はまりどころ

* CMD17のレスポンスがエラーになって、データが読みだせなかった。
* QEMUのソースコードを読んでみたが、ダメ。
* SDHCIの場合はSDカードのセクタが読めていたので、同じ順番にコマンドを送信していたが、ダメ。
* 一念発起してトレースをつかってみることにした。
* トレースの設定方法が分からなかったが、ソースコードにtrace_sdcard_read_data()という関数がある場合は sdcard_read_data と書くと、trace_sdcard_read_data（）のトレースが出力される。
* ```sdcard*``` と書くと関連のトレースがまとめて出力される。
* SDHCIでセクタリードが動いているコードをトレースさせて比較してみたら、原因がすぐ分かった。
* ACMD51の後に、データリードをしていなかったのが原因。 
* ACMD51の後に、データリードをしてSCRを読み切らないと、SDカードの内部ステートが ```sd_transfer_state```に変わらないです。該当部のQEMUソースコード

```
    case 51:    /* ACMD51: SEND_SCR */
        ret = sd->scr[sd->data_offset ++];

        if (sd->data_offset >= sizeof(sd->scr))
            sd->state = sd_transfer_state;
        break;
```

* ACMD51後のデータリードを追加したら、最初のセクタがリードできるようになりました。


# todo
* QEMUでは動いたけど、ウェイトとステータスチェックが足りないので実機では動かないので、コードを追加する。


# 参考情報

LinuxカーネルのSDHOSTドライバソースコード
 https://github.com/torvalds/linux/blob/master/drivers/mmc/host/bcm2835.c
NetBSDのSDHOSTドライバソースコード
 https://github.com/NetBSD/src/blob/trunk/sys/arch/arm/broadcom/bcm2835_sdhost.c
u-bootのSDHOSTドライバソースコード
 https://github.com/u-boot/u-boot/blob/master/drivers/mmc/bcm2835_sdhost.c
QEMUのSDHOSTエミュレーションソースコード
 https://github.com/qemu/qemu/blob/master/hw/sd/bcm2835_sdhost.c

ベアメタルのSDHCIのセクタリード
 https://github.com/bztsrc/raspi3-tutorial/tree/master/0B_readsector