- 了解mmc host driver.
1.host相关数据结构
1.1.struct mmc_host
struct mmc_host是mmc core由host controller抽象出来的结构体,用于代表一个mmc host控制器。
struct mmc_host {
struct device *parent; // 对应的host controller的device
struct device class_dev; // mmc_host的device结构体,会挂在class/mmc_host下
int index; // 该host的索引号
const struct mmc_host_ops *ops; // 该host的操作集,由host controller设置,后面说明
unsigned int f_min; // 该host支持的最低频率
unsigned int f_max; // 该host支持的最大频率
unsigned int f_init; // 该host使用的初始化频率
u32 ocr_avail; // 该host可用的ocr值(电压相关)
u32 ocr_avail_sdio; /* SDIO-specific OCR */
u32 ocr_avail_sd; /* SD-specific OCR */
u32 ocr_avail_mmc; /* MMC-specific OCR */
struct notifier_block pm_notify;
u32 max_current_330; // 3.3V时的最大电流
u32 max_current_300; // 3.0V时的最大电流
u32 max_current_180; // 1.8V时的最大电流
u32 caps; /* Host capabilities */ // host属性
u32 caps2; /* More host capabilities */ // host属性2
mmc_pm_flag_t pm_caps; /* supported pm features */ // 电源管理属性
int clk_requests; /* internal reference counter */
unsigned int clk_delay; /* number of MCI clk hold cycles */
bool clk_gated; /* clock gated */
struct delayed_work clk_gate_work; /* delayed clock gate */
unsigned int clk_old; /* old clock value cache */
spinlock_t clk_lock; /* lock for clk fields */
struct mutex clk_gate_mutex; /* mutex for clock gating */
struct device_attribute clkgate_delay_attr;
unsigned long clkgate_delay;
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_segs; /* see blk_queue_max_segments */
unsigned short unused;
unsigned int max_req_size; /* maximum number of bytes in one req */
unsigned int max_blk_size; /* maximum size of one mmc block */
unsigned int max_blk_count; /* maximum number of blocks in one req */
unsigned int max_discard_to; /* max. discard timeout in ms */
/* private data */
spinlock_t lock; /* lock for claim and bus ops */ // host的bus使用的锁
struct mmc_ios ios; /* current io bus settings */ // io setting,后续说明
u32 ocr; /* the current OCR setting */ // 当前使用的ocr的值
/* group bitfields together to minimize padding */
unsigned int use_spi_crc:1;
unsigned int claimed:1; /* host exclusively claimed */ // host是否已经被占用
unsigned int bus_dead:1; /* bus has been released */ // host的bus是否处于激活状态
int rescan_disable; /* disable card detection */ // 禁止rescan的标识,禁止搜索card
int rescan_entered; /* used with nonremovable devices */ // 是否已经rescan过的标识,对应不可移除的设备只能rescan一次
struct mmc_card *card; /* device attached to this host */ // 和该host绑定在一起的card
wait_queue_head_t wq;
struct task_struct *claimer; /* task that has host claimed */ // 该host的占有者进程
struct task_struct *suspend_task;
int claim_cnt; /* "claim" nesting count */ // 占有者进程对该host的占用计数
struct delayed_work detect; // 检测卡槽变化的工作
struct wake_lock detect_wake_lock; // 检测卡槽变化的工作使用的锁
const char *wlock_name; // 锁名称
int detect_change; /* card detect flag */ // 需要检测卡槽变化的标识
struct mmc_slot slot; // 卡槽的结构体
const struct mmc_bus_ops *bus_ops; /* current bus driver */ // host的mmc总线的操作集,后面说明
unsigned int bus_refs; /* reference counter */ // host的mmc总线的使用计数
unsigned int bus_resume_flags; // host的mmc总线的resume标识
mmc_pm_flag_t pm_flags; /* requested pm features */
#ifdef CONFIG_REGULATOR
bool regulator_enabled; /* regulator state */ // 代表regulator(LDO)的状态
#endif
struct mmc_supply supply;
struct dentry *debugfs_root; // 对应的debug目录结构体
struct mmc_async_req *areq; /* active async req */ // 当前正在处理的异步请求
struct mmc_context_info context_info; /* async synchronization info */ // 异步请求的信息
unsigned int actual_clock; /* Actual HC clock rate */ // 实际的时钟频率
};
- ocr值各个位代表的电压意义如下:
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
- host属性(mmc_host->caps)支持的属性如下:
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
#define MMC_CAP_MMC_HIGHSPEED (1 << 1) /* Can do MMC high-speed timing */
#define MMC_CAP_SD_HIGHSPEED (1 << 2) /* Can do SD high-speed timing */
#define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
#define MMC_CAP_1_8V_DDR (1 << 11) /* can support */
/* DDR mode at 1.8V */
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
/* DDR mode at 1.2V */
#define MMC_CAP_HSDDR (MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)
#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
#define MMC_CAP_BUS_WIDTH_TEST (1 << 14) /* CMD14/CMD19 bus width ok */
#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS SDR12 mode */
#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS SDR25 mode */
#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
- host属性2(mmc_host->caps2)支持的属性如下:
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
#define MMC_CAP2_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */
#define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
#define MMC_CAP2_PACKED_RD (1 << 12) /* Allow packed read */
#define MMC_CAP2_PACKED_WR (1 << 13) /* Allow packed write */
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
MMC_CAP2_PACKED_WR)
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
#define MMC_CAP2_PACKED_WR_CONTROL (1 << 16) /* Allow write packing control */
#define MMC_CAP2_CLK_SCALE (1 << 17) /* Allow dynamic clk scaling */
#define MMC_CAP2_STOP_REQUEST (1 << 18) /* Allow stop ongoing request */
/* Use runtime PM framework provided by MMC core */
#define MMC_CAP2_CORE_RUNTIME_PM (1 << 19)
#define MMC_CAP2_SANITIZE (1 << 20) /* Support Sanitize */
/* Allows Asynchronous SDIO irq while card is in 4-bit mode */
#define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 21)
#define MMC_CAP2_HS400_1_8V (1 << 22) /* can support */
#define MMC_CAP2_HS400_1_2V (1 << 23) /* can support */
#define MMC_CAP2_CORE_PM (1 << 24) /* use PM framework */
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_NONHOTPLUG (1 << 25) /*Don't support hotplug*/
1.2.struct mmc_host_ops
mmc core将host需要提供的一些操作方法封装成struct mmc_host_ops。mmc core主模块的很多接口都是基于这里面的操作方法来实现的,通过这些方法来操作host硬件达到对应的目的。 所以struct mmc_host_ops也是host controller driver需要实现的核心部分。
struct mmc_host_ops {
/*
* 'enable' is called when the host is claimed and 'disable' is called
* when the host is released. 'enable' and 'disable' are deprecated.
*/
int (*enable)(struct mmc_host *host); // 使能host,当host被占用时(第一次调用mmc_claim_host)调用
int (*disable)(struct mmc_host *host); // 禁用host,当host被释放时(第一次调用mmc_release_host)调用
/*
* 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.
*/
// post_req和pre_req是为了实现异步请求处理而设置的
// 异步请求处理就是指,当另外一个异步请求还没有处理完成的时候,可以先准备另外一个异步请求而不必等待
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); // host处理mmc请求的方法,在mmc_start_request中会调用
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); // 设置host的总线的io setting
int (*get_ro)(struct mmc_host *host); // 获取host上的card的读写属性
int (*get_cd)(struct mmc_host *host); // 检测host的卡槽中card的插入状态
/* optional callback for HC quirks */
void (*init_card)(struct mmc_host *host, struct mmc_card *card); // 初始化card的方法
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios); // 切换信号电压的方法
/* Check if the card is pulling dat[0:3] low */
int (*card_busy)(struct mmc_host *host); // 用于检测card是否处于busy状态
/* The tuning command opcode value is different for SD and eMMC cards */
int (*execute_tuning)(struct mmc_host *host, u32 opcode); // 执行tuning操作,为card选择一个合适的采样点
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); // 选择信号的驱动强度
void (*hw_reset)(struct mmc_host *host); // 硬件复位
void (*card_event)(struct mmc_host *host); //
unsigned long (*get_max_frequency)(struct mmc_host *host); // 获取host支持的最大频率的方法
unsigned long (*get_min_frequency)(struct mmc_host *host); // 获取host支持的最小频率的方法
int (*notify_load)(struct mmc_host *, enum mmc_load);
int (*stop_request)(struct mmc_host *host); // 停止请求处理的方法
unsigned int (*get_xfer_remain)(struct mmc_host *host);
};
1.3.mmc_host_class
mmc_host_class代表了mmc_host这个类。其内容如下:
static struct class mmc_host_class = {
.name = "mmc_host", // 添加到sys文件系统之后,会生成/sys/class/mmc_host这个目录
.dev_release = mmc_host_classdev_release, // 从mmc_host这个class下release掉某个设备之后要做的对应操作
};
2.APIs
2.1.mmc host分配、释放
struct mmc_host *mmc_alloc_host(int extra, struct device *dev);
void mmc_free_host(struct mmc_host *host)
- 用来分配或者释放一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。
2.2.mmc host 注册、卸载
int mmc_add_host(struct mmc_host *host);
void mmc_remove_host(struct mmc_host *host);
- 注册或者卸载mmc_host到设备驱动中,添加到sys类下面,并设置相应的debug目录,然后启动mmc_host。
2.3.mmc host class 注册、卸载
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
- 注册或者卸载mmc_host class
2.4.mmc host 属性解析(关键函数)
void mmc_of_parse(struct mmc_host *host);
- 底层host controller驱动调用,解析mmc_host的dtsi节点的部分属性。
3.Interface
3.1.mmc_register_host_class
int mmc_register_host_class(void)
{
return class_register(&mmc_host_class); // 以mmc_host_class为class创建一个class,关于mmc_host_class在上述数据结构已经说明过了
}
- /sys/class/mmc_host
3.2.mmc_alloc_host
主要工作:
- 分配内存空间
- 初始化其class device(对应/sys/class/mmc0节点)
- clock gate、锁、工作队列、wakelock、detect工作的初始化
- 初始化detect成员(也就是检测工作)为mmc_rescan
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
int err;
struct mmc_host *host;
/* 分配内存空间,其中多分配了extra字节作为私有数据 */
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
/* 因为只是分配了一个mmc_host,host还没有准备好,所以这里禁用rescan,也就是设置mmc_host->rescan_disable */
host->rescan_disable = 1; // mmc_start_host中会去使能
/* 为该mmc_host分配一个唯一的id号,设置到host->index */
idr_preload(GFP_KERNEL);
spin_lock(&mmc_host_lock);
err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
if (err >= 0)
host->index = err;
spin_unlock(&mmc_host_lock);
idr_preload_end();
dev_set_name(&host->class_dev, "mmc%d", host->index); // 以mmc_host的id号构成mmc_host的name,例如mmc0、mmc1
host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
// 将mmc_device(class_dev)的类设置为mmc_host_class
// 注册到sysfs之后,会相应生成/sys/class/mmc_host/mmc0
device_initialize(&host->class_dev); // 初始化mmc_host->class_dev
device_enable_async_suspend(&host->class_dev);
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
// 初始化detect工作为mmc_rescan,后续调度host->detect来检测是否有card插入时,就会调用到mmc_rescan。
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
timer_setup(&host->retune_timer, mmc_retune_timer, 0);
/* 一些size的初始化 */
host->max_segs = 1; // 初始化最大支持段(由host自己根据硬件进行修改),可以通过/sys/block/mmcblk0/queue/max_segments进行修改
host->max_seg_size = PAGE_SIZE; // 初始化段大小,(由host自己根据硬件进行修改)
host->max_req_size = PAGE_SIZE; // 一次MMC请求的最大字节数
host->max_blk_size = 512; // 一个块的最大字节数
host->max_blk_count = PAGE_SIZE / 512; // 一次MMC请求的最大块数量
return host;
}
重点如下:
mmc_alloc_host
-> INIT_DELAYED_WORK(&host->detect, mmc_rescan); //mmc_rescan //用来做卡的检测
何时调用该work? 如下所示。
3.3.mmc_add_host实现
主要工作:
- 将mmc_host的class_dev添加到设备驱动模型中,在sysfs中生成相应的节点
- 初始化mmc_host相关的debug目录
- 调用mmc_start_host启动host,调用mmc_rescan
int mmc_add_host(struct mmc_host *host)
{
int err;
/* 将mmc_host->class_dev添加到设备驱动模型中,在sys下生成相应节点 */
err = device_add(&host->class_dev);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
/*对应sys节点为/sys/kernel/debug/mmc0 */
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
mmc_start_host(host);
mmc_register_pm_notifier(host);
return 0;
}
3.3.1.创建节点:
- /sys/class/mmc_host/mmc0
- /sys/kernel/debug/mmc0
- /sys/bus/platform/devices/d4280000.sdhci/mmc_host/mmc0
3.3.2.mmc_start_host
在mmc_alloc_host中初始化一个work,即mmc_rescan。
其中初始化了一个work节点,mmc_rescan这个函数比较重要,用来做卡的检测。
mmc_add_host向系统添加一个mmc_host,并调用mmc_start_host:
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
mmc_claim_host(host);
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host);
}
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
SD dts配置:
&sdhc_2 {
#address-cells = <0>;
interrupt-parent = <&sdhc_2>;
interrupts = <0 1 2>;
#interrupt-cells = <1>;
interrupt-map-mask = <0xffffffff>;
interrupt-map = <0 &intc 0 125 0
1 &intc 0 221 0
2 &msm_gpio 38 0>;
interrupt-names = "hc_irq", "pwr_irq", "status_irq";
vqmmc-supply = <®_eldo1>;
cd-gpios = <&msm_gpio 38 0x1>; //0x1 - 表示插卡低有效
电压设置:
vqmmc-supply = <®_eldo1>;
reg_eldo1:eldo1 {
regulator-name = "vddio-sdmmc";
regulator-min-mincrovolt=<1800000>;
regulator-max-mincrovolt=<1800000>;
}
函数调用流程:
sdhci_setup_host:
->mmc_regulator_get_supply //通过device tree获取vmmc和vqmmc
SD检卡中断检测:
Card detection:
If no property below is supplied, host native card detect is used.
Only one of the properties in this section should be supplied:
broken-cd: There is no card detection available; polling must be used.
cd-gpios: Specify GPIOs for card detection, see gpio binding
non-removable: non-removable slot (like eMMC); assume always present.
- cd-gpios = <&msm_gpio 38 0x1>; // 最后一位,需要根据实际情况进行设置;
#define GPIO_ACTIVE_HIGH 0
#define GPIO_ACTIVE_LOW 1
内核提供两种方式进行sd card detection:
- 轮训方式(sdhci_setup_host)
/*
* Enable polling on when card detection is broken and no card detect
* gpio is present.
*/
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
mmc_card_is_removable(mmc) &&
mmc_gpio_get_cd(host->mmc) < 0)
mmc->caps |= MMC_CAP_NEEDS_POLL;
- gpio中断检卡方式(mmc_of_parse):(cat /proc/interrupts )
/* Parse Card Detection */
if (device_property_read_bool(dev, "non-removable")) {
host->caps |= MMC_CAP_NONREMOVABLE;
} else {
cd_cap_invert = device_property_read_bool(dev, "cd-inverted");
if (device_property_read_bool(dev, "broken-cd"))
host->caps |= MMC_CAP_NEEDS_POLL;
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
0, &cd_gpio_invert);
if (!ret)
dev_info(host->parent, "Got CD GPIO\n");
else if (ret != -ENOENT && ret != -ENOSYS)
return ret;
if (cd_cap_invert ^ cd_gpio_invert)
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
}
中断注册:mmc_gpiod_request_cd_irq:
void mmc_gpiod_request_cd_irq(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
int ret, irq;
if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
return;
irq = gpiod_to_irq(ctx->cd_gpio);
if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
irq = -EINVAL;
if (irq >= 0) {
if (!ctx->cd_gpio_isr)
ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
ret = devm_request_threaded_irq(host->parent, irq,
NULL, ctx->cd_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
ctx->cd_label, host);
if (ret < 0)
irq = ret;
}
host->slot.cd_irq = irq;
if (irq < 0)
host->caps |= MMC_CAP_NEEDS_POLL;
else if ((host->caps & MMC_CAP_CD_WAKE) && !enable_irq_wake(irq))
host->slot.cd_wake_enabled = true;
}
//中断处理mmc_gpio_cd_irqt:
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{
/* Schedule a card detection after a debounce timeout */
struct mmc_host *host = dev_id;
host->trigger_card_event = true;
mmc_detect_change(host, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
mmc_detect_change会调用_mmc_detect_change ,如下所示:
mmc_schedule_delayed_work(&host->detect, delay);向work_queue提交一个work来检测卡,具体的检测函数就是mmc_rescan:
如果设置了MMC_CAP_NONREMOVABLE,表示host不支持热插拔卡,否则调用mmc_host_ops的get_cd检查是否有卡,如果有卡,则调用mmc_rescan_try_freq继续探测,如果探测到总线上挂有有效的设备,按照设备是否支持相应协议命令来区分sdio设备,SD卡和MMC卡。
- 如果是SDID,调用mmc_attach_sdio;
- 如果是SD,调用mmc_attach_sd;
- 如果是MMC,调用mmc_attach_mmc;
调用如下所示:
2444 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
2445 {
2446 host->f_init = freq;
2451 mmc_power_up(host, host->ocr_avail);
2452
2453 /*
2454 * Some eMMCs (with VCCQ always on) may not be reset after power up, so
2455 * do a hardware reset if possible.
2456 */
2457 mmc_hw_reset_for_init(host);
2458
2459 /*
2460 * sdio_reset sends CMD52 to reset card. Since we do not know
2461 * if the card is being re-initialized, just send it. CMD52
2462 * should be ignored by SD/eMMC cards.
2463 * Skip it if we already know that we do not support SDIO commands
2464 */
2465 if (!(host->caps2 & MMC_CAP2_NO_SDIO))
2466 sdio_reset(host);
2468 mmc_go_idle(host);
2469
2470 if (!(host->caps2 & MMC_CAP2_NO_SD))
2471 mmc_send_if_cond(host, host->ocr_avail);
2472
2473 /* Order's important: probe SDIO, then SD, then MMC */
2474 if (!(host->caps2 & MMC_CAP2_NO_SDIO))
2475 if (!mmc_attach_sdio(host))
2476 return 0;
2477
2478 if (!(host->caps2 & MMC_CAP2_NO_SD))
2479 if (!mmc_attach_sd(host))
2480 return 0;
2481
2482 if (!(host->caps2 & MMC_CAP2_NO_MMC))
2483 if (!mmc_attach_mmc(host))
2484 return 0;
2485
2486 mmc_power_off(host);
2487 return -EIO;
2488 }
- mmc_go_idle函数:发送CMD0使卡进入IDLE状态
- mmc_send_if_cond函数:发送CMD8,检查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出先发送CMD8,如响应为无效命令,则卡为SD1.1,否则就是SD2.0。
- mmc_send_app_op_cond:循环发送CMD55、ACMD41给卡读取OCR寄存器。
3.4.调用mmc:mmc_attach_mmc
2196 int mmc_attach_mmc(struct mmc_host *host)
2197 {
2198 int err;
2199 u32 ocr, rocr;
2202
2203 /* Set correct bus mode for MMC before attempting attach */
2204 if (!mmc_host_is_spi(host))
2205 mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
2206
2207 err = mmc_send_op_cond(host, 0, &ocr);
2210
2211 mmc_attach_bus(host, &mmc_ops);
2212 if (host->ocr_avail_mmc)
2213 host->ocr_avail = host->ocr_avail_mmc;
2214
2218 if (mmc_host_is_spi(host)) {
2219 err = mmc_spi_read_ocr(host, 1, &ocr);
2221 goto err;
2222 }
2223
2224 rocr = mmc_select_voltage(host, ocr);
2237 err = mmc_init_card(host, rocr, NULL);
2240
2241 mmc_release_host(host);
2242 err = mmc_add_card(host->card);
2245
2246 mmc_claim_host(host);
2247 return 0;
2248 }
- 调用mmc_init_card,完成card的初始化;
- 调用mmc_add_card将mmc_card注册到mmc bus,这样就触发了mmc_blk_probe函数。
3.5.调用sdcard:mmc_attach_sd
int mmc_attach_sd(struct mmc_host *host)
{
int err;
u32 ocr, rocr;
err = mmc_send_app_op_cond(host, 0, &ocr);
mmc_attach_bus(host, &mmc_sd_ops);
if (host->ocr_avail_sd)
host->ocr_avail = host->ocr_avail_sd;
if (mmc_host_is_spi(host)) {
mmc_go_idle(host);
err = mmc_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
ocr &= ~0x7FFF;
rocr = mmc_select_voltage(host, ocr);
err = mmc_sd_init_card(host, rocr, NULL);
mmc_release_host(host);
err = mmc_add_card(host->card);
mmc_claim_host(host);
return 0;
}
3.5.1.代码分析:
- mmc_attach_bus(host, &mmc_sd_ops);
// sd卡的总线操作 core/sd.c
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
.sysfs_add = mmc_sd_sysfs_add,
.sysfs_remove = mmc_sd_sysfs_remove,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
};
.detect,驱动程序调用此函数去检测mmc卡的状态,具体实现是发送CMD13命令,并读回响应,如果响应错误,则依次调用.remove、detach_bus来移除卡及释放总线。
-
mmc_sd_init_card
- mmc_sd_get_cid(host, ocr, cid, &rocr); //发送 CMD2获取卡的身份信息,进入到身份状态
- mmc_alloc_card(host, &sd_type); //分配一张 SD 类型的 card 结构
- mmc_send_relative_addr(host, &card->rca); //获取卡的相对地址
- mmc_sd_get_csd(host, card); //CMD9, 获取 CSD 寄存器的信息,包括 block 长度,卡容量等信息
- mmc_select_card(card); //发送 CMD7, 选中目前 RADD 地址上的卡,任何时候总线上只有一张卡被选中,进入了传输状态
- mmc_sd_setup_card(host, card, oldcard != NULL); //发送ACMD51获取SCR寄存器值,发送ACMD13获取SD卡状态信息,解析并填充card结构,SCR寄存器是对CSD的补充。
- mmc_sd_init_uhs_card(card); //切换UHS-I mode
-
调用mmc_add_card()把 mmc_card 挂载到 mmc_bus_type 总线去 。
3.5.2.mmc_sd_get_cid
738 int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
739 {
740 int err;
741 u32 max_current;
742 int retries = 10;
743 u32 pocr = ocr;
744
745 try_again:
746 if (!retries) {
747 ocr &= ~SD_OCR_S18R;
748 pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
749 }
750
757 mmc_go_idle(host); //发送CMD0
758
765 err = mmc_send_if_cond(host, ocr); //发送CMD8,
766 if (!err)
767 ocr |= SD_OCR_CCS;
768
769 /*
770 * If the host supports one of UHS-I modes, request the card
771 * to switch to 1.8V signaling level. If the card has failed
772 * repeatedly to switch however, skip this.
773 */
774 if (retries && mmc_host_uhs(host))
775 ocr |= SD_OCR_S18R;
776
781 max_current = sd_get_host_max_current(host);
782 if (max_current > 150)
783 ocr |= SD_OCR_XPC;
784
785 err = mmc_send_app_op_cond(host, ocr, rocr); //发送ACMD41
786 if (err)
787 return err;
788
789 /*
790 * In case CCS and S18A in the response is set, start Signal Voltage
791 * Switch procedure. SPI mode doesn't support CMD11.
792 */
793 if (!mmc_host_is_spi(host) && rocr &&
794 ((*rocr & 0x41000000) == 0x41000000)) {
795 err = mmc_set_uhs_voltage(host, pocr); //发送CMD11切换,切换电压
796 if (err == -EAGAIN) {
797 retries--;
798 goto try_again;
799 } else if (err) {
800 retries = 0;
801 goto try_again;
802 }
803 }
804
805 err = mmc_send_cid(host, cid); /发送CMD2,获取cid
806 return err;
807 }
流程图如下所示:
重点分析如何切换1.8v电压:
mmc_sd_get_cid
->mmc_set_uhs_voltage
-->先close clock
-->mmc_set_signal_voltage
->start_signal_voltage_switch //驱动自定义,可参考sdhci_start_signal_voltage_switch
->再open clock
3.5.3.UFS-I mode
UHS-I provides up to 104MB/sec performance on 4-bit SD bus with the single end driver interface. Card form factor is the same and existing connector can be used.
3.5.2.1.UHS-I Card Operation Modes
3.5.2.2.UHS-I Card 初始化流程
3.5.2.3.代码分析
static int mmc_sd_init_uhs_card(struct mmc_card *card)
{
int err;
u8 *status;
//sd卡3.0版本才加入的UHS-I
if (!card->scr.sda_spec3)
return 0;
//判断是否支持class10命令,CMD6属于该类
if (!(card->csd.cmdclass & CCC_SWITCH))
return 0;
status = kmalloc(64, GFP_KERNEL);//CMD6的应答
if (!status) {
pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM;
}
/* Set 4-bit bus width */
if ((card->host->caps & MMC_CAP_4_BIT_DATA) && //host是否支持4位数据线宽度
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { //卡是否支持4位数据线宽度模式
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); //发送ACMD6切换
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); //host端的设置.
}
/*这个函数是找一个card和host都支持的速度,类似上面对数据线宽度的操作,期望找到一个host和card支持的最快的速度.*/
sd_update_bus_speed_mode(card);
/*driver strength在spec中没找到详细的说明,spec中流程图里有这个步骤.这个也是3.0才增加的,sd卡这边切换用的是CMD6, driver strength属于CMD6的function group 3*/
err = sd_select_driver_type(card, status);
/*这个函数里面注释比较清楚了,根据电压和速度模式,设置卡的最大功率,属于CMD6的function group 4*/
err = sd_set_current_limit(card, status);
/* Set bus speed mode of the card */
err = sd_set_bus_speed_mode(card, status);/*设置速度,属于CMD6的function group 1*/
/* SPI mode doesn't define CMD19 */
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) {
mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK);
mmc_host_clk_release(card->host);
}
out:
kfree(status);
return err;
}
4.SD卡的启动过程
根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,关注启动SD卡的卡识别模式。
- 1.发送指令CMD0使卡设备处于idle状态;
- 2.发送指令CMD8,如果卡设备有response,说明此卡为SD2.0以上;
- 3.发送指令CMD55+ACMD41,该指令是用来探测卡设备的工作电压是否符合host端的要求;在发送ACMD41这类指令之前需要先发送CMD55指令,在SDIO中ACMD41指令被CMD5替代。
- 4.发送指令CMD11转换工作电压到1.8V;
- 5.发送指令CMD2获取CIA;
- 6.发送指令CMD3获取RCA(relative card address)
5.mmc power sequence framework
add generic power sequence framework:
We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices. This power
sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
framework to handle this requirement. The generic code is supplied
some common helpers from host driver, and individual power sequence
driver handles kinds of power sequence for devices.
Since the MMC has already done the similar things, and this power
sequence handling can be generic, we use mmc power sequence code
as base to create this framework.
This patch set is based on Krzysztof Kozlowski's RFC patch set (v4.7-rc1)
[1], and making some changes which can let it be generic. After that,
we create a generic power sequence driver for USB devices which handles
below things, it includes all input signals for devices I can consider.
- Clock and its frequencies
- GPIO for reset and the duration time
- GPIO for enable
- Regulator for power
- drivers/mmc/core/pwrseq_emmc.c
- https://lwn.net/Articles/691877/
小结:
mmc_rescan(struct work_struct *work)
-> mmc_rescan_try_freq(host, max(freqs[i], host->f_min))
-> mmc_attach_sdio(host) /* 检测卡的类型 */
-> mmc_attach_sd(host)
-> mmc_attach_mmc(host)
-> mmc_send_op_cond(host, 0, &ocr); /* 发送卡的ID */
-> mmc_init_card(host, host->ocr, NULL); /* 初始化mmc_card */
-> card = mmc_alloc_card(host, &mmc_type);
-> device_initialize(&card->dev);
-> card->dev.bus = &mmc_bus_type; /* 设置总线为mmc_bus_type */
-> card->type = MMC_TYPE_MMC; /* 设置card结构体 */
-> mmc_release_host(host);
-> mmc_add_card(host->card); /* 添加卡mmc_card */
-> device_add(&card->dev);
-> mmc_claim_host(host); /* 使能host */
6.Debug
6.1.调试sd 卡插拔中断问题
1>.dts 添加如下:
cd-inverted;
cd-gpios = <&gpio 65 0x0>; //最后一位配置为0,还是1,需要根据实际情况设置。
2>.gpio65 作为sd 卡插拔检测引脚,原理图如下所示:
TF_CD连接到GPIO_65,使用1.8v 上拉,即默认如果不插卡,D7电压测试点为1.8v,当插入卡后,D7测试点为0v。软件需要配置GPIO_65为pull up,如下所示:
config_pull_up:config_pull_up {
bias-pull-up;
};
sd_cd: sd_cd {
xxx,pins = <gpio65 1 &config_pull_up>;
}
refer to
- http://www.codexiu.cn/linux/blog/7520/
- https://www.cnblogs.com/lihuidashen/p/6112169.html