声明:本文很多内容和思路参考了http://www.wowotech.net/comm/mmc_host_driver.html,对原作者表示感谢!
1.前言
本文是Linux MMC framework的第二篇,将介绍host对象的基本结构和相应的操作
2.主要数据结构和API
2.1 struct mmc_host
Elemete Name |
struct mmc_host |
Path |
include/linux/mmc/host.h |
Responsiblities |
mmc core使用struct mmc_host来抽象mmc host controller
|
Attributions |
- parent : struct device *类型,指向该mmc host的父设备,一般为注册此mmc_host的platform device
- class_dev:一个struct device类型的变量,是该MMC host在设备模型中作为一个“设备”的体现。当然,人如其名,该设备从属于某一个class(mmc_host_class)
- index:该mmc_host的索引号,因为有可能有多个mmc host controller
- ops:struct mmc_host_ops *类型的指针,包含了此mmc_host所有的操作函数集,见struct mmc_host_ops结构体描述
- f_min、f_max、f_init,该MMC host支持的时钟频率范围,最小频率、最大频率以及初始频率
- ocr_avail:通常的OCR电压范围
- ocr_avail_sdio、ocr_avail_sd、ocr_avail_mmc:sdio sd mmc特定的电压范围
- pm_notify:struct notifier_block类型,电源管理相关的notify
- max_current_330/max_current_300/max_current_180:支持的最大电流
- mmcid:
- caps:host所具备的能力
- caps2:host所具备的其它功能
- pm_caps:mmc_pm_flag_t类型,支持的pm特性
-
- clk_requests:
- clk_delay:
- clk_gated:clk是否已经开启了
- clk_gate_work:struct delayed_work类型
- clk_old:
- clk_lock:spinlock_t 类型,对clk相关临界资源保护
- clk_gate_mutex:struct mutex,保护clk gate的排它锁
- clkgate_delay_attr:struct device_attribute类型
- clkgate_delay:
-
- max_seg_size:一个segment最大为多少字节
- max_segs:最多支持多少segments
- unused:
- max_req_size:一个request的最大字节个数
- max_blk_size:一个mmc block的最大大小
- max_blk_count:一个request的最大block数目
- max_discard_to:最大的discard超时时间(ms)
-
- lock:spinlock_t类型mmc的互斥保护
- ios:struct mmc_ios类型,当前的io总线设置
- ocr:当前的操作电压设置
-
- use_spi_crc:
- claimed:host排他声明
- bus_dead:总线是否被释放
- removed:host是否被移除了
- rescan_disable:是否禁用卡检测,一般初始时禁用,ready后才使能
- rescan_entered:用于非可移除式设备
- card:struct mmc_card *类型,用于连接到这个host的设备
- wq:wait_queue_head_t类型,work队列
- claimer:struct task_struct *类型,host声明的task
- claim_cnt:"claim" nesting count
- detect:struct delayed_work 类型,用于检测卡的work线程
- detect_wake_lock:struct wake_lock
- detect_change:card detect flag
- slot:struct mmc_slot类型;
- bus_ops:struct mmc_bus_ops *类型, current bus driver
- bus_refs:reference counter
- bus_resume_flags:
- sdio_irqs:
- sdio_irq_thread:struct task_struct *类型
- sdio_irq_pending:
- sdio_irq_thread_abort:atomic_t类型
- pm_flags:mmc_pm_flag_t 类型,请求的pm特性
- led:struct led_trigger *类型
- regulator_enabled:regulator状态
- supply:struct mmc_supply类型
- debugfs_root:struct dentry *类型,用于在debugfs中显示相应节点
- areq:struct mmc_async_req *类型, /* active async req */
- context_info:struct mmc_context_info类型,; /* async synchronization info */
- fail_mmc_request:struct fault_attr类型
- actual_clock:Actual HC clock rate
- slotno:used for sdio acpi binding
- private:一个0长度的数组,可以在mmc_alloc_host时指定长度,由host controller driver自行支配
|
|
Operations |
- struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
分配一个mmc_host结构体,并对其进行初始化
- int mmc_add_host(struct mmc_host *host)
将host device注册进设备驱动模型,并做host硬件的初始化,向PM core 注册notify, 在本函数完成之前,必须能保证host可以使用
- void mmc_remove_host(struct mmc_host *host)
将host device从设备驱动模型移除,并将所有的卡与bus切断连接,向pm core注销notify,将mmc bus掉电
- void mmc_free_host(struct mmc_host *host)
释放mmc_host结构体
- int mmc_power_save_host(struct mmc_host *host)
对host掉电处理
- int mmc_power_restore_host(struct mmc_host *host)
恢复host上电
- void mmc_detect_change(struct mmc_host *host, unsigned long delay)
检测mmc卡的状态
- void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
mmc host在处理完一次request后需要调用此函数来进行后处理
- static inline void mmc_signal_sdio_irq(struct mmc_host *host)
- void sdio_run_irqs(struct mmc_host *host);
- int mmc_regulator_get_ocrmask(struct regulator *supply)
返回支持的操作电压范围
- int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, unsigned short vdd_bit);
设置regulator以匹配操作电压
- int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
- int mmc_regulator_get_supply(struct mmc_host *mmc)
通过device tree获取vcc和vccq
- int mmc_pm_notify(struct notifier_block *notify_block, unsigned long mode, void *unused)
用于pm在suspend时做卡移除操作
- static inline int mmc_card_is_removable(struct mmc_host *host)
卡是否被移除
- static inline int mmc_card_keep_power(struct mmc_host *host)
卡在suspend时是否仍然保持供电
- static inline int mmc_card_wake_sdio_irq(struct mmc_host *host)
sdio中断来时是否会唤醒休眠的系统
- static inline int mmc_host_cmd23(struct mmc_host *host)
是否支持CMD23
- static inline int mmc_boot_partition_access(struct mmc_host *host)
是否支持从boot分区访问
- static inline int mmc_host_uhs(struct mmc_host *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)
- void mmc_retune_timer_stop(struct mmc_host *host)
- static inline void mmc_retune_needed(struct mmc_host *host)
- static inline void mmc_retune_recheck(struct mmc_host *host)
|
2.2 struct mmc_host_ops
Elemete Name |
struct mmc_host_ops |
Path |
include/linux/mmc/host.h |
Responsiblities |
主要包含了host contrller 所需要实现的操作
|
Attributions |
- post_req/pre_req:是为了支持双buffer的请求(一个请求正在处理,同时准备另一个请求)
- request:用来处理一次transfer request
如下的函数可能会sleep,不能在中断上下文中调用:
- set_ios:
- get_ro:获取卡的读写状态,返回0表示可读写卡,返回1表示只读卡,返回-ENOSYS表示不支持,返回其它的负值表示其它错误发生
- get_cd:返回0表示卡未连接,返回1表示卡有连接,返回-ENOSYS表示不支持,返回其它的负值表示其它错误发生
- enable_sdio_irq:
- init_card:
- start_signal_voltage_switch:
- card_busy:
- execute_tuning:根据SD和eMMC卡的不同,调节tuning命令操作码值
- select_drive_strength:设定驱动能力
- hw_reset:对device执行reset操作
- card_event:
|
Operations |
|
2.3 struct mmc_ios
Elemete Name |
struct mmc_ios |
Path |
include/linux/mmc/host.h |
Responsiblities |
|
Attributions |
clock:
vdd:
bus_mode:command输出模式,opendrain or pushpull
chip_select:用于SPI片选
power_mode:power supply mode
bus_
timing:时序
signal_voltage:信号电压
drv_type:
|
Operations |
|
3. 主要流程
3.1 mmc_add_host
mmc_add_host->
mmc_start_host->
mmc_detect_change->
mmc_rescan->
mmc_rescan_try_freq->
mmc_attach_mmc
一般在xxx_mmc_probe中执行mmc_add_host来对sd进行注册并完成相应的初始化
mmc_start_host会调用mmc_detect_change会启动detect的work线程来做mmc检测,对应的线程处理函数为mmc_rescan
对一些边界情况进行处理:
a)如果卡检测禁用了,则直接退出;
b)如果是不可移除的卡,如贴片的eMMC;,则只检测一次退出;
c)如果定义了总线的bus_ops->detect函数,则执行之;
如果host的bus被移除了,就为用户空间准备一把超时锁,为做相关处理争取时间;
调用mmc_bus_put来尝试释放总线,即将host->bus_ops 标记为 NULL;
如果host->bus_ops 不为NULL,说明卡在线,已经扫描过,直接退出;
通过调用host->ops->get_cd来获取卡在线状态,如果不在线作相应处理:mmc_power_off和mmc_release_host;
除去上面几种特殊情况,说明是首次扫描卡,则通过调用mmc_rescan_try_freq进行卡扫描
将按照如下的几种频率来对卡进行扫描:
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
mmc_power_up:主要分两阶段MMC_POWER_UP完成给卡的上电,MMC_POWER_ON完成时钟设置以及对模块CLK EN的使能;
mmc_hw_reset_for_init:对卡做reset(主要是对eMMC卡);
sdio_reset:发送CMD52给SDIO卡作reset,SD/eMMC会忽略这个命令;
mmc_go_idle:发送CMD0,命令的flags一般包括了response类型和CMD类型,对于非SPI模式需要将D3设置为高电平,以防进入SPI模式;arg为0,进入IDLE模式。
这里是按照如下的顺序来进行扫描:SDIO SD MMC
以emmc检测为例,因此扫描时会调用回调mmc_attach_mmc()
主要完成card的初始化(一般是identification mode下),并通过mmc_add_card将mmc_card注册到mmc bus,这样就触发了执行前文所述的mmc_blk_probe函数
TODO