1. 介紹
Linux中,將包括MMC、SD、SDIO統稱為MMC子系統
MMC子系統從功能上可分為三個層次
- card層: Card驅動, 或稱client驅動 - core層: MMC的核心層, 完成不同協議和規范的實現, 為host層和設備驅動層提供接口函數 - host層: Host驅動, 針對不同主機端的SDHC、MMC控制器的驅動
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>