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介绍的更多相关文章

  1. Linux MMC framework2:基本组件之core

    1.前言 本文主要core组件的主要流程,在介绍的过程中,将详细说明和core相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API TODO 3. 主要流程 3.1 mm ...

  2. Linux mmc framework2:基本组件之queue

    1.前言 本文主要介绍card下queue组件的主要流程,在介绍的过程中,将详细说明和queue相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API 2.1 struct ...

  3. Linux mmc framework2:基本组件之block

    1.前言 本文主要block组件的主要流程,在介绍的过程中,将详细说明和block相关的流程,涉及到其它组件的详细流程再在相关文章中说明. 2.主要数据结构和API 2.1 struct mmc_ca ...

  4. Linux MMC framework2:基本组件之host

    声明:本文很多内容和思路参考了http://www.wowotech.net/comm/mmc_host_driver.html,对原作者表示感谢! 1.前言 本文是Linux MMC framewo ...

  5. Linux mmc framework1:软件架构

    [部分内容来自] http://www.wowotech.net/comm/mmc_framework_arch.html 1. 前言 由eMMC基础技术1:MMC简介中MMC.SD.SDIO的介绍可 ...

  6. [MMC]Linux MMC/SD/SDIO驱动分析

    转自:http://www.cnblogs.com/cslunatic/p/3678045.html 一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(Multimed ...

  7. linux驱动基础系列--Linux mmc sd sdio驱动分析

    前言 主要是想对Linux mmc子系统(包含mmc sd sdio)驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.块设备驱动.设备模型等也不进行详细说明原 ...

  8. Linux MMC 驱动子系统简述(源码剖析)

    1. Linux MMC 驱动子系统 块设备是Linux系统中的基础外设之一,而 MMC/SD 存储设备是一种典型的块设备.Linux内核设计了 MMC子系统,用于管理 MMC/SD 设备. MMC ...

  9. 01 Linux入门介绍

    一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ...

随机推荐

  1. GBDT && XGBOOST

                                  GBDT && XGBOOST Outline Introduction GBDT Model XGBOOST Model ...

  2. [译]10个有关SCP的命令

    原文来源: https://www.tecmint.com/scp-commands-examples/ 基本语法 scp source_file_name username@destination_ ...

  3. Uva 294 Divisors(唯一分解定理)

    题意:求区间内正约数最大的数. 原理:唯一分解定义(又称算术基本定理),定义如下: 任何一个大于1的自然数 ,都可以唯一分解成有限个质数的乘积  ,这里  均为质数,其诸指数  是正整数.这样的分解称 ...

  4. 安装floodlight遇到的问题和解决

    环境:ubuntu18.04 安装floodlight先前准备:java的环境,ant. sudo apt-get install build-essential defailt-jdk ant py ...

  5. 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. ...

  6. (转) linux I/O优化 磁盘读写参数设置

    关于页面缓存的信息,可以用cat /proc/meminfo 看到.其中的Cached 指用于pagecache的内存大小(diskcache-SwapCache).随着写入缓存页,Dirty 的值会 ...

  7. javaScript运算符学习笔记

    1.赋值运算符 javaScript运算符可以分为简单赋值和复合赋值运算.简单赋值运算是将赋值运算符(=)右边的表达式的值保存到赋值运算符左边的变量中,复合赋值运算则是混合了其他操作(算术运算操作,位 ...

  8. Java中动态代理实现原理深究

    一.前言 笔者平时开发使用“动态代理”不多,最近在看设计模式的时候,“动态代理”又在面前晃了几次,所以这次想从源码的角度去分析动态代理的实现原理,以窥探其精妙~ 二.正文 2.1 静态代理  本文源码 ...

  9. RxAndroid+RxJava+Gson+retrofit+okhttp初步搭建android网络请求框架

    新建工程集成, 一.工具集成(2017-4-27) 首先第一步集成retrofit retrofit 的 git 网站: https://github.com/square/retrofit 在git ...

  10. 解决Git无法同步空文件夹的问题

    思路:在每个空文件夹下创建空文件,同步后再删除 package org.zln.module1.demo1; import org.apache.log4j.Logger; import java.i ...