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[];
};
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[];     /* 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 =
/* 禁用扫描*/
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 =
/* 设置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驱动的编写主要步骤如下:
. 通过mmc_alloc_host分配一个mmc_host结构体
. 定义实现mmc_host_ops数据结构, 并赋值给上面的mmc_host::mmc_host_ops成员变量
. 给mmc_host成员变量赋值, 如ocr_avail、caps等成员变量
. 调用mmc_add_host注册该Host
7. Card驱动
对于Memory Card,内核实现了块设备驱动; 对于SDIO设备(比如WiFi), 则需要厂商实现(比如BCM的bcmdhd)
参考:
<ooonebook mmc>
<WF111 Datasheet>
<BCM4330 Datasheet>
<MMC/SD卡驱动实例开发讲解>
<SDIO Simplified Specification>
Linux MMC介绍的更多相关文章
- Linux MMC framework2:基本组件之core
		1.前言 本文主要core组件的主要流程,在介绍的过程中,将详细说明和core相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API TODO 3. 主要流程 3.1 mm ... 
- Linux mmc framework2:基本组件之queue
		1.前言 本文主要介绍card下queue组件的主要流程,在介绍的过程中,将详细说明和queue相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API 2.1 struct ... 
- Linux mmc framework2:基本组件之block
		1.前言 本文主要block组件的主要流程,在介绍的过程中,将详细说明和block相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API 2.1 struct mmc_ca ... 
- Linux MMC framework2:基本组件之host
		声明:本文很多内容和思路参考了http://www.wowotech.net/comm/mmc_host_driver.html,对原作者表示感谢! 1.前言 本文是Linux MMC framewo ... 
- Linux mmc framework1:软件架构
		[部分内容来自] http://www.wowotech.net/comm/mmc_framework_arch.html 1. 前言 由eMMC基础技术1:MMC简介中MMC.SD.SDIO的介绍可 ... 
- [MMC]Linux MMC/SD/SDIO驱动分析
		转自:http://www.cnblogs.com/cslunatic/p/3678045.html 一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(Multimed ... 
- linux驱动基础系列--Linux mmc sd sdio驱动分析
		前言 主要是想对Linux mmc子系统(包含mmc sd sdio)驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.块设备驱动.设备模型等也不进行详细说明原 ... 
- Linux MMC 驱动子系统简述(源码剖析)
		1. Linux MMC 驱动子系统 块设备是Linux系统中的基础外设之一,而 MMC/SD 存储设备是一种典型的块设备.Linux内核设计了 MMC子系统,用于管理 MMC/SD 设备. MMC ... 
- 01 Linux入门介绍
		一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ... 
随机推荐
- GBDT && XGBOOST
			GBDT && XGBOOST Outline Introduction GBDT Model XGBOOST Model ... 
- [译]10个有关SCP的命令
			原文来源: https://www.tecmint.com/scp-commands-examples/ 基本语法 scp source_file_name username@destination_ ... 
- Uva 294 Divisors(唯一分解定理)
			题意:求区间内正约数最大的数. 原理:唯一分解定义(又称算术基本定理),定义如下: 任何一个大于1的自然数 ,都可以唯一分解成有限个质数的乘积 ,这里 均为质数,其诸指数 是正整数.这样的分解称 ... 
- 安装floodlight遇到的问题和解决
			环境:ubuntu18.04 安装floodlight先前准备:java的环境,ant. sudo apt-get install build-essential defailt-jdk ant py ... 
- Could not resolve com.android.support.constraint:constraint-layout:1.1.3.
			原文地址: http://fanjiajia.cn/2018/09/25/Android%20Studio%20Could%20not%20resolve%20com.android.support. ... 
- (转) linux I/O优化 磁盘读写参数设置
			关于页面缓存的信息,可以用cat /proc/meminfo 看到.其中的Cached 指用于pagecache的内存大小(diskcache-SwapCache).随着写入缓存页,Dirty 的值会 ... 
- javaScript运算符学习笔记
			1.赋值运算符 javaScript运算符可以分为简单赋值和复合赋值运算.简单赋值运算是将赋值运算符(=)右边的表达式的值保存到赋值运算符左边的变量中,复合赋值运算则是混合了其他操作(算术运算操作,位 ... 
- Java中动态代理实现原理深究
			一.前言 笔者平时开发使用“动态代理”不多,最近在看设计模式的时候,“动态代理”又在面前晃了几次,所以这次想从源码的角度去分析动态代理的实现原理,以窥探其精妙~ 二.正文 2.1 静态代理 本文源码 ... 
- RxAndroid+RxJava+Gson+retrofit+okhttp初步搭建android网络请求框架
			新建工程集成, 一.工具集成(2017-4-27) 首先第一步集成retrofit retrofit 的 git 网站: https://github.com/square/retrofit 在git ... 
- 解决Git无法同步空文件夹的问题
			思路:在每个空文件夹下创建空文件,同步后再删除 package org.zln.module1.demo1; import org.apache.log4j.Logger; import java.i ... 
