5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Raspberry Pi 3の SDHOSTを動かす

Last updated at Posted at 2018-05-26

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ファイルに記述します。

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

5
5
0

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
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?