LeavaTailの日記

LeavaTailの日記

Linuxエンジニアを目指した技術者の備忘録

Linuxカーネルのファイルアクセスの処理を追いかける (23) MMC: mmc_attach_sd

関連記事

概要

QEMUの vexpress-a9 (arm) で Linux 5.15を起動させながら、ファイル書き込みのカーネル処理を確認していく。

本章では、MMCサブシステムの初期化処理について確認した。

はじめに

ユーザプロセスはファイルシステムという機構によって記憶装置上のデータをファイルという形式で書き込み・読み込みすることができる。
本調査では、ユーザプロセスがファイルに書き込み要求を実行したときにLinuxカーネルではどのような処理が実行されるかを読み解いていく。

調査対象や環境などはPart 1: 環境セットアップを参照。

注意

一部の仕様書は非公開となっているため、公開情報からの推測が含まれています。そのため、内容に誤りが含まれている恐れがります。

SD判定

ここまで、MMCサブシステムから デバイス検出処理 (mmc_rescan関数) が、非同期による遅延処理が実行される。 その処理内で、 mmc_attach_sd関数によって、デバイスがSDであるかの検出とSDの初期化に入る。

mmc_attach_sd関数の定義は次のようになっている。

// 1804:
int mmc_attach_sd(struct mmc_host *host)
{
    int err;
    u32 ocr, rocr;

    WARN_ON(!host->claimed);

    err = mmc_send_app_op_cond(host, 0, &ocr);
    if (err)
        return err;

    mmc_attach_bus(host, &mmc_sd_ops);
    if (host->ocr_avail_sd)
        host->ocr_avail = host->ocr_avail_sd;

    /*
    * We need to get OCR a different way for SPI.
    */
    if (mmc_host_is_spi(host)) {
        mmc_go_idle(host);

        err = mmc_spi_read_ocr(host, 0, &ocr);
        if (err)
            goto err;
    }

    /*
    * Some SD cards claims an out of spec VDD voltage range. Let's treat
    * these bits as being in-valid and especially also bit7.
    */
    ocr &= ~0x7FFF;

    rocr = mmc_select_voltage(host, ocr);

    /*
    * Can we support the voltage(s) of the card(s)?
    */
    if (!rocr) {
        err = -EINVAL;
        goto err;
    }

    /*
    * Detect and init the card.
    */
    err = mmc_sd_init_card(host, rocr, NULL);
    if (err)
        goto err;

    mmc_release_host(host);
    err = mmc_add_card(host->card);
    if (err)
        goto remove_card;

    mmc_claim_host(host);
    return 0;

remove_card:
    mmc_remove_card(host->card);
    host->card = NULL;
    mmc_claim_host(host);
err:
    mmc_detach_bus(host);

    pr_err("%s: error %d whilst initialising SD card\n",
        mmc_hostname(host), err);

    return err;
}

初めに、 mmc_send_app_op_cond関数によって SDかどうかを判定する。 mmc_send_app_op_cond関数では、ACMD41(SD_SEND_OP_COND) のレスポンスで判断する。*1 SD仕様に準拠したメモリカードである場合には、このコマンドでレスポンスが返ってくる。

"inquiry CMD41"の場合には、レスポンスとしてOCR(Operation Conditions Register?)が取得できる。

Vddの選択

OCR は、メモリカードの動作電圧範囲が 100mV 単位で表現される。

SPIモードでは、OCR の取得方法が異なり、mmc_spi_read_ocr関数によって取得する。 (しかし、今回はSPIモードではないため割愛する)

ここでパッチ概要によると、一部のメモリカードでは、この OCRの特定ビットを無効な電圧範囲とすることがある。

patchwork.kernel.org

取得したOCRとホストコントローラの対応電圧を使い、mmc_select_voltage関数は 供給電圧を設定する。 mmc_select_voltage関数の定義は次のようになっている。

// 1109:
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
    int bit;

    /*
    * Sanity check the voltages that the card claims to
    * support.
    */
    if (ocr & 0x7F) {
        dev_warn(mmc_dev(host),
        "card claims to support voltages below defined range\n");
        ocr &= ~0x7F;
    }

    ocr &= host->ocr_avail;
    if (!ocr) {
        dev_warn(mmc_dev(host), "no support for card's volts\n");
        return 0;
    }

    if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
        bit = ffs(ocr) - 1;
        ocr &= 3 << bit;
        mmc_power_cycle(host, ocr);
    } else {
        bit = fls(ocr) - 1;
        ocr &= 3 << bit;
        if (bit != host->ios.vdd)
            dev_warn(mmc_dev(host), "exceeding card's volts\n");
    }

    return ocr;
}

mmc_select_voltage関数では、OCRで取得された値とホストコントローラがサポートしている電圧から、最大VDDを設定する。 例えば、inquiry CMD41でocr0xff8000 、ホストコントローラの対応範囲host->ocr_avail0x300000の場合には、3.3~3.4Vとなる。

Vddの設定

カード初期化

OCRの値がホストコントローラの対応電圧の範囲に入っている場合、mmc_sd_init_card関数によって、SDメモリカードの初期化の処理に入る。 mmc_sd_init_card関数の定義は次のようになっている。

// 1389:
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
    struct mmc_card *oldcard)
{
    struct mmc_card *card;
    int err;
    u32 cid[4];
    u32 rocr = 0;
    bool v18_fixup_failed = false;

    WARN_ON(!host->claimed);
retry:
    err = mmc_sd_get_cid(host, ocr, cid, &rocr);
    if (err)
        return err;

    if (oldcard) {
        if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
            pr_debug("%s: Perhaps the card was replaced\n",
                mmc_hostname(host));
            return -ENOENT;
        }

        card = oldcard;
    } else {
        /*
        * Allocate card structure.
        */
        card = mmc_alloc_card(host, &sd_type);
        if (IS_ERR(card))
            return PTR_ERR(card);

        card->ocr = ocr;
        card->type = MMC_TYPE_SD;
        memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
    }

    /*
    * Call the optional HC's init_card function to handle quirks.
    */
    if (host->ops->init_card)
        host->ops->init_card(host, card);

    /*
    * For native busses:  get card RCA and quit open drain mode.
    */
    if (!mmc_host_is_spi(host)) {
        err = mmc_send_relative_addr(host, &card->rca);
        if (err)
            goto free_card;
    }

    if (!oldcard) {
        err = mmc_sd_get_csd(card);
        if (err)
            goto free_card;

        mmc_decode_cid(card);
    }

    /*
    * handling only for cards supporting DSR and hosts requesting
    * DSR configuration
    */
    if (card->csd.dsr_imp && host->dsr_req)
        mmc_set_dsr(host);

    /*
    * Select card, as all following commands rely on that.
    */
    if (!mmc_host_is_spi(host)) {
        err = mmc_select_card(card);
        if (err)
            goto free_card;
    }

    err = mmc_sd_setup_card(host, card, oldcard != NULL);
    if (err)
        goto free_card;

    /*
    * If the card has not been power cycled, it may still be using 1.8V
    * signaling. Detect that situation and try to initialize a UHS-I (1.8V)
    * transfer mode.
    */
    if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) &&
        mmc_sd_card_using_v18(card) &&
        host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
        /*
        * Re-read switch information in case it has changed since
        * oldcard was initialized.
        */
        if (oldcard) {
            err = mmc_read_switch(card);
            if (err)
                goto free_card;
        }
        if (mmc_sd_card_using_v18(card)) {
            if (mmc_host_set_uhs_voltage(host) ||
                mmc_sd_init_uhs_card(card)) {
                v18_fixup_failed = true;
                mmc_power_cycle(host, ocr);
                if (!oldcard)
                    mmc_remove_card(card);
                goto retry;
            }
            goto done;
        }
    }

    /* Initialization sequence for UHS-I cards */
    if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
        err = mmc_sd_init_uhs_card(card);
        if (err)
            goto free_card;
    } else {
        /*
        * Attempt to change to high-speed (if supported)
        */
        err = mmc_sd_switch_hs(card);
        if (err > 0)
            mmc_set_timing(card->host, MMC_TIMING_SD_HS);
        else if (err)
            goto free_card;

        /*
        * Set bus speed.
        */
        mmc_set_clock(host, mmc_sd_get_max_clock(card));

        /*
        * Switch to wider bus (if supported).
        */
        if ((host->caps & MMC_CAP_4_BIT_DATA) &&
            (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
            err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
            if (err)
                goto free_card;

            mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
        }
    }

    if (!oldcard) {
        /* Read/parse the extension registers. */
        err = sd_read_ext_regs(card);
        if (err)
            goto free_card;
    }

    /* Enable internal SD cache if supported. */
    if (card->ext_perf.feature_support & SD_EXT_PERF_CACHE) {
        err = sd_enable_cache(card);
        if (err)
            goto free_card;
    }

    if (host->cqe_ops && !host->cqe_enabled) {
        err = host->cqe_ops->cqe_enable(host, card);
        if (!err) {
            host->cqe_enabled = true;
            host->hsq_enabled = true;
            pr_info("%s: Host Software Queue enabled\n",
                mmc_hostname(host));
        }
    }

    if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
        host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
        pr_err("%s: Host failed to negotiate down from 3.3V\n",
            mmc_hostname(host));
        err = -EINVAL;
        goto free_card;
    }
done:
    host->card = card;
    return 0;

free_card:
    if (!oldcard)
        mmc_remove_card(card);

    return err;
}

カード識別モード

SDメモリカードには、カードを識別する番号 Card IDentification(CID) を持つ。

mmc_sd_init_card関数では、初めに CID を取得する。 mmc_sd_init_card関数の定義は次のようになっている。

// 808:
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
{
    int err;
    u32 max_current;
    int retries = 10;
    u32 pocr = ocr;

try_again:
    if (!retries) {
        ocr &= ~SD_OCR_S18R;
        pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
    }

    /*
    * Since we're changing the OCR value, we seem to
    * need to tell some cards to go back to the idle
    * state.  We wait 1ms to give cards time to
    * respond.
    */
    mmc_go_idle(host);

    /*
    * If SD_SEND_IF_COND indicates an SD 2.0
    * compliant card and we should set bit 30
    * of the ocr to indicate that we can handle
    * block-addressed SDHC cards.
    */
    err = mmc_send_if_cond(host, ocr);
    if (!err)
        ocr |= SD_OCR_CCS;

    /*
    * If the host supports one of UHS-I modes, request the card
    * to switch to 1.8V signaling level. If the card has failed
    * repeatedly to switch however, skip this.
    */
    if (retries && mmc_host_uhs(host))
        ocr |= SD_OCR_S18R;

    /*
    * If the host can supply more than 150mA at current voltage,
    * XPC should be set to 1.
    */
    max_current = sd_get_host_max_current(host);
    if (max_current > 150)
        ocr |= SD_OCR_XPC;

    err = mmc_send_app_op_cond(host, ocr, rocr);
    if (err)
        return err;

    /*
    * In case the S18A bit is set in the response, let's start the signal
    * voltage switch procedure. SPI mode doesn't support CMD11.
    * Note that, according to the spec, the S18A bit is not valid unless
    * the CCS bit is set as well. We deliberately deviate from the spec in
    * regards to this, which allows UHS-I to be supported for SDSC cards.
    */
    if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x01000000)) {
        err = mmc_set_uhs_voltage(host, pocr);
        if (err == -EAGAIN) {
            retries--;
            goto try_again;
        } else if (err) {
            retries = 0;
            goto try_again;
        }
    }

    err = mmc_send_cid(host, cid);
    return err;
}

カード認識するにあたってメモリカードを idle状態に設定する必要がある。 mmc_go_idle関数によって、メモリカードを idle状態に設定することができる。

mmc_go_idle関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。CMD0(FO_IDLE) はカードをidle状態に設定するコマンドである。

[    1.173771][   T45] mmc0: starting CMD0 arg 00000000 flags 000000c0

SDメモリカードには、SDv1とSDv2の異なるバージョンが存在しており、それぞれで初期化のシーケンスが若干異なる。

SDv1かSDv2か判定するためには、SDv2で追加されたCMD8(SEND_IF_COND)のレスポンスによって判定する。

SDv2判定のためのCMD8は mmc_send_if_conf関数によってコマンドを発行する発行することができる。 mmc_send_if_cond関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.192937][   T45] mmc0: starting CMD8 arg 000001aa flags 000002f5

もし、レスポンスが返ってきた場合、SD High Capacity(SDHC) や SD eXtended Capacity(SDXC) を意味する Card Capacity Status(CCS) を設定する。(SDHC や SDXC は SDv2 で追加された仕様である)

一方で、UHS-I は 信号電圧を 1.8Vまで省電圧化されている。 もし、UHS-Iがサポートされている場合には、S18R (Switching to 1.8V Request) を設定する。

その後、sd_get_host_max_current関数にてホストが供給できる最大電流を取得する。 もし、150mAまで供給できる場合には XPC (SDXC Power Control?) のビットを設定する。

ここまでで設定したocrを引数として ACMD41(SD_SEND_OP_COND) を呼び出す。 ACMD41 は引数が設定されている場合には、inquiry ではなく first ACMD41 として扱われる。

first ACMD41の引数

mmc_send_app_op_cond関数(first ACMD41)が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.193312][   T45] mmc0: starting CMD55 arg 00000000 flags 000000f5
[    1.193603][   T45] mmc0: starting CMD41 arg 40200000 flags 000000e1

first AMCD41のレスポンス rocr の特定ビット S18A(Switching to 1.8V Accepted) は、1.8Vへの切り替えが可能であることを意味する。

CIDの取得には、mmc_send_cid関数を用いる。 SDモードでは、CMD2(ALL_SEND_CID)によってCIDを取得することができる。

mmc_send_cid関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.193849][   T45] mmc0: starting CMD2 arg 00000000 flags 00000007

ここで取得した CID から既存のカードの置き換え処理でない場合には、mmc_alloc_card関数によって mmc_card構造体の変数cardの確保と初期化する。((もし、ホストコントローラ特有の初期化処理が必要な場合には、init_cardを呼び出すことができる))

SDモードでは、RCA(Relative Card Address) の取得が必要となる。 これは、CMD3(SEND_RELATIVE_ADDR)によって取得できる。

mmc_send_relative_addr関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.194188][   T45] mmc0: starting CMD3 arg 00000000 flags 00000075

SDメモリカードでは記憶容量などといった情報を CSD (Card-Specific Data?) レジスタに保持している。 このレジスタの値の取得とcardに情報を代入する処理を mmc_sd_get_csd関数が担う。

データ転送モード

SDメモリカードは、CMD9(SEND_CSD)を受け取ると CSDレジスタの値を返す。 mmc_sd_get_csd関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.448682][   T48] mmc0: starting CMD9 arg 45670000 flags 00000007

一部のSDメモリカードには DSR(Driver Stage Register) が実装されており、CMD/DATA出力の立ち上がり/立ち下がりの時間が強さを調整できる。 デバイスツリーに dsrプロパティを設定している場合、mmc_set_dsr関数によってCMD4 (SET_DSR)が発行される。

ただし、今回はこれが設定されていないため、CMD4は発行されない。

git.kernel.org

SDメモリカードでは、データ転送の前にCMD7(SELECT/DESELECT_CARD)でカードの選択をする。 mmc_select_card関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.194743][   T45] mmc0: starting CMD7 arg 45670000 flags 00000015

そして、mmc_sd_setup_card関数によってカードの取得・設定していく。 mmc_sd_setup_card関数の定義は次のようになっている。

// 919:
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
    bool reinit)
{
    int err;

    if (!reinit) {
        /*
        * Fetch SCR from card.
        */
        err = mmc_app_send_scr(card);
        if (err)
            return err;

        err = mmc_decode_scr(card);
        if (err)
            return err;

        /*
        * Fetch and process SD Status register.
        */
        err = mmc_read_ssr(card);
        if (err)
            return err;

        /* Erase init depends on CSD and SSR */
        mmc_init_erase(card);

        /*
        * Fetch switch information from card.
        */
        err = mmc_read_switch(card);
        if (err)
            return err;
    }

    /*
    * For SPI, enable CRC as appropriate.
    * This CRC enable is located AFTER the reading of the
    * card registers because some SDHC cards are not able
    * to provide valid CRCs for non-512-byte blocks.
    */
    if (mmc_host_is_spi(host)) {
        err = mmc_spi_set_crc(host, use_spi_crc);
        if (err)
            return err;
    }

    /*
    * Check if read-only switch is active.
    */
    if (!reinit) {
        int ro = mmc_sd_get_ro(host);

        if (ro < 0) {
            pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
                mmc_hostname(host));
        } else if (ro > 0) {
            mmc_card_set_readonly(card);
        }
    }

    return 0;
}

SDメモリカードには、SCR(Sd Configuration Register) で SDメモリカードの特定の情報を持つ。 これには、mmc_app_send_scr関数が ACMD51 を発行する必要がある。 この関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.194989][   T45] mmc0: starting CMD55 arg 45670000 flags 00000095
[    1.195346][   T45] mmc0: starting CMD51 arg 00000000 flags 000000b5

その後、SSR(Sd Status Register) を取得する。 これには、mmc_read_ssr関数が ACMD13 を発行する必要がある。 この関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.196964][   T45] mmc0: starting CMD55 arg 45670000 flags 00000095
[    1.197161][   T45] mmc0: starting CMD13 arg 00000000 flags 000001b5

mmc_init_erase関数では、SSRなどから erase_size(eraseの最小単位) や preferred_erase_size(Allocation Unit size) を card に設定する。

mmc_read_switch関数は、SDメモリカードが対応しているバススピードモードを確認する。 これには、mmc_read_switch関数が CMD6 を発行する必要がある。 この関数が呼ばれたとき、次のようなデバッグメッセージが確認することができる。

[    1.197741][   T45] mmc0: starting CMD6 arg 00fffff0 flags 000000b5

その後、バススピードやバス幅の設定をする。 ここでは、UHS-Iの場合とそうではない場合で分かれており、対応関係は次のようになっている。

バスインターフェース バススピードモード バススピード
default 12.5MB/s
High speed 25MB/s
UHS-I SDR50/DDR50 50MB/s
UHS-I SDR104 104MB/s

また、SD spec 6.x から 性能改善機能としてキャッシュなどが追加されており、これらをサポートしているカードに対しては CMD48(READ_EXTR_SINGLE)とCMD49(WRITE_EXTR_SINGLE)を発行することができる。

MMCバスに追加

mmc_add_card関数によって初期化の処理が完了したMMCメモリカードLinuxバイスモデルに登録する。 mmc_add_card関数の定義は次のようになっている。

// 308:
int mmc_add_card(struct mmc_card *card)
{
    int ret;
    const char *type;
    const char *uhs_bus_speed_mode = "";
    static const char *const uhs_speeds[] = {
        [UHS_SDR12_BUS_SPEED] = "SDR12 ",
        [UHS_SDR25_BUS_SPEED] = "SDR25 ",
        [UHS_SDR50_BUS_SPEED] = "SDR50 ",
        [UHS_SDR104_BUS_SPEED] = "SDR104 ",
        [UHS_DDR50_BUS_SPEED] = "DDR50 ",
    };


    dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);

    switch (card->type) {
    case MMC_TYPE_MMC:
        type = "MMC";
        break;
    case MMC_TYPE_SD:
        type = "SD";
        if (mmc_card_blockaddr(card)) {
            if (mmc_card_ext_capacity(card))
                type = "SDXC";
            else
                type = "SDHC";
        }
        break;
    case MMC_TYPE_SDIO:
        type = "SDIO";
        break;
    case MMC_TYPE_SD_COMBO:
        type = "SD-combo";
        if (mmc_card_blockaddr(card))
            type = "SDHC-combo";
        break;
    default:
        type = "?";
        break;
    }

    if (mmc_card_uhs(card) &&
        (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
        uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];

    if (mmc_host_is_spi(card->host)) {
        pr_info("%s: new %s%s%s card on SPI\n",
            mmc_hostname(card->host),
            mmc_card_hs(card) ? "high speed " : "",
            mmc_card_ddr52(card) ? "DDR " : "",
            type);
    } else {
        pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
            mmc_hostname(card->host),
            mmc_card_uhs(card) ? "ultra high speed " :
            (mmc_card_hs(card) ? "high speed " : ""),
            mmc_card_hs400(card) ? "HS400 " :
            (mmc_card_hs200(card) ? "HS200 " : ""),
            mmc_card_hs400es(card) ? "Enhanced strobe " : "",
            mmc_card_ddr52(card) ? "DDR " : "",
            uhs_bus_speed_mode, type, card->rca);
    }

#ifdef CONFIG_DEBUG_FS
    mmc_add_card_debugfs(card);
#endif
    card->dev.of_node = mmc_of_find_child_device(card->host, 0);

    device_enable_async_suspend(&card->dev);

    ret = device_add(&card->dev);
    if (ret)
        return ret;

    mmc_card_set_present(card);

    return 0;
}

ここまでの処理によってカード種類などが判明しているため、カーネルメッセージに出力する。 例えば、今回の環境では次のようなデバッグメッセージが確認することができる。

[    1.197018][   T45] mmc0: new SD card at address 4567

その後、mmc_add_card_debugfs関数で debugfs にエントリを追加し、device_add関数で sysfs にエントリを追加する。 mmc_add_card_debugfs関数の定義は次のようになっている。

// 253:
void mmc_add_card_debugfs(struct mmc_card *card)
{
    struct mmc_host    *host = card->host;
    struct dentry  *root;

    if (!host->debugfs_root)
        return;

    root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
    card->debugfs_root = root;

    debugfs_create_x32("state", S_IRUSR, root, &card->state);
}

mmc_add_card_debugfs関数によって メモリカードの状態を確認できるようなエントリが追加される。

// 1:
/* Card states */
#define MMC_STATE_PRESENT   (1<<0)      /* present in sysfs */
#define MMC_STATE_READONLY  (1<<1)      /* card is read-only */
#define MMC_STATE_BLOCKADDR (1<<2)      /* card uses block-addressing */
#define MMC_CARD_SDXC       (1<<3)      /* card is SDXC */
#define MMC_CARD_REMOVED    (1<<4)      /* card has been removed */
#define MMC_STATE_SUSPENDED (1<<5)      /* card is suspended */

例えば、今回の環境で起動直後に確認した場合には次のような結果が得られる。

# cat /sys/kernel/debug/mmc0/mmc0\:4567/state
0x00000001

device_add関数はLinuxバイスモデルに"Device"を登録するAPIである。

この関数によって、関連するコンポーネントとして bus の probe処理が呼ばれる。 今回は mmc_bus_probe関数 (と mmc_blk_probe関数) を実行する。

おわりに

本記事では、カーネル起動時に呼び出される mmc_attach_sd関数について確認した。
この関数では、SDメモリカードの初期化ロジックが組み込まれており、次のようなCMDが発行されている。

SDメモリカードの初期化フロー図

変更履歴

  • 2024/05/10: 記事公開

参考

*1:ACMDは、直前にCMD55である場合に、そのコマンドをACMDとして解釈される