[mmc subsystem] mmc core(第六章)——mmc core主模块

mmc subsystem系列(持续更新中):
[mmc subsystem] 概念与框架
[mmc subsystem] mmc core(第一章)——概述
[mmc subsystem] mmc core(第二章)——数据结构和宏定义说明
[mmc subsystem] mmc core(第三章)——bus模块说明
[mmc subsystem] mmc core(第四章)——host模块说明
[mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)
[mmc subsystem] mmc core(第六章)——mmc core主模块

建议先参考《[mmc subsystem] 概念与框架》和《[mmc subsystem] mmc core(第一章)——概述》对整体有一个了解。

=========================================================================================================

一、说明

1、mmc core概述

mmc core主模块是mmc core的实现核心。也是本章的重点内容。
对应代码位置drivers/mmc/core/core.c。
其主要负责如下功能:

  • mmc core初始化,包括注册mmc bus、mm host class等等
  • mmc host的管理和维护,包括为其他模块提供mmc_host的操作接口,如下
    • host的启动和停止
    • host的占用和释放
    • host电源状态的保存和恢复
    • host总线操作集的绑定和解绑
    • host上卡状态检测
  • 为其他模块提供mmc_card的操作接口,如下
    • card的唤醒和休眠
    • card擦除
    • card属性的获取
  • 为其他模块提供总线io setting的接口
  • 为其他模块提供mmc请求接口
  • card检测接口
  • bkops操作接口
  • regulator操作接口
  • clock操作接口
  • mmc core电源管理操作接口

2、操作集说明

在mmc_host中有两个操作集成员,需要理解一下,以免在代码中产生误会:

  • mmc_host->struct mmc_host_ops *ops,这个是host的操作集,由host controller驱动决定。对于sdhci类host来说,就是sdhci_ops(sdhci.c中设置)。
  • mmc_host->struct mmc_bus_ops *bus_ops,这个是mmc总线的操作集(也可以理解为host的mmc bus handler,host的总线处理方法),由总线上的card type决定。对于mmc card type来说,就是mmc_ops_unsafe或者mmc_ops(mmc_attach_bus_ops中设置)。

二、API总览

1、mmc core初始化相关

  • mmc_init & mmc_exit (模块内使用)

2、mmc host的管理和维护相关

  • mmc_claim_host & mmc_try_claim_host & mmc_release_host (模块内使用)
  • mmc_power_up & mmc_power_off
  • mmc_start_host & mmc_stop_host
  • mmc_power_save_host & mmc_power_restore_host
  • mmc_resume_host & mmc_suspend_host
  • mmc_pm_notify

3、mmc card的操作相关(包括card状态的获取)

  • mmc_hw_reset & mmc_hw_reset_check &
  • mmc_card_awake & mmc_card_sleep
  • mmc_card_is_prog_state
  • mmc_can_erase
  • mmc_can_trim
  • mmc_can_discard
  • mmc_can_sanitize
  • mmc_can_secure_erase_trim
  • mmc_erase_group_aligned

4、总线io setting相关

  • mmc_set_ios
  • mmc_set_chip_select
  • mmc_set_clock
  • mmc_set_bus_mode
  • mmc_set_bus_width
  • mmc_select_voltage
  • mmc_set_signal_voltage(特殊)
  • mmc_set_timing
  • mmc_set_driver_type

  • mmc_get_max_frequency & mmc_get_min_frequency

5、host的mmc总线相关

  • mmc_resume_bus
  • mmc_attach_bus & mmc_detach_bus

6、mmc请求相关

  • mmc_request_done
  • mmc_wait_for_req
  • mmc_wait_for_cmd
  • mmc_set_data_timeout
  • mmc_align_data_size

7、card检测相关

  • mmc_detect_change
  • mmc_rescan
  • mmc_detect_card_removed

8、bkops操作相关

  • mmc_blk_init_bkops_statistics
  • mmc_start_delayed_bkops
  • mmc_start_bkops & mmc_stop_bkops
  • mmc_start_idle_time_bkops
  • mmc_read_bkops_status

9、regulator操作相关

  • mmc_regulator_get_ocrmask
  • mmc_regulator_set_ocr
  • mmc_regulator_get_supply

10、card擦除操作相关

  • mmc_init_erase
  • mmc_erase

11、clock操作接口

  • mmc_init_clk_scaling & mmc_exit_clk_scaling
  • mmc_can_scale_clk
  • mmc_disable_clk_scaling

12、mmc core电源管理操作

  • mmc_rpm_hold & mmc_rpm_release

二、接口代码说明——mmc core初始化相关

1、mmc_init实现

负责初始化整个mmc core。

  • 主要工作:

    • 分配一个workqueue,用于专门处理mmc core的执行的工作
    • 注册mmc bus
    • 注册mmc host class
  • 代码如下:

static int __init mmc_init(void)
{
    int ret;

/* 分配一个workqueue,用于专门处理mmc core的执行的工作 */
    workqueue = alloc_ordered_workqueue("kmmcd", 0);

/* 注册mmc bus */
    ret = mmc_register_bus();    // 调用mmc_register_bus注册mmc bus,具体参考《mmc core——bus模块说明》
        // 会生成/sys/bus/mmc目录

/* 注册mmc host class */
    ret = mmc_register_host_class();    // 调用mmc_register_host_class注册mmc host class,具体参考《mmc core——host模块说明》
        // 会生成/sys/class/mmc_host目录

/* 注册sdio bus */
    ret = sdio_register_bus();

    return 0;

}

subsys_initcall(mmc_init);

三、接口代码说明——mmc host的管理和维护相关

1、mmc_claim_host & mmc_try_claim_host & mmc_release_host

host被使能之后就不能再次被使能,并且只能被某个进程独自占用。
可以简单地将host理解为一种资源,同时只能被一个进程获取,但是在占用进程里面可以重复占用。
在对host进行操作之前(包括发起mmc请求),必须使用mmc_claim_host和mmc_release_host来进行获取和释放。

  • 变量说明

    • mmc_host->claimed用来表示host是否被占用
    • mmc_host->claimer用来表示host的占用者(进程)
    • mmc_host->claim_cnt用来表示host的占用者的占用计数,为0时则会释放这个host
  • 代码如下

static inline void mmc_claim_host(struct mmc_host *host)
{
    __mmc_claim_host(host, NULL);·// 调用__mmc_claim_host来获取host
}

int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
/只考虑abort为NULL的情况,在mmc core中的mmc_claim_host也是将其设置为NULL
    DECLARE_WAITQUEUE(wait, current);    
    unsigned long flags;
    int stop;

    might_sleep();        // 说明这个函数可能导致进程休眠

    add_wait_queue(&host->wq, &wait);    // 把当前进程加入到等待队列中

    spin_lock_irqsave(&host->lock, flags);
    while (1) {    // 以下尝试获取host,如果host正在被占用,会进入休眠
        set_current_state(TASK_UNINTERRUPTIBLE);    // 设置进程状态为TASK_UNINTERRUPTIBLE状态
        stop = abort ? atomic_read(abort) : 0;
        if (stop || !host->claimed || host->claimer == current)    // 当host的占用标志claimed为0,或者占用者是当前进程的时候,说明可以占用了,退出
            break;
        spin_unlock_irqrestore(&host->lock, flags);
        schedule();    // 否则,进行调度进入休眠
        spin_lock_irqsave(&host->lock, flags);
    }
    set_current_state(TASK_RUNNING);    // 设置进程为运行状态
    if (!stop) {
        host->claimed = 1;    // 设置占用标志claimed 
        host->claimer = current;    // 设置占用者为当前进程
        host->claim_cnt += 1;    // 占用计数加1
    } else
        wake_up(&host->wq);
    spin_unlock_irqrestore(&host->lock, flags);
    remove_wait_queue(&host->wq, &wait);    // 将当前进程从等待队列中退出
    if (host->ops->enable && !stop && host->claim_cnt == 1)
        host->ops->enable(host);  // 调用host操作集中的enable方法来占用该host,对应sdhci类host即为sdhci_enable
    return stop;
}

void mmc_release_host(struct mmc_host *host)
{
    unsigned long flags;

    WARN_ON(!host->claimed);

    if (host->ops->disable && host->claim_cnt == 1)    // 当前claim_cnt为1(马上要变为0),调用释放host了
        host->ops->disable(host);    // 调用host操作集中的disable方法来释放该host,对应sdhci类host即为sdhci_disable

    spin_lock_irqsave(&host->lock, flags);
    if (--host->claim_cnt) {
        /* Release for nested claim */
        spin_unlock_irqrestore(&host->lock, flags);    // 如果减一之后计数还不为0,说明当前进程需要继续占用该host,不做其他操作
    } else {    // 以下需要释放该host
        host->claimed = 0;    // 设置占用标志claimed为0
        host->claimer = NULL;    // 清空占用者(进程)
        spin_unlock_irqrestore(&host->lock, flags);
        wake_up(&host->wq);    // 唤醒host的等待队列,让那些调用mmc_claim_host睡眠等待host资源的进程被唤醒
    }
}

int mmc_try_claim_host(struct mmc_host *host) 
{
// 和mmc_claim_host的主要区别在于进程不会休眠,获取失败直接返回
    int claimed_host = 0;
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);
    if (!host->claimed || host->claimer == current) {
        host->claimed = 1;
        host->claimer = current;
        host->claim_cnt += 1;
        claimed_host = 1;
    }
    spin_unlock_irqrestore(&host->lock, flags);
    if (host->ops->enable && claimed_host && host->claim_cnt == 1)
        host->ops->enable(host);
    return claimed_host;
}

会调用mmc_host->struct mmc_host_ops->enable和mmc_host->struct mmc_host_ops->disable来使能和禁用host。
对于sdhci类的host,相应就是sdhci_enable和sdhci_disable。

2、mmc_power_up & mmc_power_off

mmc host的上电操作和关电操作。

  • mmc的power状态

    • MMC_POWER_OFF:掉电状态
    • MMC_POWER_UP:正在上电的状态
    • MMC_POWER_ON:供电正常的状态
  • 主要工作
    主要工作就是初始化host的总线设置、总线时钟以及工作电压、信号电压。

  • 代码如下

void mmc_power_up(struct mmc_host *host)
{
    int bit;

/* 判断是否已经处于MMC_POWER_ON,是的话不进行后续操作 */
    if (host->ios.power_mode == MMC_POWER_ON)
        return;

/* 第一阶段,先设置对应的io setting使host处于MMC_POWER_UP的状态(总线工作频率没有设置) */
    mmc_host_clk_hold(host);    // 先获取host时钟

    /* If ocr is set, we use it */
    if (host->ocr)
        bit = ffs(host->ocr) - 1;    // 选择一个ocr配置设置为host的工作电压
    else
        bit = fls(host->ocr_avail) - 1;
    host->ios.vdd = bit;
    if (mmc_host_is_spi(host))
        host->ios.chip_select = MMC_CS_HIGH;
    else {
        host->ios.chip_select = MMC_CS_DONTCARE;
        host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;    // 设置总线模式
    }
    host->ios.power_mode = MMC_POWER_UP;
    host->ios.bus_width = MMC_BUS_WIDTH_1;    // 设置总线宽度为1
    host->ios.timing = MMC_TIMING_LEGACY;    // 串口时序
    mmc_set_ios(host);    // 调用mmc_set_ios设置总线的io setting,后面会说明

    /*
     * This delay should be sufficient to allow the power supply
     * to reach the minimum voltage.
     */
    mmc_delay(10);

/* 第二阶段,以host的初始化工作频率再次设置io setting,使host处于MMC_POWER_ON状态 */
    host->ios.clock = host->f_init;    // 设置总线的时钟频率
    host->ios.power_mode = MMC_POWER_ON;
    mmc_set_ios(host);   // 调用mmc_set_ios设置总线的io setting,后面会说明

    /*
     * This delay must be at least 74 clock sizes, or 1 ms, or the
     * time required to reach a stable voltage.
     */
    mmc_delay(10);

/* 设置信号的电压 */
    /* Set signal voltage to 3.3V */
    __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);

    mmc_host_clk_release(host);    // 释放host时钟
}

3、mmc_start_host & mmc_stop_host

mmc_start_host 用来启动一个host,mmc_stop_host用来停止一个host。
当底层host controller调用mmc_add_host来注册host时,在mmc_add_host中就会调用mmc_start_host来启动一个host了。具体参考《mmc core——host模块说明》。
相对应的,会在mmc_remove_host中调用mmc_stop_host停止host。

void mmc_start_host(struct mmc_host *host)
{
    mmc_claim_host(host);    // 因为上电操作涉及到对host的使用和设置,需要先占用host

    host->f_init = max(freqs[0], host->f_min);    // 通过最小频率要设置初始化频率
    host->rescan_disable = 0;    // 设置rescan_disable标志为0,说明已经可以进行card检测了
    if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)    // 如果mmc属性设置了MMC_CAP2_NO_PRESCAN_POWERUP,也就是在rescan前不需要进行power up操作时,则进行关电
        mmc_power_off(host);
    else
        mmc_power_up(host);    // 否则,调用mmc_power_up对host进行上电操作。这里也是mmc core中启动host的核心函数。

    mmc_release_host(host);    // 完成上电操作,释放host

/* 到这里host已经可以工作了,可以开始进行后续的card操作了 */
    mmc_detect_change(host, 0);    // 调用mmc_detect_change检测card变化,后续会继续说明
}

四、接口代码说明——card检测相关

1、mmc_detect_change

在上述中我们知道在启动host的函数mmc_start_host 中最后调用了mmc_detect_change来开始检测card(也就是检测mmc卡槽的状态变化情况)。
其实mmc_detect_change是在driver发现mmc卡槽状态发生变化时,调用mmc_detect_change来进行确认和处理。

void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
    unsigned long flags;
    spin_lock_irqsave(&host->lock, flags);
    WARN_ON(host->removed);
    spin_unlock_irqrestore(&host->lock, flags);
#endif
    host->detect_change = 1;    // 检测到card状态发生变化的标识

    mmc_schedule_delayed_work(&host->detect, delay);    // 间隔delay jiffies之后调用host->detect的工作
}

在《host模块说明》已经知道了在mmc_alloc_host中默认将host->detect工作设置为mmc_rescan(card重新扫描)函数,
INIT_DELAYED_WORK(&host->detect, mmc_rescan)。
当然,host也可以自己另外设置,但是一般都是使用mmc core提供的mmc_rescan作为detect工作来搜索card。下面说明。

2、mmc_rescan

用于检测host的卡槽状态,并对状态变化做相应的操作。
有card插入时,重新扫描mmc card。

void mmc_rescan(struct work_struct *work)
{
    struct mmc_host *host =
        container_of(work, struct mmc_host, detect.work);
    bool extend_wakelock = false;

/* 如果rescan_disable被设置,说明host此时还禁止rescan */
    if (host->rescan_disable)
        return;

/* 对于设备不可移除的host来说,只能rescan一次 */
    /* If there is a non-removable card registered, only scan once */
    if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
        return;
    host->rescan_entered = 1;

    mmc_bus_get(host);    // 获取host对应的bus
    mmc_rpm_hold(host, &host->class_dev);    // 使host处于rpm resume的状态

/* 以下判断原来的card是否已经被移除,移除了则需要做相应的操作 */
    /*
     * if there is a _removable_ card registered, check whether it is
     * still present
     */
    if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
        && !(host->caps & MMC_CAP_NONREMOVABLE))
        host->bus_ops->detect(host);    
        // host->bus_ops存在的话说明之前是有card插入的状态
        // 需要调用host->bus_ops->detect检测card是否被移除,是的话在host->bus_ops->detect中做相应的处理
        // 对于mmc type card来说,对应就是mmc_detect,具体参考《card相关模块》

    host->detect_change = 0;
    /* If the card was removed the bus will be marked
     * as dead - extend the wakelock so userspace
     * can respond */
    if (host->bus_dead)
        extend_wakelock = 1;    // 需要设置一个wakelock锁,使用户空间可以及时做出相应

    /*
     * Let mmc_bus_put() free the bus/bus_ops if we've found that
     * the card is no longer present.
     */
    mmc_bus_put(host);    
       // 因为在这个函数的前面已经获取了一次host,可能导致host->bus_ops->detect中检测到card拔出之后,没有真正释放到host的bus,所以这里先put一次
        // host bus的计数(bus_refs)为0的时候,会调用__mmc_release_bus清空host bus的信息
    mmc_bus_get(host);
        // 再获取host bus

    /* if there still is a card present, stop here */
    if (host->bus_ops != NULL) {    // 说明此时还有card插入,退出后续的操作
        mmc_rpm_release(host, &host->class_dev);
        mmc_bus_put(host);
        goto out;
    }

    mmc_rpm_release(host, &host->class_dev);    

    /*
     * Only we can add a new handler, so it's safe to
     * release the lock here.
     */
    mmc_bus_put(host);

/* 检测当前卡槽状态,根据卡槽状态做相应的操作 */
    if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
        // 调用host->ops->get_cd来判断host的卡槽的当前card插入状态
        // 对应sdhci类型的host来说,就是sdhci_get_cd
        // 为0的时候,表示没有card插入,对应host进行power off操作之后进行退出
        // 为1的时候,表示当前有card插入,跳到后续的操作
        mmc_claim_host(host);
        mmc_power_off(host);
        mmc_release_host(host);
        goto out;
    }

    mmc_rpm_hold(host, &host->class_dev);
    mmc_claim_host(host);
    if (!mmc_rescan_try_freq(host, host->f_min))    // 调用mmc_rescan_try_freq,以支持的最低频率作为工作频率尝试搜索card,后续继续说明
        extend_wakelock = true;
    mmc_release_host(host);
    mmc_rpm_release(host, &host->class_dev);
 out:
    /* only extend the wakelock, if suspend has not started yet */
    if (extend_wakelock && !host->rescan_disable)    
        wake_lock_timeout(&host->detect_wake_lock, HZ / 2);    // 占用wakelock,使系统在HZ/2的时间内不会休眠

    if (host->caps & MMC_CAP_NEEDS_POLL)
        mmc_schedule_delayed_work(&host->detect, HZ);    
               // 当host设置了MMC_CAP_NEEDS_POLL属性时,需要每隔HZ的时间轮询检测host的卡槽状态,
                // 调度了host->detect工作,对应就是mmc_rescan
}

会调用host->ops->get_cd来判断host的卡槽的当前card插入状态,对应sdhci类型的host就是sdhci_get_cd。
会调用host->bus_ops->detect检测card是否被移除,是的话在host->bus_ops->detect中做相应的处理。对于mmc type card,就是mmc_detect。

3、mmc_rescan_try_freq

以一定频率搜索host bus上的card。

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
    host->f_init = freq;

    mmc_power_up(host);    // 给host做上电操作

    mmc_hw_reset_for_init(host);    // 硬件复位和初始化
    mmc_go_idle(host);

    mmc_send_if_cond(host, host->ocr_avail);    // 获取card的可用频率,存储到host->ocr_avail中

    /* Order's important: probe SDIO, then SD, then MMC */
/* 用于绑定card到host bus上(也就是card和host的绑定)。 */
    if (!mmc_attach_sdio(host))    // 先假设card是sdio type card,尝试绑定到host bus上,失败则说明不是sdio type card,继续后面的操作,否则返回
        return 0;
    if (!mmc_attach_sd(host))  // 先假设card是sd type card,尝试绑定到host bus上,失败则说明不是sd type card,继续后面的操作,否则返回
        return 0;
    if (!mmc_attach_mmc(host))  // 先假设card是mmc type card,尝试绑定到host bus上,失败则说明不是mmc type card,继续后面的操作,否则返回
        // mmc_attach_mmc通过mmc_host获取mmc type card信息,初始化mmc_card,并进行部分驱动,最后将其注册到mmc_bus上。
        // 具体参考《card相关模块说明》
        return 0;

    mmc_power_off(host);
    return -EIO;
}

五、接口代码说明——总线io setting相关

0、mmc_ios说明

struct mmc_ios 由mmc core定义的规范的结构,用来维护mmc总线相关的一些io setting。如下:

struct mmc_ios {
    unsigned int    clock;          /* clock rate */ // 当前工作频率
    unsigned int    old_rate;       /* saved clock rate */    // 上一次的工作频率
    unsigned long   clk_ts;         /* time stamp of last updated clock */    // 上一次更新工作频率的时间戳
    unsigned short  vdd;/* vdd stores the bit number of the selected voltage range from below. */   // 支持的电压表
    unsigned char   bus_mode;       /* command output mode */    // 总线输出模式,包括开漏模式和上拉模式
    unsigned char   chip_select;        /* SPI chip select */    // spi片选
    unsigned char   power_mode;     /* power supply mode */    // 电源状态模式
    unsigned char   bus_width;      /* data bus width */    // 总线宽度
    unsigned char   timing;         /* timing specification used */    // 时序类型
    unsigned char   signal_voltage;     /* signalling voltage (1.8V or 3.3V) */    // 信号的工作电压
    unsigned char   drv_type;       /* driver type (A, B, C, D) */    // 驱动类型
};

在设置总线io setting的过程中,就是要设置mmc_host->mmc_ios中的这些成员。
然后通过调用mmc_set_ios进行统一设置。后续会继续说明。

1、mmc_set_ios

统一设置mmc总线的io设置(io setting)。

void mmc_set_ios(struct mmc_host *host)
{
    struct mmc_ios *ios = &host->ios;

    if (ios->clock > 0)
        mmc_set_ungated(host);    // 关闭clock的门控
    host->ops->set_ios(host, ios);    // 调用host->ops->set_ios来对mmc总线的io setting进行设置,核心函数
        // 对于sdhci类型的host,对应就是sdhci_set_ios
}

会调用host->ops->set_ios来对mmc总线的io setting进行设置,核心函数。对于sdhci类型的host,对应就是sdhci_set_ios

2、mmc_set_bus_mode & mmc_set_bus_width

  • mmc_set_bus_mode用于设置总线模式,有如下模式
    • MMC_BUSMODE_OPENDRAIN(开漏模式)
    • MMC_BUSMODE_PUSHPULL(上拉模式)
  • mmc_set_bus_width用于设置总线宽度,有如下模式
    • MMC_BUS_WIDTH_1
    • MMC_BUS_WIDTH_4
    • MMC_BUS_WIDTH_8

代码如下:

void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
    mmc_host_clk_hold(host);
    host->ios.bus_mode = mode;
    mmc_set_ios(host);
    mmc_host_clk_release(host);
}

void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
    mmc_host_clk_hold(host);
    host->ios.bus_width = width;
    mmc_set_ios(host);
    mmc_host_clk_release(host);
}

其他设置io setting的函数类似,不多说明。

六、接口代码说明——host的mmc总线相关

1、mmc_attach_bus & mmc_detach_bus

  • 主要功能

    • mmc_attach_bus用于将分配一个mmc总线操作集给host。
    • mmc_detach_bus用于释放和host相关联的mmc总线操作集。
  • 一些变量

    • mmc_host->bus_ops,表示host的mmc总线操作集
    • mmc_host->bus_refs,表示host的mmc总线的使用者计数
    • mmc_host->bus_dead,表示host的mmc总线是否被激活,如果设置了bus_ops,那么就会被激活了
  • 代码如下

void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);

    BUG_ON(host->bus_ops);    // 不允许重复设置host的mmc总线操作集
    BUG_ON(host->bus_refs);    // 当mmc总线的使用者计数还存在时,不允许设置host的mmc总线操作集

    host->bus_ops = ops;    // 设置host的mmc总线操作集
    host->bus_refs = 1;    // host的mmc总线的使用者计数设置为1,相当于调用了mmc_bus_get
    host->bus_dead = 0;    // 总线被激活了

    spin_unlock_irqrestore(&host->lock, flags);
}

void mmc_detach_bus(struct mmc_host *host)
{
    unsigned long flags;
    spin_lock_irqsave(&host->lock, flags);
    host->bus_dead = 1;    // host的mmc总线设置为dead状态
    spin_unlock_irqrestore(&host->lock, flags);
    mmc_bus_put(host);    // 调用mmc_bus_put释放host的mmc总线,也就是对host的mmc总线的使用者计数-1
}

在《card相关模块》中可以看到mmc_attach_mmc->mmc_attach_bus_ops调用mmc_attach_bus来绑定了host的mmc总线操作集为mmc_ops_unsafe或者mmc_ops

2、mmc_bus_get & mmc_bus_put

static inline void mmc_bus_get(struct mmc_host *host)
{
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);
    host->bus_refs++;    // 对host的mmc总线的使用者计数+1
    spin_unlock_irqrestore(&host->lock, flags);
}

static inline void mmc_bus_put(struct mmc_host *host)
{
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);
    host->bus_refs--;    // 对host的mmc总线的使用者计数-1
    if ((host->bus_refs == 0) && host->bus_ops)    // 说明host的mmc总线当前并没有使用,调用__mmc_release_bus进行实际的释放操作
        __mmc_release_bus(host);
    spin_unlock_irqrestore(&host->lock, flags);
}

static void __mmc_release_bus(struct mmc_host *host)
{
    host->bus_ops = NULL; // 清空host的mmc总线操作集
}

七、接口代码说明——mmc请求相关

分成同步的mmc请求和异步的mmc请求。差别如下:

1、流程上的差别:
(1)会阻塞的处理流程:
mmc_wait_for_req
——》__mmc_start_req // 发起请求
————》init_completion(&mrq->completion);  
————》mrq->done = mmc_wait_done
————》mmc_start_request(host, mrq);   // 实际发起请求的操作
——》mmc_wait_for_req_done   // 阻塞等待请求处理完成
——》返回

(2)不阻塞等待该命令的处理流程:
(注意:并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞)
mmc_start_req
——》mmc_wait_for_data_req_done   // 阻塞等待上一次的请求处理
——》__mmc_start_data_req   // 发起异步请求
————》mrq->done = mmc_wait_data_done
————》mmc_start_request   // 实际发起请求的操作
——》返回

最后都是调用了mmc_start_request使host向MMC发起请求。

0、数据结构说明

一个mmc请求分成两部分内容,分别是命令部分和数据部分。

  • mmc_command
struct mmc_command {
    u32            opcode;    // 命令的操作码,如MMC_GO_IDLE_STATE、MMC_SEND_OP_COND等等
    u32            arg;    // 命令的参数
    u32            resp[4];    // response值
    unsigned int        flags;        /* expected response type */    // 期待的response的类型
#define mmc_resp_type(cmd)    ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))

/*
* These are the command types.
*/
#define mmc_cmd_type(cmd)    ((cmd)->flags & MMC_CMD_MASK)

    unsigned int        retries;    /* max number of retries */    // 失败时的重复尝试次数
    unsigned int        error;        /* command error */    // 命令的错误码

/*
* Standard errno values are used for errors, but some have specific
* meaning in the MMC layer:
*
 * ETIMEDOUT    Card took too long to respond
 * EILSEQ       Basic format problem with the received or sent data
 *              (e.g. CRC check failed, incorrect opcode in response
 *              or bad end bit)
 * EINVAL       Request cannot be performed because of restrictions
 *              in hardware and/or the driver
 * ENOMEDIUM    Host can determine that the slot is empty and is
 *              actively failing requests
*/

    unsigned int        cmd_timeout_ms;    /* in milliseconds */    // 命令执行的等待超时事件

    struct mmc_data        *data;        /* data segment associated with cmd */    // 和该命令关联在一起的数据段
    struct mmc_request    *mrq;        /* associated request */    // 该命令关联到哪个request
};
  • mmc_data
struct mmc_data {
    unsigned int        timeout_ns; /* data timeout (in ns, max 80ms) */   // 超时时间,以ns为单位
    unsigned int        timeout_clks;   /* data timeout (in clocks) */   // 超时时间,以clock为单位
    unsigned int        blksz;      /* data block size */   // 块大小
    unsigned int        blocks;     /* number of blocks */   // 块数量
    unsigned int        error;      /* data error */   // 传输的错误码
    unsigned int        flags;   // 传输标识

#define MMC_DATA_WRITE  (1 << 8)
#define MMC_DATA_READ   (1 << 9)
#define MMC_DATA_STREAM (1 << 10)

    unsigned int        bytes_xfered;

    struct mmc_command  *stop;      /* stop command */   // 结束传输的命令
    struct mmc_request  *mrq;       /* associated request */   // 该命令关联到哪个request

    unsigned int        sg_len;     /* size of scatter list */
    struct scatterlist  *sg;        /* I/O scatter list */
    s32         host_cookie;    /* host private data */
    bool            fault_injected; /* fault injected */
};
  • mmc_request
    struct mmc_request是mmc core向host controller发起命令请求的处理单位。
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;    // 所属host
};
  • mmc_async_req
struct mmc_async_req {
    /* active mmc request */
    struct mmc_request  *mrq;
    unsigned int cmd_flags; /* copied from struct request */

    /*
     * Check error status of completed mmc request.
     * Returns 0 if success otherwise non zero.
     */
    int (*err_check) (struct mmc_card *, struct mmc_async_req *);
    /* Reinserts request back to the block layer */
    void (*reinsert_req) (struct mmc_async_req *);
    /* update what part of request is not done (packed_fail_idx) */
    int (*update_interrupted_req) (struct mmc_card *,
            struct mmc_async_req *);
};

1、mmc_wait_for_req

发起mmc_request请求并且等待其处理完成。由其他需要发起mmc请求的模块调用。
可以结合后面的mmc_request_done来看。

void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
    if (mmc_bus_needs_resume(host))
        mmc_resume_bus(host);
#endif
    __mmc_start_req(host, mrq);    // 开始发起mmc_request请求
    mmc_wait_for_req_done(host, mrq);    // 等待mmc_request处理完成
}

//-----------------------------------__mmc_start_req说明,开始发起mmc_request请求
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
/* 发起mmc_request前的一些初始化工作,包括完成量和处理完成的回调函数的设置 */
    init_completion(&mrq->completion);    // 初始化完成量,在mmc_wait_for_req_done中会去等待这个完成量
    mrq->done = mmc_wait_done; 
        // 设置mmc_request处理完成的回调函数,会调用complete(&mrq->completion);来设置完成量
        // host controller会调用mmc_request_done来执行这个回调函数,具体在后面分析
    if (mmc_card_removed(host->card)) {    // 检测card是否存在
        mrq->cmd->error = -ENOMEDIUM;
        complete(&mrq->completion);
        return -ENOMEDIUM;
    }

/* 调用mmc_start_request发起mmc请求 */
    mmc_start_request(host, mrq);    // 开始处理mmc_request请求
    return 0;
}

static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{

    WARN_ON(!host->claimed);

/* 以下对mmc_request的各个成员,包括cmd、data、stop做验证操作和关联操作 */
    mrq->cmd->error = 0;
    mrq->cmd->mrq = mrq;
    if (mrq->data) {
        BUG_ON(mrq->data->blksz > host->max_blk_size);
        BUG_ON(mrq->data->blocks > host->max_blk_count);
        BUG_ON(mrq->data->blocks * mrq->data->blksz >
            host->max_req_size);
        mrq->cmd->data = mrq->data;      // 也就是说mmc_request的data和其cmd中的data是一一样的
        mrq->data->error = 0;
        mrq->data->mrq = mrq;
        if (mrq->stop) {
            mrq->data->stop = mrq->stop;
            mrq->stop->error = 0;
            mrq->stop->mrq = mrq;
        }
#ifdef CONFIG_MMC_PERF_PROFILING
        if (host->perf_enable)
            host->perf.start = ktime_get();
#endif
    }

/* 获取时钟 */
    mmc_host_clk_hold(host);

/* 调用host controller的request方法来处理mmc_request请求 */
    host->ops->request(host, mrq);    
       // host->ops->request也就是host controller的request方法,对于sdhci类型的host来说,就是sdhci_request
}

//-----------------------------------mmc_wait_for_req_done说明,等待mmc_request处理完成
static void mmc_wait_for_req_done(struct mmc_host *host,
                  struct mmc_request *mrq)
{
    struct mmc_command *cmd;

    while (1) {
        wait_for_completion_io(&mrq->completion);   // 在这里休眠,等待mrq->completion完成量,在__mmc_start_req中初始化的

        cmd = mrq->cmd;   // 获取对应的command

        /*
         * If host has timed out waiting for the commands which can be
         * HPIed then let the caller handle the timeout error as it may
         * want to send the HPI command to bring the card out of
         * programming state.
         */
        if (cmd->ignore_timeout && cmd->error == -ETIMEDOUT)
            break;

        if (!cmd->error || !cmd->retries || mmc_card_removed(host->card))
                // 如果command正常处理完成,或者失败重复尝试次数为0,或者card被移除了,直接退出循环返回
            break;

                // 以下处理失败重复尝试的情况
        pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
             mmc_hostname(host), cmd->opcode, cmd->error);
        cmd->retries--;
        cmd->error = 0;
        host->ops->request(host, mrq);
    }
}

会调用host->ops->request来对mmc_request进行处理,对于sdhci类型的host,对应就是sdhci_request。
这个方法就是mmc_request实际被处理的核心。

2、mmc_request_done

通知mmc core某个mmc_request已经处理完成,由host controller调用。
以sdhci类型的host为例,处理完一个mmc_request之后,会执行sdhci_tasklet_finish,而在sdhci_tasklet_finish中会调用mmc_request_done来通知host某个mmc_request已经处理完成了。

void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
    struct mmc_command *cmd = mrq->cmd;
    int err = cmd->error;

    if (host->card)
        mmc_update_clk_scaling(host);

    if (err && cmd->retries && !mmc_card_removed(host->card)) {
                // command执行出错,如果还需要重复尝试的话,这里不释放clock,只是通知mmc core
        if (mrq->done)
            mrq->done(mrq);   
               // 执行mmc_request的回调函数来通知mmc core,
               // 对于__mmc_start_req发起的request来说,就是mmc_wait_done,上面已经说明过了
               // 对于__mmc_start_data_req发起的request来说,就是mmc_wait_data_done,后面会说明
    } else {
        mmc_should_fail_request(host, mrq);   
                // 用于模拟data传输概率出错的情况
                // 具体参考http://blog.csdn.net/luckywang1103/article/details/52224160

        if (mrq->done)
            mrq->done(mrq);
                // 执行mmc_request的回调函数来通知mmc core,对于__mmc_start_req发起的request来说,就是mmc_wait_done,上面已经说明过了

        mmc_host_clk_release(host);
    }
}

通过上述,mrq->done被调度,mmc_wait_done被执行,mrq->completion被设置。
然后等待mrq->completion的mmc_wait_for_req_done就会继续往下执行。

3、mmc_wait_for_cmd

mmc_wait_for_cmd用于处理一个不带数据请求的命令。
会被封装到mmc_request中,通过调用mmc_wait_for_req来发起请求。

int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
    struct mmc_request mrq = {NULL};

    WARN_ON(!host->claimed);
    memset(cmd->resp, 0, sizeof(cmd->resp));   // 清空command的response
    cmd->retries = retries;   // 失败时的重复尝试次数
    mrq.cmd = cmd;   // 封装到mmc_request中
    cmd->data = NULL;   // 不带数据包的命令,故清空data
    mmc_wait_for_req(host, &mrq);   // 调用mmc_wait_for_req发起mmc请求并且等待其处理完成

    return cmd->error;   // 返回错误码
}

4、mmc_start_req(重要)

机制说明如下:mmc_start_req会先判断上一次的asycn_req是否处理完成,如果没有处理完成,则会等待其处理完成。
如果处理完成了,为当前要处理的asycn_req发起请求,但是并不会等待,而是直接返回。
注意:并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞。这样,可以利用等待的一部分时间来做其他操作。
为了方便理解这个函数,需要看一下其函数注释。
* 要注意,在函数里面有两个异步请求:
- areq:表示新的异步请求
- host->areq:表示上一次发起的、正在处理、等待完成的异步请求

  • 代码如下(为了方便理解,对代码进行了简化):
/**
 *  mmc_start_req - start a non-blocking request    // 该函数用来发起一个不阻塞的请求
 *  @host: MMC host to start command    // 要发起对应请求的host
 *  @areq: async request to start    // 要发起的异步请求
 *  @error: out parameter returns 0 for success, otherwise non zero    // 返回值,返回0表示成功,返回非零表示失败
 *
 *  Start a new MMC custom command request for a host.    // 为host发起的一个新的mmc命令请求
 *  If there is on ongoing async request wait for completion    // 如果host已经有一个正在处理、等待完成的异步请求,那么会等待这个请求完成!!!
 *  of that request and start the new one and return.    // 然后发起新的请求,然后返回!!!
 *  Does not wait for the new request to complete.    // 并不会等待这个新的请求完成!!!
 *
 *      Returns the completed request, NULL in case of none completed.    // 会返回被完成的mmc请求(而不是新的mmc请求。)空表示没有mmc请求被完成。
 *  Wait for the an ongoing request (previoulsy started) to complete and
 *  return the completed request. If there is no ongoing request, NULL
 *  is returned without waiting. NULL is not an error condition.
// 等待上一次发起的mmc请求完成,然后把这个mmc请求返回。如果没有mmc请求正在处理,那么就直接返回而不会等待。空并不是错误条件。
 */
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
                    struct mmc_async_req *areq, int *error)
{
    int err = 0;
    int start_err = 0;
    struct mmc_async_req *data = host->areq;
    unsigned long flags;
    bool is_urgent;

    /* Prepare a new request */
/* 为新的异步请求做准备处理 */
    if (areq) {
        /*
         * start waiting here for possible interrupt
         * because mmc_pre_req() taking long time
         */
        mmc_pre_req(host, areq->mrq, !host->areq);
    }

/* 对上一次发起的、正在处理、等待完成的异步请求进行处理、等待操作 */
    if (host->areq) {
        err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);   // 在这里等待正在处理的异步请求处理完成
        //.......以下过滤了错误处理的部分
    }

/* 对新的异步请求进行发起操作 */
    if (!err && areq) {
        /* urgent notification may come again */
        spin_lock_irqsave(&host->context_info.lock, flags);
        is_urgent = host->context_info.is_urgent;
        host->context_info.is_urgent = false;
        spin_unlock_irqrestore(&host->context_info.lock, flags);
        if (!is_urgent || (areq->cmd_flags & REQ_URGENT)) {
            start_err = __mmc_start_data_req(host, areq->mrq);    // 调用__mmc_start_data_req发起新的异步请求
        } else {
            /* previous request was done */
            err = MMC_BLK_URGENT_DONE;
            if (host->areq) {
                mmc_post_req(host, host->areq->mrq, 0);
                host->areq = NULL;
            }
            areq->reinsert_req(areq);
            mmc_post_req(host, areq->mrq, 0);
            goto exit;
        }
    }

    if (host->areq)
        mmc_post_req(host, host->areq->mrq, 0);

     /* Cancel a prepared request if it was not started. */
    if ((err || start_err) && areq)
        mmc_post_req(host, areq->mrq, -EINVAL);

    if (err)
        host->areq = NULL;
    else
        host->areq = areq;

exit:
    if (error)
        *error = err;
    return data;    // 反正上一次正常处理的异步请求
}

//-----------------------------------------------------------------------------------------------------------------------------
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
    mrq->done = mmc_wait_data_done;
        // 设置mmc_request处理完成的回调函数,会唤醒正在等待请求被完成的进程,后面说明
        // host controller会调用mmc_request_done来执行这个回调函数,具体前面分析过了

    mrq->host = host;
    mmc_start_request(host, mrq);    // 开始处理mmc_request请求,前面已经说明过了

    return 0;
}

static void mmc_wait_data_done(struct mmc_request *mrq)
{
    unsigned long flags;
    struct mmc_context_info *context_info = &mrq->host->context_info;

    spin_lock_irqsave(&context_info->lock, flags);
    mrq->host->context_info.is_done_rcv = true;    // 设置is_done_rcv标识
    wake_up_interruptible(&mrq->host->context_info.wait);    // 唤醒context_info上的等待进程
    spin_unlock_irqrestore(&context_info->lock, flags);
}

//-----------------------------------------------------------------------------------------------------------------------------
static int mmc_wait_for_data_req_done(struct mmc_host *host,
                      struct mmc_request *mrq,
                      struct mmc_async_req *next_req)
{
// struct mmc_request *mrq:表示正在等待完成的请求
// struct mmc_async_req *next_req:表示下一次要执行的异步请求
    struct mmc_command *cmd;
    struct mmc_context_info *context_info = &host->context_info;
    bool pending_is_urgent = false;
    bool is_urgent = false;
    bool is_done_rcv = false;
    int err, ret;
    unsigned long flags;

    while (1) {
/* 在这里等待正在进行的请求完成,会在mmc_wait_data_done中被唤醒 */
/* 有几种情况会唤醒等待进程 */
        ret = wait_io_event_interruptible(context_info->wait,(context_info->is_done_rcv || context_info->is_new_req  || context_info->is_urgent));
        spin_lock_irqsave(&context_info->lock, flags);
        is_urgent = context_info->is_urgent;
        is_done_rcv = context_info->is_done_rcv;
        context_info->is_waiting_last_req = false;
        spin_unlock_irqrestore(&context_info->lock, flags);

/* 对请求处理完成的处理 */
        if (is_done_rcv) {
            context_info->is_done_rcv = false;
            context_info->is_new_req = false;
            cmd = mrq->cmd;


            if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) {
/* 请求正常处理完成,或者失败但是不需要重复尝试的情况的处理 */
                err = host->areq->err_check(host->card, host->areq);
                //.......
                break; /* return err */
            } else {
/* 对请求处理出错并且需要重复尝试的情况的处理 */
                //.......
            }
        }
    }
    return err;
}

你可能感兴趣的:(mmc)