LoginSignup
6
2

More than 5 years have passed since last update.

SDIOについて

Last updated at Posted at 2018-06-08

SDIOについて

SDIOエミュレータを書いているので、情報をまとめています。 まだ途中です。

マイルストーン

CMD53のマルチブロックライト:
CMD53のマルチブロックリード:
CMD53のシングルブロックライト: 2018-06-24
CMD53のマルチバイトライト: 2018-06-24
CMD53のシングルブロックリード: 2018-06-24
CMD53のマルチバイトリード: 2018-06-22
Linuxカーネルが各ファンクションのCISを読み出す:2018-06-19
LinuxカーネルがFBRを読み出す:2018-06-16
LinuxカーネルがCommon CISを読み出す:2018-06-16
LinuxカーネルがCCCRを読み出す:2018-06-16

SDIOで使うコマンド

最低限の動作に必要なコマンド

CMD0とCMD3とCMD7はSD仕様書に記載。
CMD5とCMD52とCMD53はSDIO仕様書に記載。

  • CMD0 GO_IDLE_STATE
    • カードをIDLE状態にする。
    • レスポンンス無し
  • CMD3 SEND_REL_ADDR
    • 相対アドレスをホストが取得する。
    • レスポンスはR6 相対アドレス、ただしSDIOのみ対応のカードだとステータスビットの取り扱いが異なる。
  • CMD5 IO_SEND_OP_COND
    • I/O OCR (24bit)などをやりとりする。カードが対応している電圧についての情報が記述されている。
    • SDIOの初期化を開始する。ReadyになるとCが1になる。
    • SDIOのファンクションの数が分かる。
    • レスポンスはR4 I/O OCR
  • CMD7 SELECT/DESELECT_CARD
    • CMD3で取得した相対アドレスを送信し、カードを選択する。
    • カード選択をするとCommand Stateに遷移する。
    • レスポンスはR1b
  • CMD52 IO_RW_DIRECT
    • 1バイトのデータを読み書きする。
    • 信号線はCMDのみを使う。DAT0-3の信号線は使わない。
    • レスポンスはR5
  • CMD53 IO_RW_EXTENDED
    • マルチバイト、マルチブロックのデータを読み書きする。
    • 1bit widthの場合はDAT0信号線を使う。4bit widthの場合DAT0-3の信号線を使う。
    • レスポンスはR5

SDIO初期化シーケンス

LinuxのSDIOをトレースして解析

  • CMD52 FN0の0x6を読む stateが違うので応答しない
  • CMD52 FN0の0x6に8を書き込む stateが違うので応答しない
  • CMD0 GO_IDLE_STATE
  • CMD8 SEND_IF_COND SDメモリなら応答するが、SDIOなので応答しない
  • CMD5 IO_SEND_OP_COND SDIOなら応答
  • CMD5 IO_SEND_OP_COND 電圧を指定
  • CMD3 SEND_RELATIVE_ADDR
  • CMD7 SD SELECT/DESELECT_CARD

CMD7 CARD SELECT後はTransfer Stateになるので、CMD52とCMD53が使えるようになる。

SDIO State

SDIOカードのステート

SDIOカードを主体として考える。

  • Initialize State
    • 初期状態
    • CMD5 を受け付ける
    • CMD3を受信すると、Standbyに遷移
  • Standby State
    • CMD7を受け付ける
    • 正しい相対アドレスのCMD7を受信するとCommandに遷移
  • Command State
    • カードが選択されデータ転送コマンドを受け付ける状態
    • CMD52とCMD53を受け付ける。
  •  Transfer State
    • CMD53のデータ転送状態 
    • CMD52 Abortのみ受け付ける 

SDIOカードのステートの情報はCMD52のレスポンスに含まれている。

  • 00 : Initialize また 非活性な状態
  • 01 : Command State
  • 02 : Transfer State
  • 03 : 予約

ファンクション

あとで書く

設定レジスタ

OCR

  • カードの対応電圧の情報
  • CMD5で取得する。
例: 20FFFF00
デコードすると
0 : C : not ready
2 : Number of I/O Function : Function1とFunction2がある
0 : Memory Present : no memory
0b11111111111100000000 : I/O OCR : 2.0-3.6に対応

CCCR : Card Common Control Registers

  • 256バイトのカード共通の制御レジスタ 
  • CMD52, CMD53で読み書きする。
  • バージョン情報
  • ファンクションの情報
  • ブロックサイズ
  • CISのアドレス
例
32 xx 00 00 xx xx xx 40
02 70 10 00 xx xx xx xx
xx xx 01 01

3 : SDIOのバージョン : SDIO 2.0
2 : CCCR/FBRのバージョン : CCCR/FBR 1.2
1 : SCSI Support Coontinus SPI interrupt : Yes
1 : SMB Support Multi Block : Yes
70 10 00 : Common CISのアドレス : 0x001070
1 : SMPC Support Master Power Control : Yes
1 : SHS Support High Speed : Yes

FBR : Function Basic Registers

  • ファンクションの制御レジスタ。 ひとつのファンクション毎に256バイト。
  • CMD52, CMD53で読み書きする。
  • Functioon code
  • ブロックサイズの初期値は0なので、Hostから設定する。
例
100: 00 xx xx xx xx xx xx xx 
xx xx 00 10 00
00 10 00 : Function CISのアドレス : 0x001000


200: 00 xx xx xx xx xx xx xx 
xx xx 38 10 00

38 10 00 : Function CISのアドレス : 0x001038

CIS : Card Information Structure

  • CISはPCMCIAの仕様を使っており、SD仕様書には情報が無い。
  • PCMCIAの仕様書は有料。(だけど2018年現在、ロシアのサイトにPDFがある。)
  • フォーマットはLinuxとかBSDのソースコードをみれば分かる。
  • Tupleと呼ぶ可変長のフォーマット、1バイト目がCODE、2バイト目が長さ兼次のTuple、3バイト目以降が可変長データ
例
01000: 20 04 D0 02 A6 A9 21 02 0C 00 22 2A 01 00 00 00
01010: 00 00 00 00 00 00 00 00 40 00 00 FF FF 80 00 00
01020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01030: 00 00 00 00 00 00 FF   

デコード
20 04 D0 02 A6 A9: MANFID が D0 02 A6 A9
21 02 0C 00 : FUNCID が 0C 00
22 2A 01 00 00 00 
00 00 00 00 00 00 00 00 40 00 00 FF FF 80 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00: FUNCE
FF : 終端

01030:                         20 04 D0 02 A6 A9 21 02
01040: 0C 00 22 2A 01 01 00 00 00 00 00 00 00 00 00 00
01050: 00 02 00 FF FF 80 00 00 00 00 00 00 00 00 00 00
01060: C8 00 00 00 00 00 00 00 00 00 00 00 00 00 FF

01070: 20 04 D0 02 A6 A9 21 02 0C 00 22 04 00 20 00 58
01080: 80 02 00 0B 80 03 02 01 11 80 03 1B 26 07 80 07
01090: 19 B8 27 EB AF DC 01 00 00 00 00 00 00 00 00 00
010A0: ここから
010B0:
010C0:
010D0:
010E0:
010F0:
01100:
01110:
01120:
01130:
01140:
01150:
01160:
01170:
01180:
01190: ここまで00
011A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF

デコード
20 04 D0 02 A6 A9
21 02 0C 00
22 04 00 20 00 58
80 02 00 0B: ベンダー独自
80 03 02 01 11: ベンダー独自
80 03 1B 26 07: ベンダー独自
80 07 19 B8 27 EB AF DC 01: ベンダー独自
空白
FF : 終端



Linux で SDIOのコマンドをダンプする

  • CMD5 と CMD52 と CMD53 を出力する。
  • 変更するカーネルのファイルは drivers/mmc/core/sdio_ops.c
        printk("CMD5 %x %X \n", ocr, *rocr);
        if (write) {
                printk("CMD52W FN%d %05X %02X\n", fn, addr, in);
        } else {
                printk("CMD52R FN%d %05X %02X\n", fn, addr, cmd.resp[0] & 0xFF);
        }
        if (write) {
                printk("CMD53W FN%d %05X\n", fn, addr);
                print_hex_dump_bytes("CMD53W:", DUMP_PREFIX_NONE,
                                     buf, data.blksz * data.blocks);
        } else {
                printk("CMD53R FN%d %05X\n", fn, addr);
                print_hex_dump_bytes("CMD53R:", DUMP_PREFIX_NONE,
                                     buf, data.blksz * data.blocks);
        }j

のような感じです。
カーネルログ出力が大量に出るので、カーネルの起動オプションにlog_buf_len=16Mとかすると良いです。

エミュレーションの実装

  • SDIO_CARD
    • SD_CARDと並列にSDIO_CARDを作成
    • sd_busはSDメモリとSDIOで共用
    • sd_busからカードへアクセスする部分に、SD_CARDとSDIO_CARDの判別処理を加える。
  • CMD52
    • SDIOカード内のメモリ空間を定義する。
    • メモリ空間に初期値を設定する。
    • 与えられた入力で、メモリ空間をリードライトする。 * R5を生成する関数を作る。
  • CCCR, FBR, CIS
    • Linuxなどで読み出している部分のログを取る。
    • ログからCCCR, FBR, CISの値を調べて、リード時に同じ値を返すようにする。
    • CCCRとFBRはライトもできるようにする。
    • これで、とりあえずは動いた。

ここまではカードとバスの修正だけでOK
CMD53はホストの修正が必要

  • CMD53

  • メモリ空間がどう見えるかはカードに依存する。

  • メインメモリの領域を借りるにはどうしたら良いのか

不明点

  • カードをInitialize State から Command Stateに切り替えるには
    • CMD5 と CMD3 と CMD7を送信するとCommand Stateに遷移する。
  • BCM43438だと認識するのは、どこを見てるのか? 
    • CISのvendorとclass
    • Functionの数が2
    • Function0の0x8000-0x8004を読み0x1541a9a6であること ここまでは共通の認識処理
    • todo:チップの型番認識
    • Marvell、RealtekはCISに型番を埋めてあるので、OSレベルで判別ができる。

Linuxドライバ

  • brcmf_sdio_register()でstruct sdio_driverを登録すると、sdio_driver->id_tableとIDが一致したら、カーネルからsdio_driver->probe()が呼ばれる。

はまり点

  • Common CISを読んで、バス幅設定後にコマンドタイムアウトが発生する。なのでSDIOカードの初期化が途中で止まる
    • 正常動作なら、コマンド完了割り込みが入ると、タイマ割り込みがクリアされる。
    • 小さなプログラムで。QEMUでも、コマンド完了割り込みが入ることを確認した。
    • SDHCIのレジスタアクセスをトレースしてみると、最後のコマンドから応答がない。カードが応答していない。SD CLOCKが止められている。
    • qemu sdhciのcapabのデフォルト値 0x057834b4
    • sdio_enable_hs()、 mmc_set_clock()あたり
    • sd clkが止まらない小細工をしたら、mmc1: new high speed SDIO card at address 4567 まで動いた。
    • とりあえずこの小細工をして進める。
  • SDIOのDATA転送できない
    • CMD53のマルチブロックリードを送信後、データレジスタからデータを読み出せない。
    • sdhci_read_dataport()でs->prnsts & SDHC_DATA_AVAILABLE を満たさないのでバッファが空のようだ。
    • このフラグを立てるのは、sdhci_read_block_from_card()
    • 遡るとsdhci_send_command() -> sdhci_data_transfer() -> sdhci_read_block_from_card()
    • sdhci_send_command() で if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT))の条件に該当していなかったのか?
    • sdhci_data_transfer()でif ((s->trnmod & SDHC_TRNS_READ) && sdbus_data_ready(&s->sdbus))に該当していなかった
    • sd.cでsd->state == sd_sendingdata_stateでないとdata_readyにならない。
    • 上記3箇所の条件を通るように修正したらデータを読み出せるようになった。だけどブロック単位で読んでしまうので、バイト単位になるように改造が必要
  • SDIOの2回目のCMD53 バイトデータ転送ができない
    • 1回目のDATA転送ができるようになったが、2回目の転送を行うと、CMD53が途中で消失する。
    • data転送後の終了処理が呼ばれていないの原因っぽい
    • sdhci_can_issue_command()の戻り値がfalseになるとコマンドを発行しない
    • SDメモリの場合はsdhci_read_dataport()でデータ転送終了処理をやる。これがブロックサイズに依存しているのでCMD53バイト転送だと終了処理が呼ばれない。
    • CMD53バイト転送後にデータ転送終了処理をすると、2回目のCMD53がカード側に届くようになった。
    • また転送完了割り込みが入るようになったので、割り込みをクリアする処理を入れたが、その後のUART出力がおかしくなるので、転送完了割り込みは、テストアプリの初期設定でマスクすることにした。
    • カード側で変数の初期化が抜けていたので、初期化を追加したら、やっと2回目のCMD53 バイトデータ転送ができるようになった。

参考情報

  • SD仕様書
  • SDIO仕様書
  • SDHOST仕様書
  • ROHM BU1805GUのソフトウェア開発仕様書
6
2
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
6
2