1.前言
本文是Linux MMC framework的第二篇,将从驱动工程师的角度,介绍MMC host controller driver有关的知识,学习并掌握如何在MMC framework的框架下,编写MMC控制器的驱动程序。同时,通过本篇文章,我们会进一步的理解MMC、SD、SDIO等有关的基础知识。
2.MMC host驱动介绍
MMC的host driver,是用于驱动MMC host控制器的程序,位于“drivers/mmc/host”目录。从大的流程上看,编写一个这样的驱动非常简单,只需要三步:
当然,看着简单,一牵涉到实现细节,还是很麻烦的,后面我们会慢慢分析。
注1:分析MMC host driver的时候,Linux kernel中有大把大把的例子(例如drivers/mmc/host/pxamci.c),大家可尽情参考、学习,不必谦虚(这是学习Linux的最佳方法)。
注2:由于MMC host driver牵涉到具体的硬件controller,分析的过程中需要一些具体的硬件辅助理解,本文将以“X Project”所使用Bubblegum-96平台为例,具体的硬件spec可参考[1]。
3.主要数据结构
3.1.struct mmc_host
MMC core使用struct mmc_host结构抽象具体的MMC host controller,该结构的定义位于“include/linux/mmc/host.h”中,它既可以用来描述MMC控制器所具有的特性、能力(host driver关心的内容),也保存了host driver运行过程中的一些状态、参数(MMC core关心的内容)。需要host driver关心的部分的具体的介绍如下:
2.struct mmc_host_ops
struct mmc_host_ops抽象并集合了MMC host controller所有的操作函数集,包括:
1.数据传输有关的函数
/*
* It is optional for the host to implement pre_req and post_req in
* order to support double buffering of requests (prepare one
* request while another request is active).
* pre_req() must always be followed by a post_req().
* To undo a call made to pre_req(), call post_req() with
* a nonzero err condition.
*/
void (*post_req)(struct mmc_host *host, struct mmc_request *req, int err);
void (*pre_req)(struct mmc_host *host, struct mmc_request *req, bool is_first_req);
void (*request)(struct mmc_host *host, struct mmc_request *req);
pre_req和post_req是非必需的,host driver可以利用它们实现诸如双buffer之类的高级功能。
数据传输的主题是struct mmc_request类型的指针,具体可参考3.7小节的介绍。
2.总线参数的配置以及卡状态的获取函数
/*
* Avoid calling these three functions too often or in a "fast path",
* since underlaying controller might implement them in an expensive
* and/or slow way.
*
* Also note that these functions might sleep, so don't call them
* in the atomic contexts!
*
* Return values for the get_ro callback should be:
* 0 for a read/write card
* 1 for a read-only card
* -ENOSYS when not supported (equal to NULL callback)
* or a negative errno value when something bad happened
*
* Return values for the get_cd callback should be:
* 0 for a absent card
* 1 for a present card
* -ENOSYS when not supported (equal to NULL callback)
* or a negative errno value when something bad happened
*/
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *host);
int (*get_cd)(struct mmc_host *host);
set_ios用于设置bus的参数(ios,可参考3.5小节的介绍);get_ro可获取card的读写状态(具体可参考上面的注释);get_cd用于检测卡的存在状态。
注4:注释中特别说明了,这几个函数可以sleep,耗时较长,没事别乱用。
3.其它一些非主流函数,都是optional的,用到的时候再去细看即可。
3.3.struct mmc_pwrseq
MMC framework的power sequence是一个比较有意思的功能,它提供一个名称为struct mmc_pwrseq_ops的操作函数集,集合了power on、power off等操作函数,用于控制MMC系统的供电,如下:
struct mmc_pwrseq_ops {
void (*pre_power_on)(struct mmc_host *host);
void (*post_power_on)(struct mmc_host *host);
void (*power_off)(struct mmc_host *host);
void (*free)(struct mmc_host *host);
};
struct mmc_pwrseq {
const struct mmc_pwrseq_ops *ops;
};
与此同时,MMC core提供了一个通用的pwrseq的管理模块(drivers/mmc/core/pwrseq.c),以及一些简单的pwrseq策略(如drivers/mmc/core/pwrseq_simple.c、drivers/mmc/core/pwrseq_emmc.c),最终的目的是,通过一些简单的dts配置,即可正确配置MMC的供电,例如:
/* arch/arm/boot/dts/omap3-igep0020.dts */
mmc2_pwrseq: mmc2_pwrseq {
compatible = "mmc-pwrseq-simple";
reset-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>, /* gpio_139 - RESET_N_W */
<&gpio5 10 GPIO_ACTIVE_LOW>; /* gpio_138 - WIFI_PDN */
};
/* arch/arm/boot/dts/rk3288-veyron.dtsi */
emmc_pwrseq: emmc-pwrseq {
compatible = "mmc-pwrseq-emmc";
pinctrl-0 = <&emmc_reset>;
pinctrl-names = "default";
reset-gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;
};
3.4.Host capabilities
通过的caps和caps2两个字段,MMC host driver可以告诉MMC core控制器的一些特性、能力,包括(具体可参考include/linux/mmc/host.h中相关的宏定义,更为细致的使用场景和指南,需要结合实际的硬件,具体分析):
MMC_CAP_4_BIT_DATA,支持4-bit的总线传输;
MMC_CAP_MMC_HIGHSPEED,支持“高速MMC时序”;
MMC_CAP_SD_HIGHSPEED,支持“高速SD时序”;
MMC_CAP_SDIO_IRQ,可以产生SDIO有关的中断;
MMC_CAP_SPI,仅仅支持SPI协议(可参考“drivers/mmc/host/mmc_spi.c”中有关的实现);
MMC_CAP_NEEDS_POLL,表明需要不停的查询卡的插入状态(如果需要支持热拔插的卡,则需要设置该feature);
MMC_CAP_8_BIT_DATA,支持8-bit的总线传输;
MMC_CAP_AGGRESSIVE_PM,支持比较积极的电源管理策略(kernel的注释为“Suspend (e)MMC/SD at idle”);
MMC_CAP_NONREMOVABLE,表明该MMC控制器所连接的卡是不可拆卸的,例如eMMC;
MMC_CAP_WAIT_WHILE_BUSY,表面Host controller在向卡发送命令时,如果卡处于busy状态,是否等待。如果不等待,MMC core在一些流程中(如查询busy状态),需要额外做一些处理;
MMC_CAP_ERASE,表明该MMC控制器允许执行擦除命令;
MMC_CAP_1_8V_DDR,支持工作电压为1.8v的DDR(Double Data Rate)mode[3];
MMC_CAP_1_2V_DDR,支持工作电压为1.2v的DDR(Double Data Rate)mode[3];
MMC_CAP_POWER_OFF_CARD,可以在启动之后,关闭卡的供电(一般只有在SDIO的应用场景中才可能用到,因为SDIO所连接的设备可能是一个独立的设备);
MMC_CAP_BUS_WIDTH_TEST,支持通过CMD14和CMD19进行总线宽度的测试,以便选择一个合适的总线宽度进行通信;
MMC_CAP_UHS_SDR12、MMC_CAP_UHS_SDR25、MMC_CAP_UHS_SDR50、MMC_CAP_UHS_SDR104,它们是SD3.0的速率模式,分别表示支持25MHz、50MHz、100MHz和208MHz的SDR(Single Data Rate,相对[3]中的DDR)模式;
MMC_CAP_UHS_DDR50,它也是SD3.0的速率模式,表示支持50MHz的DDR(Double Data Rate[3])模式;
MMC_CAP_DRIVER_TYPE_A、MMC_CAP_DRIVER_TYPE_C、MMC_CAP_DRIVER_TYPE_D,分别表示支持A/C/D类型的driver strength(驱动能力,具体可参考相应的spec);
MMC_CAP_CMD23,表示该controller支持multiblock transfers(通过CMD23);
MMC_CAP_HW_RESET,支持硬件reset;
MMC_CAP2_FULL_PWR_CYCLE,表示该controller支持从power off到power on的完整的power cycle;
MMC_CAP2_HS200_1_8V_SDR、MMC_CAP2_HS200_1_2V_SDR,HS200是eMMC5.0支持的一种速率模式,200是200MHz的缩写,分别表示支持1.8v和1.2v的SDR模式;
MMC_CAP2_HS200,表示同时支持MMC_CAP2_HS200_1_8V_SDR和MMC_CAP2_HS200_1_2V_SDR;
MMC_CAP2_HC_ERASE_SZ,支持High-capacity erase size;
MMC_CAP2_CD_ACTIVE_HIGH,CD(Card-detect)信号高有效;
MMC_CAP2_RO_ACTIVE_HIGH,RO(Write-protect)信号高有效;
MMC_CAP2_PACKED_RD、MMC_CAP2_PACKED_WR,允许packed read、packed write;
MMC_CAP2_PACKED_CMD,同时支持DMMC_CAP2_PACKED_RD和MMC_CAP2_PACKED_WR;
MMC_CAP2_NO_PRESCAN_POWERUP,在scan之前不要上电;
MMC_CAP2_HS400_1_8V、MMC_CAP2_HS400_1_2V,HS400是eMMC5.0支持的一种速率模式,400是400MHz的缩写,分别表示支持1.8v和1.2v的HS400模式;
MMC_CAP2_HS400,同时支持MMC_CAP2_HS400_1_8V和MMC_CAP2_HS400_1_2V;
MMC_CAP2_HSX00_1_2V,同时支持MMC_CAP2_HS200_1_2V_SDR和MMC_CAP2_HS400_1_2V;
MMC_CAP2_SDIO_IRQ_NOTHREAD,SDIO的IRQ的处理函数,不能在线程里面执行;
MMC_CAP2_NO_WRITE_PROTECT,没有物理的RO管脚,意味着任何时候都是可以读写的;
MMC_CAP2_NO_SDIO,在初始化的时候,不会发送SDIO相关的命令(也就是说不支持SDIO模式)。
3.5.struct mmc_ios
struct mmc_ios中保存了MMC总线当前的配置情况,包括如下信息:
MMC_TIMING_LEGACY,旧的、不再使用的规范;
MMC_TIMING_MMC_HS,High speed MMC规范(具体可参考相应的spec,这里不再详细介绍,下同);
MMC_TIMING_SD_HS,High speed SD;
MMC_TIMING_UHS_SDR12;
MMC_TIMING_UHS_SDR25
MMC_TIMING_UHS_SDR50
MMC_TIMING_UHS_SDR104
MMC_TIMING_UHS_DDR50
MMC_TIMING_MMC_DDR52
MMC_TIMING_MMC_HS200
MMC_TIMING_MMC_HS400
8.signal_voltage,总线信号使用哪一种电压,3.3v(MMC_SIGNAL_VOLTAGE_330)1.8v(MMC_SIGNAL_VOLTAGE_180)或者1.2v(MMC_SIGNAL_VOLTAGE_120)。
9.drv_type,驱动能力,包括:
MMC_SET_DRIVER_TYPE_B
MMC_SET_DRIVER_TYPE_A
MMC_SET_DRIVER_TYPE_C
MMC_SET_DRIVER_TYPE_D
3.6.struct mmc_supply
struct mmc_supply中保存了两个struct regulator指针(如下),用于控制MMC子系统有关的供电(vmmc和vqmmc)。
struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
struct regulator *vqmmc; /* Optional Vccq supply */
};
关于vmmc和vqmmc,说明如下:
3.7.struct mmc_request
struct mmc_request封装了一次传输请求,定义如下:
/* include/linux/mmc/core.h */
struct mmc_request {
struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_command *stop;
struct completion completion;
void (*done)(struct mmc_request *);/* completion function */
struct mmc_host *host;
};
要理解这个数据结构,需要先了解MMC的总线协议(bus protocol),这里以eMMC[2]为例进行简单的介绍(更为详细的解释,可参考相应的spec以及本站的文章--“eMMC 原理 4 :总线协议[7]”)。
3.7.1.MMC bus protocol
在eMMC的spec中,称总线协议为“message-based MultiMediaCard bus protocol”,这里的message由三种信标(token)组成:
以上token除了Command之外,剩余的两个(Response和Data)都是非必需的,也就是说,一次传输可以是:不需要应答、不需要数据传输的Command;需要应答、不需要数据传输的Command;不需要应答、需要数据传输的Command;不需要应答、不需要数据传输的Command。
Command token的格式只有一种(具体可参考[2]中“Command token format”有关的表述),长度为48bits,包括Start bit(0)、Transmitter bit(1, host command)、Content(38bits)、CRC checksum(7bits)、Stop bit(1)。
根据内容的不同,Response token的格式有5中,分别简称为R1/R3/R4/R5/R2,其中R1/R3/R4/R5的长度为48bits,R2为136bits(具体可参考[2]中“Response token format”有关的表述)。
对于包含了Data token的Command,有两种类型:
最后,以block为单位的传输,大体上也分为两类:
3.7.2.struct mmc_request
了解MMC bus protocol之后,再来看一次MMC传输请求(struct mmc_request )所包含的内容:
3.7.3.struct mmc_command
struct mmc_command结构抽象了一个MMC command,包括如下内容:
3.7.4.struct mmc_data
struct mmc_data结构包含了数据传输有关的内容:
4.主要API
第3章花了很大的篇幅介绍了用于抽象MMC host的数据结构----struct mmc_host,并详细说明了和mmc_host相关的mmc request、mmc command、mmc data等结构。基于这些知识,本章将介绍MMC core提供的和struct mmc_host有关的操作函数,主要包括如下几类。
4.1.向MMC host controller driver提供的用于操作struct mmc_host的API
包括:
struct mmc_host *mmc_alloc_host(int extra, struct device *);
int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
int mmc_of_parse(struct mmc_host *host);
static inline void *mmc_priv(struct mmc_host *host) {
return (void *)host->private;
}
int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);
从mmc host的角度进行电源管理,进入/退出power save状态。
void mmc_detect_change(struct mmc_host *, unsigned long delay);
void mmc_request_done(struct mmc_host *, struct mmc_request *);
当host driver处理完成一个mmc request之后,需要调用该函数通知MMC core,MMC core会进行一些善后的操作,例如校验结果、调用mmc request的.done回调等等。
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
void sdio_run_irqs(struct mmc_host *host);
对于SDIO类型的总线,这两个函数用于操作SDIO irqs,后面用到的时候再分析。
int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, unsigned short vdd_bit);
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);
int mmc_regulator_get_supply(struct mmc_host *mmc);
regulator有关的辅助函数:
4.2.用于判断MMC host controller所具备的能力的API
比较简单,可结合第3章的介绍理解:
#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI)
static inline int mmc_card_is_removable(struct mmc_host *host)
static inline int mmc_card_keep_power(struct mmc_host *host)
static inline int mmc_card_wake_sdio_irq(struct mmc_host *host)
static inline int mmc_host_cmd23(struct mmc_host *host)
static inline int mmc_boot_partition_access(struct mmc_host *host)
static inline int mmc_host_uhs(struct mmc_host *host)
static inline int mmc_host_packed_wr(struct mmc_host *host)
static inline int mmc_card_hs(struct mmc_card *card)
static inline int mmc_card_uhs(struct mmc_card *card)
static inline bool mmc_card_hs200(struct mmc_card *card)
static inline bool mmc_card_ddr52(struct mmc_card *card)
static inline bool mmc_card_hs400(struct mmc_card *card)
static inline void mmc_retune_needed(struct mmc_host *host)
static inline void mmc_retune_recheck(struct mmc_host *host)
5.MMC host驱动的编写步骤
经过上面章节的描述,相信大家对MMC controller driver有了比较深的理解,接下来驱动的编写就是一件水到渠成的事情了。这里简要描述一下驱动编写步骤,也顺便为本文做一个总结。
5.1.struct mmc_host的填充和注册
编写MMC host驱动的所有工作,都是围绕struct mmc_host结构展开的。在对应的platform driver的probe函数中,通过mmc_alloc_host分配一个mmc host后,我们需要根据controller的实际情况,填充对应的字段。
mmc host中大部分和controller能力/特性有关的字段,可以通过dts配置(然后在代码中调用mmc_of_parse自动解析并填充),举例如下(注意其中红色的部分,都是MMC framework的标准字段):
/* arch/arm/boot/dts/exynos5420-peach-pit.dts */
&mmc_1 {
status = "okay";
num-slots = <1>;
non-removable;
cap-sdio-irq;
keep-power-in-suspend;
clock-frequency = <400000000>;
samsung,dw-mshc-ciu-div = <1>;
samsung,dw-mshc-sdr-timing = <0 1>;
samsung,dw-mshc-ddr-timing = <0 2>;
pinctrl-names = "default";
pinctrl-0 = <&sd1_clk>, <&sd1_cmd>, <&sd1_int>, <&sd1_bus1>,
<&sd1_bus4>, <&sd1_bus8>, <&wifi_en>;
bus-width = <4>;
cap-sd-highspeed;
mmc-pwrseq = <&mmc1_pwrseq>;
vqmmc-supply = <&buck10_reg>;
};
5.2 数据传输的实现
填充struct mmc_host变量的过程中,工作量最大的,就是对struct mmc_host_ops的实现(毫无疑问!所有MMC host的操作逻辑都封在这里呢!!)。这里简单介绍一下相关的概念,具体的驱动编写步骤,后面文章会结合“X Project”详细描述。
5.2.1.Sectors(扇区)、Blocks(块)以及Segments(段)的理解
我们在3.1小节介绍struct mmc_host的时候,提到了max_seg_size、max_segs、max_req_size、max_blk_size、max_blk_count等参数。这和磁盘设备(块设备)中Sectors、Blocks、Segments等概念有关,下面简单介绍一下:
1.Sectors
2.Blocks
Blocks是数据传输的基本单位,是VFS(虚拟文件系统)抽象出来的概念,是纯软件的概念,和硬件无关。它必须是2的整数倍、不能大于Sectors的单位、不能大于page的长度,一般为512B、2048B或者4096B。
对MMC设备来说,由于协议的封装,淡化了Sector的概念,或者说,MMC设备可以支持一定范围内的任意的Block size。Block size的范围,由两个因素决定:
3.Segments
块设备的数据传输,本质上是设备上相邻扇区与内存之间的数据传输。通常情况下,为了提升性能,数据传输通过DMA方式。
在磁盘控制器的旧时代,DMA操作都比较简单,每次传输,数据在内存中必须是连续的。现在则不同,很多SOC都支持“分散/聚合”(scatter-gather)DMA操作,这种操作模式下,数据传输可以在多个非连续的内存区域中进行。对于每个“分散/聚合”DMA操作,块设备驱动需要向控制器发送:
a)初始扇区号和传输的总共扇区数
b)内存区域的描述链表,每个描述都包含一个地址和长度。不同的描述之间,可以在物理上连续,也可以不连续。
控制器来管理整个数据传输,例如:在读操作中,控制器从块设备相邻的扇区上读取数据,然后将数据分散存储在内存的不同区域。这里的每个内存区域描述(物理连续的一段内存,可以是一个page,也可以是page的一部分),就称作Segment。一个Segment包含多个相邻扇区。最后,利用“分散/聚合”的DMA操作,一次数据传输可以会涉及多个segments。
理解了Segment的概念之后,max_seg_size和max_segs两个字段就好理解了:
虽然控制器支持“分散/聚合”的DMA操作,但物理硬件总有限制,例如最大的Segment size(也即一个内存描述的最大长度),最多支持的segment个数(max_segs)等。
5.2.2.struct mmc_data中的sg
我们在3.7.4小节介绍struct mmc_data时,提到了scatterlist的概念。结合上面Segment的解释,就很好理解了:
6.参考文档
[1] SoC_bubblegum96.pdf
[2] JESD84-A44.pdf
[3] DDR mode, https://en.wikipedia.org/wiki/Double_data_rate
[4] http://www.hjreggel.net/cardspeed/cs_sdxc.html
[5] regulator framework,http://www.wowotech.net/tag/regulator
[6] MMC/SD/SDIO介绍
[7] eMMC 原理 4 :总线协议
[8] http://www.ilinuxkernel.com/files/Linux.Generic.Block.Layer.pdf
本文转自:蜗窝科技