Linux MMC介绍
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>