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の設定をします。
# 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は1回に4バイト読めるので、合計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ファイルに記述します。
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