Linux MMC介紹


1. 介紹

Linux中,將包括MMC、SD、SDIO統稱為MMC子系統

MMC子系統從功能上可分為三個層次

- card層:  Card驅動, 或稱client驅動
- core層:  MMC的核心層, 完成不同協議和規范的實現, 為host層和設備驅動層提供接口函數
- host層:  Host驅動, 針對不同主機端的SDHC、MMC控制器的驅動

MMCStructure

2. 數據結構

MMC中包含的主要數據結構如下

- mmc_host      表示一個mmc host控制器
- mmc_card      表示一個mmc設備
- mmc_ios       IO總線相關設置
- mmc_driver    表示一個card drive
- mmc_bus_ops   總線操作函數集, 有mmc、sd、sdio三種
- mmc_host_ops  Host Controller操作函數集
- mmc_command   表示一個mmc命令
- mmc_data      表示一個mmc數據
- mmc_request   表示一個mmc請求
- sdio_func     表示一個SDIO功能設備

mmc_host主要字段如下

struct mmc_host {
    int            index;
    const struct mmc_host_ops *ops;
    u32            ocr_avail;
    u32            ocr_avail_sdio;        /* SDIO-specific OCR */
    u32            ocr_avail_sd;          /* SD-specific OCR */
    u32            ocr_avail_mmc;         /* MMC-specific OCR */
    u32            caps;                  /* Host能力標志*/
    u32            caps2;                 /* Host更多能力標志*/
    struct mmc_ios         ios;           /* current io bus settings */
    int            rescan_disable;        /* disable card detection */
    int            rescan_entered;        /* used with nonremovable devices */
    struct mmc_card        *card;         /* device attached to this host */
    struct delayed_work    detect;
    int            detect_change;         /* card檢測標志 */
    struct mmc_slot        slot;
    const struct mmc_bus_ops *bus_ops;    /* current bus driver */
    struct mmc_supply      supply;
    unsigned int           slotno;        /* used for sdio acpi binding */
    int                    dsr_req;       /* DSR value is valid */
    u32                    dsr;           /* optional driver stage (DSR) value */
    unsigned long  private[0];
};

mmc_card主要字段如下

struct mmc_card {
    struct mmc_host     *host;        /* the host this device belongs to */
    struct device       dev;          /* the device */
    u32                 ocr;          /* the current OCR setting */
    unsigned int        rca;          /* relative card address of device */
    unsigned int        type;         /* Card類型: MMC、SD、SDIO、COMBO */
    unsigned int        state;        /* (our) card state */
    unsigned int        quirks;       /* card quirks */
    struct mmc_cid      cid;          /* card identification */
    struct mmc_csd      csd;          /* card specific */
    struct mmc_ext_csd  ext_csd;      /* mmc v4 extended card specific */
    struct sd_scr       scr;          /* extra SD information */
    struct sd_ssr       ssr;          /* yet more SD information */
    struct sd_switch_caps sw_caps;    /* switch (CMD6) caps */
    unsigned int        sdio_funcs;   /* number of SDIO functions */
    struct sdio_cccr    cccr;         /* common card info */
    struct sdio_cis     cis;          /* common tuple info */
    struct sdio_func    *sdio_func[7];     /* SDIO functions (devices) */
    unsigned int        sd_bus_speed;      /* Bus Speed Mode set for the card */
    unsigned int        mmc_avail_type;    /* supported device type by both host and card */
    unsigned int        drive_strength;    /* for UHS-I, HS200 or HS400 */
};

mmc_ios字段如下

struct mmc_ios {
    unsigned int     clock;             /* 時鍾頻率 */
    unsigned short   vdd;
    unsigned char    bus_mode;          /* 命令輸出模式: 開漏模式、上拉模式 */
    unsigned char    chip_select;       /* SPI片選: DONTCARE、HIGH、LOW */
    unsigned char    power_mode;        /* 電源供應狀態: UNDEFINED、OFF、UP、ON */
    unsigned char    bus_width;         /* 數據總線寬度: 1、4、8 */
    unsigned char    timing;            /* 總線速度模式: DS、HS、SDR12、SDR25... */
    unsigned char    signal_voltage;    /* 信號電壓值: 3.3V、1.8V、1.2V */
    unsigned char    drv_type;          /* 驅動類型: A, B, C, D */
    bool enhanced_strobe;               /* hs400es選擇 */
};

mmc_driver字段如下

struct mmc_driver {
    struct device_driver drv;
    int (*probe)(struct mmc_card *);
    void (*remove)(struct mmc_card *);
    void (*shutdown)(struct mmc_card *);
};

mmc_bus_ops字段如下

struct mmc_bus_ops {
    void (*remove)(struct mmc_host *);
    void (*detect)(struct mmc_host *);
    int (*pre_suspend)(struct mmc_host *);
    int (*suspend)(struct mmc_host *);
    int (*resume)(struct mmc_host *);
    int (*runtime_suspend)(struct mmc_host *);
    int (*runtime_resume)(struct mmc_host *);
    int (*power_save)(struct mmc_host *);
    int (*power_restore)(struct mmc_host *);
    int (*alive)(struct mmc_host *);
    int (*shutdown)(struct mmc_host *);
    int (*reset)(struct mmc_host *);
};

mmc_host_ops字段如下

struct mmc_host_ops {
    void    (*post_req)(struct mmc_host *, struct mmc_request *, int err);
    void    (*pre_req)(struct mmc_host *, struct mmc_request *, bool is_first_req);
    void    (*request)(struct mmc_host *host, struct mmc_request *req);
    void    (*set_ios)(struct mmc_host *, struct mmc_ios *);
    int     (*get_ro)(struct mmc_host *);
    int     (*get_cd)(struct mmc_host *);
    void    (*enable_sdio_irq)(struct mmc_host *, int enable);
    void    (*init_card)(struct mmc_host *, struct mmc_card *card);
    int     (*start_signal_voltage_switch)(struct mmc_host *, struct mmc_ios *);
    int     (*card_busy)(struct mmc_host *);
    int     (*execute_tuning)(struct mmc_host *, u32 opcode);
    int     (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
    void    (*hs400_enhanced_strobe)(struct mmc_host *, struct mmc_ios *);
    int     (*select_drive_strength)(struct mmc_card *card,
                  unsigned int max_dtr, int host_drv, int card_drv, int *drv_type);
    void    (*hw_reset)(struct mmc_host *);
    void    (*card_event)(struct mmc_host *);
    int     (*multi_io_quirk)(struct mmc_card *, unsigned int direction, int blk_size);
};

3. MMC接口

3.1 Host API

Host相關接口, 供Host Controller使用

/* 設備樹解析 */
int mmc_of_parse(struct mmc_host *);

/* 分配/釋放host結構體 */
struct mmc_host *mmc_alloc_host(int extra, struct device *);
void   mmc_free_host(struct mmc_host *);

/* 添加/移除host設備 */
int  mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);

mmc_alloc_host主要流程如下

mmc_alloc_host
  kzalloc(sizeof(struct mmc_host) + extra)
  /* 分配mmc_host結構體 */
  mmc_host::rescan_disable = 1
  /* 禁用掃描*/
  ida_pre_get
  ida_get_new
  /* ida相關 */
  dev_set_name
  /* 設置設備名稱 */
  device_initialize(mmc_host::class_dev)
  /* 初始化設備結構體 */
  mmc_gpio_alloc
  /* 分配mmc_gpio結構體 */
  init_waitqueue_head(mmc_host::wq)
  /* 初始化等待隊列 */
  INIT_DELAYED_WORK(mmc_host::detect, mmc_rescan);
  /* 初始化工作隊列 */

mmc_add_host主要流程如下

mmc_add_host
  device_add(mmc_host::class_dev)
  /* 添加設備 */
  mmc_start_host
  /* 啟用該Host */
    mmc_claim_host
    /* 占用Host控制器 */
    mmc_power_up
    /* 給Host供電 */
      mmc_host::mmc_ios::power_mode = MMC_POWER_UP
      /* 設置power狀態為正在上電 */
      mmc_set_initial_state
      /* 設置初始化狀態 */
        mmc_host::mmc_ios::bus_width = MMC_BUS_WIDTH_1
        /* 設置總線寬帶為1bit */
        mmc_host::mmc_ios::timing = MMC_TIMING_LEGACY
        /* 設置總線速度模式為DS模式 */
        mmc_set_ios
        /* 將IO總線設置寫入Host驅動, 調用mmc_host::mmc_host_ops::set_ios */
      __mmc_set_signal_voltage
      /* 設置信號電壓, 依次嘗試3.3V、1.8V、1.2V */
      mmc_host::mmc_ios::clock = mmc_host::f_init;
      /* 設置時鍾頻率 */ 
      mmc_host::mmc_ios::power_mode = MMC_POWER_ON
      /* 設置power狀態為正常供電 */ 
      mmc_set_ios
      /* 將io相關設置寫入Host驅動 */
    mmc_release_host
    /* 釋放Host控制器 */
    mmc_gpiod_request_cd_irq
    /* 釋放Host控制器 */
    _mmc_detect_change
    /* Card掃描 */
      mmc_host::detect_change = 1
      /* 設置Card檢測標志 */
      mmc_schedule_delayed_work(mmc_host::detect);
      /* 調度工作隊列, 調度函數為mmc_rescan */

3.2 Card API

/* 注冊/注銷MMC Card驅動 */
int  mmc_register_driver(struct mmc_driver *);
void mmc_unregister_driver(struct mmc_driver *);

/* 注冊/注銷SDIO Card驅動 */
int  sdio_register_driver(struct sdio_driver *);
void sdio_unregister_driver(struct sdio_driver *);

3.3 General API

/* Card檢測 */
void mmc_detect_change(struct mmc_host *host, unsigned long delay);

3.4 Command API

cmd0 - mmc_go_idle
cmd1 - mmc_send_op_cond
cmd2 - mmc_all_send_cid

4. MMC啟動

MMC在啟動時會進行相應的初始化

mmc_init
  mmc_register_bus
    bus_register                     /* 注冊了mmc總線 */
  mmc_register_host_class
    class_register                   /* 注冊'mmc_host'設備類 */
  sdio_register_bus
    bus_register                     /* 注冊了sdio總線 */

mmc_blk_init
  register_blkdev                    /* 注冊mmc塊設備 */
  mmc_register_driver
    driver_register                  /* 注冊mmc card驅動 */

5. MMC掃描

MMC設備的發現通過mmc_rescan函數來實現,通過_mmc_detect_change/mmc_detect_change來觸發
mmc_rescan主要流程如下

mmc_rescan
  mmc_host::mmc_host_ops::card_event
  mmc_host::mmc_bus_ops::detect
  mmc_host::mmc_host_ops::get_cd
  mmc_rescan_try_freq
  /* 使用不同時鍾頻率進行初始化 */
    mmc_power_up
    /* 設備供電 */
    mmc_hw_reset_for_init
    /* 針對部分eMMC(VCCQ一直為高) */
      mmc_host::mmc_host_ops::hw_reset
    sdio_reset
    /* 僅針對SDIO設備 */
    mmc_go_idle
    /* 發送CMD0命令 */
    mmc_send_if_cond
    /* 僅針對SD設備 */
    mmc_attach_sdio
    /* SDIO設備初始化 */
    mmc_attach_sd
    /* SD設備初始化 */
    mmc_attach_mmc
    /* MMC設備初始化 */
    mmc_power_off
    /* 當上述設備都不是則停止供電 */

其中SDIO、SD、MMC的初始化流程各不相同

mmc_attach_sdio主要流程如下

/* SDIO Card初始化 */
mmc_attach_sdio
  mmc_send_io_op_cond
  /* 發送SD_IO_SEND_OP_COND, 獲取??? */
  mmc_attach_bus(mmc_sdio_ops)
  /* 將SDIO總線操作集分配給Host */
  host->ocr_avail = host->ocr_avail_sdio;
  /* 設置SDIO的OCR */
  mmc_select_voltage
  /* 選擇合適的電壓值 */
  mmc_sdio_init_card
  /* 識別和初始化SDIO Card */
    ......
  pm_runtime_set_active
  /* 設置Card運行時PM狀態為活躍, 僅針對支持MMC_CAP_POWER_OFF_CARD特性的Host */
  pm_runtime_enable
  /* 使能Card運行時PM, 僅針對支持MMC_CAP_POWER_OFF_CARD特性的Host */
  ...... /* sdio functions related */
  mmc_add_card(mmc_host::mmc_card)
  /* 注冊SDIO Card */
  sdio_add_func(mmc_host::mmc_card::sdio_func)
  /* 注冊SDIO function */

mmc_attach_sd主要流程如下

/* SD Card初始化 */
mmc_attach_sd
  mmc_send_app_op_cond
  /* 發送SD_APP_OP_COND, 獲取??? */
  mmc_attach_bus(mmc_sd_ops)
  /* 將SD總線操作集分配給Host */
  host->ocr_avail = host->ocr_avail_sd;
  /* 設置SD的OCR */
  mmc_host_is_spi
  ->
    mmc_go_idle
    /* 發送CMD0 */
    mmc_spi_read_ocr
    /* 發送MMC_SPI_READ_OCR, 讀取??? */
  mmc_select_voltage
  /* 選擇合適的電壓值 */
  mmc_sd_init_card 
  /* 識別和初始化SD Card */
    ......
  mmc_add_card(mmc_host::mmc_card)
  /* 注冊SD Card */

mmc_attach_mmc主要流程如下

/* MMC Card初始化 */
mmc_attach_mmc 
  mmc_send_op_cond
  /* 發送MMC_SEND_OP_COND, 獲取??? */
  mmc_attach_bus(mmc_ops)
  /* 將MMC總線操作集分配給Host */
  host->ocr_avail = host->ocr_avail_mmc;
  /* 設置MMC的OCR */
  mmc_host_is_spi
  ->
    mmc_spi_read_ocr
    /* 發送MMC_SPI_READ_OCR, 讀取??? */
  mmc_select_voltage
  /* 選擇合適的電壓值 */
  mmc_init_card 
  /* 識別和初始化MMC Card */
    ......
  mmc_add_card(mmc_host::mmc_card)
  /* 注冊MMC Card */

6. Host驅動

Host驅動的編寫主要步驟如下:

1. 通過mmc_alloc_host分配一個mmc_host結構體
2. 定義實現mmc_host_ops數據結構, 並賦值給上面的mmc_host::mmc_host_ops成員變量
3. 給mmc_host成員變量賦值, 如ocr_avail、caps等成員變量
4. 調用mmc_add_host注冊該Host

7. Card驅動

對於Memory Card,內核實現了塊設備驅動; 對於SDIO設備(比如WiFi), 則需要廠商實現(比如BCM的bcmdhd)

參考:
<ooonebook mmc>
<WF111 Datasheet>
<BCM4330 Datasheet>
<MMC/SD卡驅動實例開發講解>
<SDIO Simplified Specification>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM