linux关机时emmc驱动处理流程
2016-08-05 09:53
363 查看
关于android的重启,推荐一篇博文:x86平台下Android系统的Linux部分的重启分析。
由于我的工作与sd/emmc存储相关,所以在接下来的分析重启的过程中,如果有涉及到该部分的流程,或者我认为有可能但是还没确认的流程,我都会贴上来;如果与其无关,我会略过,敬请谅解;
一、kernel_restart
废话不说,从reboot函数的kernel_restart函数开始,代码如下:
第1--8行注释说明:
该函数用来重启系统;
参数cmd:一个指向重启命令缓冲区的指针;
关闭一切并开启一次干净利落的重启;
这个对于正在执行的中断来说是不安全的;
第11行:做内核重启准备,上代码:
二、kernel_restart_prepare
将自己的处理函数通过参数nb注册进来,所以我们在这里可以全局搜索(source insight工具)register_reboot_notifier这个函数;
搜出来的结果没有发现跟存储相关,所以这里不详细分析。
回到本节分析的函数kernel_restart_prepare:
第4行:设置系统状态;
第5行:取消用户帮助;
第6行:关闭设备,该函数在\drivers\base\core.c文件中,这里必须展开研究:
三、device_shutdown
注释:调用设备的shutdown函数去关闭设备;
第8--35行:设置访问设备列表的同步锁,并获取一个设备和对应的父设备,然后解锁,为什么要这么做可以看注释;这些操作与51--58行做的操作相反,等下不再介绍。
第38--39行:涉及电源管理,具体做什么暂时不清楚,对我们分析应该没有影响;
第41--49行:本函数的核心部分;如果该设备注册了总线,并实现了总线关闭函数则执行该函数;否则如果注册了驱动,且驱动关闭函数的shutdown函数指针不为空则执行该函数;
下面我们将分析跟存储sd/emmc相关的1@dev->bus->shutdown。
四、mmc_alloc_host
在emmc/sd host层解析一文中,我们有分析过mmci_probe函数,该函数的第30行,是这样子的:
这里是建立了一个延时工作队列,该工作队列的处理函数为mmc_rescan,该函数是用来扫描设备的,在这里不详细分析,我们的目的是要找出设备的shutdown函数。
在mmc_rescan函数中,有这么一段:
五、mmc_attach_mmc
第20行,关联设备到总线操作集,具体实现如下。
六、mmc_attach_bus_ops
通过该函数可以看出,如果是非拔插设备,将设备挂到mmc_ops_unsafe总线操作,如果是拔插设备就挂到mmc_ops。
我的理解是,如果支持拔插,则认为拔插是安全的,如果不支持拔插则拔插是不安全的。
下面为可插拔和不可插拔的总线处理集接口:
回到本节首部的mmc_attach_bus_ops函数,末尾有个函数mmc_attach_bus:
通过该函数我们知道host->bus_ops指向了mmc_ops_unsafe或者mmc_ops中的一个;先记住等下要用:2@host->bus_ops
回到第五节的mmc_attach_mmc函数的第46行:mmc_init_card,该函数中有:
mmc_alloc_card,该函数实现如下:
七、mmc_bus_shutdown
区块层解析一文中我们可以知道,驱动的shutdow函数指针指向以下函数(依次调用):
回到本节首的mmc_bus_shutdown函数的第11--12行,调用了ost->bus_ops(总线操作集)的shutdown函数,这让我们回想起第六节我们用红色粗体字标注的2@host->bus_ops,这样我们知道此处的bus_ops指向了mmc_ops_unsafe或者mmc_ops中的一个,再一看这两个变量指向的是同一个函数mmc_shutdown。
八、mmc_shutdown(mmc.c)
该函数主要分为两步,第一步执行_mmc_resume,第二步执行_mmc_suspend。
执行第一步的判断条件中的mmc_can_poweroff_notify是判断当前mmc是否为上电状态,而!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)这个不太懂。
下面我们来看一下_mmc_resume和_mmc_suspend这两个函数干了啥!
九、_mmc_resume
第9--10行:做了一个非法判断,不管它;
第12行:获取一个host;
第14--15行:如果存储卡没被挂载(还处于运行态),则跳出函数;
第17行:上电操作,这里代码比较多,只贴出核心调用流程:mmc_power_up--》mmc_set_ios--》(mmci)的mmc_regulator_set_ocr函数。
也就是说,最终调用了host的ocr(工作条件寄存器)来上电和设置电压值。
各寄存器的作用可以参考:SD/MMC相关寄存器的介绍。
下面简单看下mmc_regulator_set_ocr函数的实现,该函数不详细介绍,自己看:
再次来到我们本节首部的_mmc_resume函数:
第18行:调用mmc_init_card初始化emmc;这个函数太长了,我们稍后来详解;
第19行:mmc_card_clr_suspended,为一个宏,用来清除挂载标记,该宏不设计命令的发送。
那么_mmc_resume函数分析完成,还剩下第18行的mmc_init_card,下面来详细分析;
十、mmc_init_card
加上注释和空行有将近500行,恐怕是我们分析过的最长的函数了,既然这么长,看来也是很重要,那就分析下:
注释:
处理检测和初始化卡的操作;
如果是重新挂载,oldcard参数就是我们要重新识别的卡。
第10--18行:初始化局部变量,ext_csd
是emmc的扩展寄存器的意思。
第21--22行:设置总线模式,如果host不是SPI(串行外设接口)总线,则设置总线为MMC_BUSMODE_OPENDRAIN模式(具体是啥我也不懂);
第31行:设置mmc为空闲状态;
注释:自从我们改变了ocr寄存器的值,我们就必须设置某些卡回到空闲状态;我们需要等待一秒给卡做出回应;该函数对那些已经休眠的卡是不需的。
看下mmc_go_idle的详细代码:
第16行和第29行,在不是spi总线的情况下,会通过ocr寄存器设置片选,mmc_set_chip_select 函数如下。
第34行,滴用了mmc_send_op_cond,等待上电完成,(1 << 30)表示当前设置的是sector模式(JESD84-B50):
该函数通过发送MMC_SEND_OP_COND(CMD1)来获取emmc上电状态,如果上电成功就跳出循环。
回到第十节首部的mmc_init_card函数:
第41--46行:spi模式下才处理,emmc不跑此流程;
第50--56行:从设备获取cid的值,命令码为MMC_ALL_SEND_CID(CMD2);
第57--78行:将cid初始化给host;
第83--86行:设置RCA的值,命令码为MMC_SET_RELATIVE_ADDR(CMD3);
第88行设置总线模式,关于以下两个宏,请参考http://www.cnblogs.com/zhangpengshou/p/3643546.html:
#define MMC_BUSMODE_OPENDRAIN 1
#define MMC_BUSMODE_PUSHPULL 2
第91--105行:从设备并设置host的csd寄存器和cid寄存器的值,命令码MMC_SEND_CSD(CMD9)。
第111行:选择card,命令码MMC_SELECT_CARD(CMD7);
第121行:从设备获取csd的值,命令码MMC_SEND_EXT_CSD(CMD8);
第124行:设置host的csd。
第133--134:设置块访问方式(byte访问,还是sector访问)。
第137行:设置擦除单元大小;
第144到最后:在初始化ext_csd的值,暂不细讲。
那么第十节的mmc_init_card函数就到这。
回到第九节的_mmc_resume函数:
第19行:清除挂载标记;
那么_mmc_resume函数也完了。
回到第八节的mmc_shutdown函数:
第17行:mmc的挂载。
十一、_mmc_suspend
说明第4--5行这里传给notify_type是EXT_CSD_POWER_OFF_LONG,应该代表的是长按关机;
第12行:如果已经挂载就直接退出;
第15行:如果当前正在处理块请求,则执行第16行:
第16行:停止块请求处理,调用了mmc_stop_bkops函数,如下:
注释:停止正在处理的块请求;发送HPI(高优先级中断)命令去停止后台操作,从而加快前台操作,例如读写。会一直等待到跳出编程状态,以防止在读写的时候出错。
第15行:调用mmc_interrupt_hpi执行hpi中断,展开如下:
第16行:检查是否支持hpi;
第22行:获取卡状态,发送MMC_SELECT_CARD(CMD7);
第28--36行:判断卡的状态,如果是非编程状态,则跳转到out执行,相当于退出函数;如果是编程状态,则跳出switch,执行第48行;
第48行:执行mmc_send_hpi_cmd函数:
第7--11行:如果不支持hpi,直接退出;
第13行:这里hpi_cmd是在mmc_read_ext_csd(mmc.c)函数中根据从设备读到的配置进行配置的。
第14--22行:根据hpi_cmd的值发送具体的命令MMC_STOP_TRANSMISSION(CMD12):MMC_SEND_STATUS(CMD13);
后面的代码:返回命令发送后的状态;
回到mmc_interrupt_hpi函数:
第52行及以后:等待设备切换出编程状态,当然这里设置了超时,如果在超时范围内没有切换出来,则退出。
那么mmc_interrupt_hpi函数讲完了,该函数主要就是向设备发送命令,让设备退出编程状态;
回到mmc_stop_bkops函数:
第21--24行:如果前面的hpi执行成功,则设置当前为非块操作状态。
那么mmc_interrupt_hpi函数讲完了,该函数让设备退出编程状态,并设置块操作状态;
回到_mmc_suspend函数:
第21行:调用mmc_cache_ctrl函数,将cache中的数据写到设备上,代码如下:
第13--15行:如果设备部支持缓存,或者卡是可移动设备(emmc是非可移动设备),则返回;
第17--18行:如果设备存在,是emmc且缓存大小大于0,则执行下面的代码,否则跳到最后返回;
第21--31行:调用mmc_switch函数(CMD6命令),并将EXT_CSD_CACHE_CTRL作为命令参数,来关闭缓存开关(插入的参数enable为0),从而触发数据从缓存到设备的操作。
mmc_cache_ctrl函数讲完,回到_mmc_suspend函数:
第25--27行:如果卡是上电状态并且还没被挂载,则设置为下电状态(不涉及命令的发送,host状态设置),否则执行下面的代码:
第28--29行:如果设备不是休眠状态,则发送MMC_SLEEP_AWAKE(CMD5)命令设置休眠,否则执行下面的代码:
第30--31行:如果不是spi模式(emmc是sdio模式),则通过发送MMC_SELECT_CARD(CMD7)删除卡。
第32行:取消高速模式;
第35行:调用mmc_power_off来设置设备的掉电,代码如下:
第8--17行:设置参数关闭时钟,关闭电压,设置总线模式为open drain,关闭片选,关闭电源,设置总线宽度等;
第18行:调用mmc_set_ios,通过修改ocr寄存器的方式来执行上面的设置,这个函数在本文的第9节有提到。
那么mmc_power_off函数就讲完了,该函数用来对设备进行掉电的操作。
回到_mmc_suspend函数:
第36行:设置卡状态为挂载,不设计命令的发送;
那么_mmc_suspend函数讲完了,实现流程如下:
1、如果设备是挂载状态,则直接返回,否则执行下面的流程:
2、如果设备当前正在执行块请求操作,则发送停止块请求操作(停止编程操作),会涉及到以下命令:
MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
MMC_SWITCH 6 /* ac [31:0] See below R1b */
MMC_SELECT_CARD 7 /* ac [31:16] RCA
#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
3、将缓存数据写到卡上,发送MMC_SWITCH 命令。
4、根据emmc的当前状态,可能会发送CMD6设置host状态为poweroff状态,或者是发送CMD1命令进入休眠,或者是发送CMD7命令设置host与card的断开;
5、通过设置设备的ocr寄存器设置设备断电。
6、设置设备为挂载状态。
那么我们本节研究的核心函数mmc_shutdown研究完了。
回到第三节的device_shutdown函数:
第45--48行:如果设备没有总线关闭函数,但是有驱动关闭函数,则执行驱动关闭函数,我们来到emmc驱动模块的block.c.通过以下结构体可以知道,驱动的shutdown函数指针指向的是mmc_blk_shutdown。
我们回到第三节的device_shutdown函数,回顾一系发现,该函数已经讲完,该函数依赖于总线和驱动的shutdown函数的实现。
回到第二节kernel_restart_prepare函数,也分析完毕;
回到第一节的kernel_restart函数:
第12行:跟cpu相关,略过;
第13行:调用syscore_shutdown关闭注册到系统内核的一些模块,代码如下。
十二、syscore_shutdown(分析发现,跟emmc没有大的关系,没有发送CMD命令)
而通过搜索可以发现,如果想要将模块注入到syscore_ops_list链表,必须要调用:
先看s3c2410_pm_syscore_ops的resume函数:
再来看下s3c24xx_irq_syscore_ops的resume函数,还是看不懂,看懂的要告诉我哦:
好,回到第一节的kernel_restart函数:
后面的kmsg_dump和machine_restart跟存储设备没有关系,而且太复杂,暂时还没搞懂,就不说了。
分析错误了的和因为能力问题没分析到的,还请大家补充,写个博客,贴个链接给我,让我学习学习,先谢谢喽。
由于我的工作与sd/emmc存储相关,所以在接下来的分析重启的过程中,如果有涉及到该部分的流程,或者我认为有可能但是还没确认的流程,我都会贴上来;如果与其无关,我会略过,敬请谅解;
一、kernel_restart
废话不说,从reboot函数的kernel_restart函数开始,代码如下:
/** * kernel_restart - reboot the system * @cmd: pointer to buffer containing command to execute for restart * or %NULL * * Shutdown everything and perform a clean reboot. * This is not safe to call in interrupt context. */ void kernel_restart(char *cmd) { kernel_restart_prepare(cmd); migrate_to_reboot_cpu(); syscore_shutdown(); if (!cmd) pr_emerg("Restarting system\n"); else pr_emerg("Restarting system with command '%s'\n", cmd); kmsg_dump(KMSG_DUMP_RESTART); machine_restart(cmd); }
第1--8行注释说明:
该函数用来重启系统;
参数cmd:一个指向重启命令缓冲区的指针;
关闭一切并开启一次干净利落的重启;
这个对于正在执行的中断来说是不安全的;
第11行:做内核重启准备,上代码:
二、kernel_restart_prepare
void kernel_restart_prepare(char *cmd) { blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; usermodehelper_disable(); device_shutdown(); }第3行:通知所有注册到reboot_notifier_list并关心SYS_RESTART的子系统;根据通知链的基本知识我们知道(http://blog.chinaunix.net/uid-25871104-id-3086446.html),所有关心reboot_notifier_list的子系统,必须通过函数
int register_reboot_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&reboot_notifier_list, nb); }
将自己的处理函数通过参数nb注册进来,所以我们在这里可以全局搜索(source insight工具)register_reboot_notifier这个函数;
搜出来的结果没有发现跟存储相关,所以这里不详细分析。
回到本节分析的函数kernel_restart_prepare:
第4行:设置系统状态;
第5行:取消用户帮助;
第6行:关闭设备,该函数在\drivers\base\core.c文件中,这里必须展开研究:
三、device_shutdown
/** * device_shutdown - call ->shutdown() on each device to shutdown. */ void device_shutdown(void) { struct device *dev, *parent; spin_lock(&devices_kset->list_lock); /* * Walk the devices list backward, shutting down each in turn. * Beware that device unplug events may also start pulling * devices offline, even as the system is shutting down. */ while (!list_empty(&devices_kset->list)) { dev = list_entry(devices_kset->list.prev, struct device, kobj.entry); /* * hold reference count of device's parent to * prevent it from being freed because parent's * lock is to be held */ parent = get_device(dev->parent); get_device(dev); /* * Make sure the device is off the kset list, in the * event that dev->*->shutdown() doesn't remove it. */ list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); /* hold lock to avoid race with probe/release */ if (parent) device_lock(parent); device_lock(dev); /* Don't allow any more runtime suspends */ pm_runtime_get_noresume(dev); pm_runtime_barrier(dev); if (dev->bus && dev->bus->shutdown) { if (initcall_debug) dev_info(dev, "shutdown\n"); dev->bus->shutdown(dev); } else if (dev->driver && dev->driver->shutdown) { if (initcall_debug) dev_info(dev, "shutdown\n"); dev->driver->shutdown(dev); } device_unlock(dev); if (parent) device_unlock(parent); put_device(dev); put_device(parent); spin_lock(&devices_kset->list_lock); } spin_unlock(&devices_kset->list_lock); async_synchronize_full(); }
注释:调用设备的shutdown函数去关闭设备;
第8--35行:设置访问设备列表的同步锁,并获取一个设备和对应的父设备,然后解锁,为什么要这么做可以看注释;这些操作与51--58行做的操作相反,等下不再介绍。
第38--39行:涉及电源管理,具体做什么暂时不清楚,对我们分析应该没有影响;
第41--49行:本函数的核心部分;如果该设备注册了总线,并实现了总线关闭函数则执行该函数;否则如果注册了驱动,且驱动关闭函数的shutdown函数指针不为空则执行该函数;
下面我们将分析跟存储sd/emmc相关的1@dev->bus->shutdown。
四、mmc_alloc_host
在emmc/sd host层解析一文中,我们有分析过mmci_probe函数,该函数的第30行,是这样子的:
mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);该函数里面有这么一行:
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
这里是建立了一个延时工作队列,该工作队列的处理函数为mmc_rescan,该函数是用来扫描设备的,在这里不详细分析,我们的目的是要找出设备的shutdown函数。
在mmc_rescan函数中,有这么一段:
mmc_rescan_try_freq(host, max(freqs[i], host->f_min))在该函数中又有这么一段:
if (!mmc_attach_mmc(host)) return 0;进入到该函数。
五、mmc_attach_mmc
/* * Starting point for MMC card init. */ int mmc_attach_mmc(struct mmc_host *host) { int err; u32 ocr, rocr; BUG_ON(!host); WARN_ON(!host->claimed); /* Set correct bus mode for MMC before attempting attach */ if (!mmc_host_is_spi(host)) mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); err = mmc_send_op_cond(host, 0, &ocr); if (err) return err; mmc_attach_bus_ops(host); if (host->ocr_avail_mmc) host->ocr_avail = host->ocr_avail_mmc; /* * We need to get OCR a different way for SPI. */ if (mmc_host_is_spi(host)) { err = mmc_spi_read_ocr(host, 1, &ocr); if (err) goto err; } rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage of the card? */ if (!rocr) { err = -EINVAL; goto err; } /* * Detect and init the card. */ err = mmc_init_card(host, rocr, NULL); if (err) goto err; mmc_release_host(host); err = mmc_add_card(host->card); mmc_claim_host(host); if (err) goto remove_card; return 0; remove_card: mmc_release_host(host); mmc_remove_card(host->card); mmc_claim_host(host); host->card = NULL; err: mmc_detach_bus(host); pr_err("%s: error %d whilst initialising MMC card\n", mmc_hostname(host), err); return err; }通过函数注释我们知道:该函数完成mmc的初始化操作。
第20行,关联设备到总线操作集,具体实现如下。
六、mmc_attach_bus_ops
static void mmc_attach_bus_ops(struct mmc_host *host) { const struct mmc_bus_ops *bus_ops; if (!mmc_card_is_removable(host)) bus_ops = &mmc_ops_unsafe; else bus_ops = &mmc_ops; mmc_attach_bus(host, bus_ops); }
通过该函数可以看出,如果是非拔插设备,将设备挂到mmc_ops_unsafe总线操作,如果是拔插设备就挂到mmc_ops。
我的理解是,如果支持拔插,则认为拔插是安全的,如果不支持拔插则拔插是不安全的。
下面为可插拔和不可插拔的总线处理集接口:
static const struct mmc_bus_ops mmc_ops = { .remove = mmc_remove, .detect = mmc_detect, .suspend = NULL, .resume = NULL, .power_restore = mmc_power_restore, .alive = mmc_alive, .shutdown = mmc_shutdown, }; static const struct mmc_bus_ops mmc_ops_unsafe = { .remove = mmc_remove, .detect = mmc_detect, .suspend = mmc_suspend, .resume = mmc_resume, .runtime_suspend = mmc_runtime_suspend, .runtime_resume = mmc_runtime_resume, .power_restore = mmc_power_restore, .alive = mmc_alive, .shutdown = mmc_shutdown, };
回到本节首部的mmc_attach_bus_ops函数,末尾有个函数mmc_attach_bus:
/* * Assign a mmc bus handler to a host. Only one bus handler may control a * host at any given time. */ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops) { unsigned long flags; BUG_ON(!host); BUG_ON(!ops); WARN_ON(!host->claimed); spin_lock_irqsave(&host->lock, flags); BUG_ON(host->bus_ops); BUG_ON(host->bus_refs); host->bus_ops = ops; host->bus_refs = 1; host->bus_dead = 0; spin_unlock_irqrestore(&host->lock, flags); }通过注释及代码可以知道,该函数为设备分配总线处理接口,每个设备只能有一个这样的总线处理接口集。
通过该函数我们知道host->bus_ops指向了mmc_ops_unsafe或者mmc_ops中的一个;先记住等下要用:2@host->bus_ops
回到第五节的mmc_attach_mmc函数的第46行:mmc_init_card,该函数中有:
mmc_alloc_card,该函数实现如下:
/* * Allocate and initialise a new MMC card structure. */ struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type) { struct mmc_card *card; card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL); if (!card) return ERR_PTR(-ENOMEM); card->host = host; device_initialize(&card->dev); card->dev.parent = mmc_classdev(host); card->dev.bus = &mmc_bus_type; card->dev.release = mmc_release_card; card->dev.type = type; return card; }该函数告诉我们设备的总线指向mmc_bus_type,还记得我们在第三节中用红色粗体字标记的1@dev->bus->shutdown吗?对该红色字体中的bus在本函数中被赋值了,这样我们要分析的就是mmc_bus_type结构体中的shutdown函数了,我们先来看一下mmc_bus_type结构体:
static struct bus_type mmc_bus_type = { .name = "mmc", .dev_groups = mmc_dev_groups, .match = mmc_bus_match, .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, .remove = mmc_bus_remove, .shutdown = mmc_bus_shutdown, .pm = &mmc_bus_pm_ops, };接下来分析下mmc_bus_shutdown。
七、mmc_bus_shutdown
static void mmc_bus_shutdown(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_host *host = card->host; int ret; if (dev->driver && drv->shutdown) drv->shutdown(card); if (host->bus_ops->shutdown) { ret = host->bus_ops->shutdown(host); if (ret) pr_warn("%s: error %d during shutdown\n", mmc_hostname(host), ret); } }第8--9行:如果设备的驱动注册了shutdown,就执行该函数,通过emmc/sd
区块层解析一文中我们可以知道,驱动的shutdow函数指针指向以下函数(依次调用):
static void mmc_blk_shutdown(struct mmc_card *card) { _mmc_blk_suspend(card); }
static int _mmc_blk_suspend(struct mmc_card *card) { struct mmc_blk_data *part_md; struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); } } return 0; }
/** * mmc_queue_suspend - suspend a MMC request queue * @mq: MMC queue to suspend * * Stop the block request queue, and wait for our thread to * complete any outstanding requests. This ensures that we * won't suspend while a request is being processed. */ void mmc_queue_suspend(struct mmc_queue *mq) { struct request_queue *q = mq->queue; unsigned long flags; if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { mq->flags |= MMC_QUEUE_SUSPENDED; spin_lock_irqsave(q->queue_lock, flags); blk_stop_queue(q); spin_unlock_irqrestore(q->queue_lock, flags); down(&mq->thread_sem); } }从上面的函数可以看出来,mmc_blk_shutdown函数实际停止了消息对列中消息的处理。
回到本节首的mmc_bus_shutdown函数的第11--12行,调用了ost->bus_ops(总线操作集)的shutdown函数,这让我们回想起第六节我们用红色粗体字标注的2@host->bus_ops,这样我们知道此处的bus_ops指向了mmc_ops_unsafe或者mmc_ops中的一个,再一看这两个变量指向的是同一个函数mmc_shutdown。
八、mmc_shutdown(mmc.c)
/* * Shutdown callback */ static int mmc_shutdown(struct mmc_host *host) { int err = 0; /* * In a specific case for poweroff notify, we need to resume the card * before we can shutdown it properly. */ if (mmc_can_poweroff_notify(host->card) && !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) err = _mmc_resume(host); if (!err) err = _mmc_suspend(host, false); return err; }
该函数主要分为两步,第一步执行_mmc_resume,第二步执行_mmc_suspend。
执行第一步的判断条件中的mmc_can_poweroff_notify是判断当前mmc是否为上电状态,而!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)这个不太懂。
下面我们来看一下_mmc_resume和_mmc_suspend这两个函数干了啥!
九、_mmc_resume
/* * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ static int _mmc_resume(struct mmc_host *host) { int err = 0; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); if (!mmc_card_suspended(host->card)) goto out; mmc_power_up(host, host->card->ocr); err = mmc_init_card(host, host->card->ocr, host->card); mmc_card_clr_suspended(host->card); out: mmc_release_host(host); return err; }
第9--10行:做了一个非法判断,不管它;
第12行:获取一个host;
第14--15行:如果存储卡没被挂载(还处于运行态),则跳出函数;
第17行:上电操作,这里代码比较多,只贴出核心调用流程:mmc_power_up--》mmc_set_ios--》(mmci)的mmc_regulator_set_ocr函数。
也就是说,最终调用了host的ocr(工作条件寄存器)来上电和设置电压值。
各寄存器的作用可以参考:SD/MMC相关寄存器的介绍。
下面简单看下mmc_regulator_set_ocr函数的实现,该函数不详细介绍,自己看:
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmci_host *host = mmc_priv(mmc); struct variant_data *variant = host->variant; u32 pwr = 0; unsigned long flags; int ret; pm_runtime_get_sync(mmc_dev(mmc)); if (host->plat->ios_handler && host->plat->ios_handler(mmc_dev(mmc), ios)) dev_err(mmc_dev(mmc), "platform ios_handler failed\n"); switch (ios->power_mode) { case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { regulator_disable(mmc->supply.vqmmc); host->vqmmc_enabled = false; } break; case MMC_POWER_UP: if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); /* * The ST Micro variant doesn't have the PL180s MCI_PWR_UP * and instead uses MCI_PWR_ON so apply whatever value is * configured in the variant data. */ pwr |= variant->pwrreg_powerup; break; case MMC_POWER_ON: if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { ret = regulator_enable(mmc->supply.vqmmc); if (ret < 0) dev_err(mmc_dev(mmc), "failed to enable vqmmc regulator\n"); else host->vqmmc_enabled = true; } pwr |= MCI_PWR_ON; break; } if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) { /* * The ST Micro variant has some additional bits * indicating signal direction for the signals in * the SD/MMC bus and feedback-clock usage. */ pwr |= host->plat->sigdir; if (ios->bus_width == MMC_BUS_WIDTH_4) pwr &= ~MCI_ST_DATA74DIREN; else if (ios->bus_width == MMC_BUS_WIDTH_1) pwr &= (~MCI_ST_DATA74DIREN & ~MCI_ST_DATA31DIREN & ~MCI_ST_DATA2DIREN); } if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) { if (host->hw_designer != AMBA_VENDOR_ST) pwr |= MCI_ROD; else { /* * The ST Micro variant use the ROD bit for something * else and only has OD (Open Drain). */ pwr |= MCI_OD; } } /* * If clock = 0 and the variant requires the MMCIPOWER to be used for * gating the clock, the MCI_PWR_ON bit is cleared. */ if (!ios->clock && variant->pwrreg_clkgate) pwr &= ~MCI_PWR_ON; spin_lock_irqsave(&host->lock, flags); mmci_set_clkreg(host, ios->clock); mmci_write_pwrreg(host, pwr); mmci_reg_delay(host); spin_unlock_irqrestore(&host->lock, flags); pm_runtime_mark_last_busy(mmc_dev(mmc)); pm_runtime_put_autosuspend(mmc_dev(mmc)); }
再次来到我们本节首部的_mmc_resume函数:
第18行:调用mmc_init_card初始化emmc;这个函数太长了,我们稍后来详解;
第19行:mmc_card_clr_suspended,为一个宏,用来清除挂载标记,该宏不设计命令的发送。
那么_mmc_resume函数分析完成,还剩下第18行的mmc_init_card,下面来详细分析;
十、mmc_init_card
/* * Handle the detection and initialisation of a card. * * In the case of a resume, "oldcard" will contain the card * we're trying to reinitialise. */ static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; int err, ddr = 0; u32 cid[4]; unsigned int max_dtr; u32 rocr; u8 *ext_csd = NULL; BUG_ON(!host); WARN_ON(!host->claimed); /* Set correct bus mode for MMC before attempting init */ if (!mmc_host_is_spi(host)) mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle * state. We wait 1ms to give cards time to * respond. * mmc_go_idle is needed for eMMC that are asleep */ mmc_go_idle(host); /* The extra bit indicates that we support high capacity */ err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr); if (err) goto err; /* * For SPI, enable CRC as appropriate. */ if (mmc_host_is_spi(host)) { err = mmc_spi_set_crc(host, use_spi_crc); if (err) goto err; } /* * Fetch CID from card. */ if (mmc_host_is_spi(host)) err = mmc_send_cid(host, cid); else err = mmc_all_send_cid(host, cid); if (err) goto err; if (oldcard) { if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { err = -ENOENT; goto err; } card = oldcard; } else { /* * Allocate card structure. */ card = mmc_alloc_card(host, &mmc_type); if (IS_ERR(card)) { err = PTR_ERR(card); goto err; } card->ocr = ocr; card->type = MMC_TYPE_MMC; card->rca = 1; memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } /* * For native busses: set card RCA and quit open drain mode. */ if (!mmc_host_is_spi(host)) { err = mmc_set_relative_addr(card); if (err) goto free_card; mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } if (!oldcard) { /* * Fetch CSD from card. */ err = mmc_send_csd(card, card->raw_csd); if (err) goto free_card; err = mmc_decode_csd(card); if (err) goto free_card; err = mmc_decode_cid(card); if (err) goto free_card; } /* * Select card, as all following commands rely on that. */ if (!mmc_host_is_spi(host)) { err = mmc_select_card(card); if (err) goto free_card; } if (!oldcard) { /* * Fetch and process extended CSD. */ err = mmc_get_ext_csd(card, &ext_csd); if (err) goto free_card; err = mmc_read_ext_csd(card, ext_csd); if (err) goto free_card; /* If doing byte addressing, check if required to do sector * addressing. Handle the case of <2GB cards needing sector * addressing. See section 8.1 JEDEC Standard JED84-A441; * ocr register has bit 30 set for sector addressing. */ if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30))) mmc_card_set_blockaddr(card); /* Erase size depends on CSD and Extended CSD */ mmc_set_erase_size(card); } /* * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF * bit. This bit will be lost every time after a reset or power off. */ if (card->ext_csd.enhanced_area_en || (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1, card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; if (err) { err = 0; /* * Just disable enhanced area off & sz * will try to enable ERASE_GROUP_DEF * during next time reinit */ card->ext_csd.enhanced_area_offset = -EINVAL; card->ext_csd.enhanced_area_size = -EINVAL; } else { card->ext_csd.erase_group_def = 1; /* * enable ERASE_GRP_DEF successfully. * This will affect the erase size, so * here need to reset erase size */ mmc_set_erase_size(card); } } /* * Ensure eMMC user default partition is enabled */ if (card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) { card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG, card->ext_csd.part_config, card->ext_csd.part_time); if (err && err != -EBADMSG) goto free_card; } /* * Enable power_off_notification byte in the ext_csd register */ if (card->ext_csd.rev >= 6) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_POWER_OFF_NOTIFICATION, EXT_CSD_POWER_ON, card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; /* * The err can be -EBADMSG or 0, * so check for success and update the flag */ if (!err) card->ext_csd.power_off_notification = EXT_CSD_POWER_ON; } /* * Activate high speed (if supported) */ if (card->ext_csd.hs_max_dtr != 0) { err = 0; if (card->ext_csd.hs_max_dtr > 52000000 && host->caps2 & MMC_CAP2_HS200) err = mmc_select_hs200(card); else if (host->caps & MMC_CAP_MMC_HIGHSPEED) err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1, card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; if (err) { pr_warning("%s: switch to highspeed failed\n", mmc_hostname(card->host)); err = 0; } else { if (card->ext_csd.hs_max_dtr > 52000000 && host->caps2 & MMC_CAP2_HS200) { mmc_card_set_hs200(card); mmc_set_timing(card->host, MMC_TIMING_MMC_HS200); } else { mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_MMC_HS); } } } /* * Compute bus speed. */ max_dtr = (unsigned int)-1; if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { if (max_dtr > card->ext_csd.hs_max_dtr) max_dtr = card->ext_csd.hs_max_dtr; if (mmc_card_highspeed(card) && (max_dtr > 52000000)) max_dtr = 52000000; } else if (max_dtr > card->csd.max_dtr) { max_dtr = card->csd.max_dtr; } mmc_set_clock(host, max_dtr); /* * Indicate DDR mode (if supported). */ if (mmc_card_highspeed(card)) { if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) && ((host->caps & (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)) == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) ddr = MMC_1_8V_DDR_MODE; else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) && ((host->caps & (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)) == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) ddr = MMC_1_2V_DDR_MODE; } /* * Indicate HS200 SDR mode (if supported). */ if (mmc_card_hs200(card)) { u32 ext_csd_bits; u32 bus_width = card->host->ios.bus_width; /* * For devices supporting HS200 mode, the bus width has * to be set before executing the tuning function. If * set before tuning, then device will respond with CRC * errors for responses on CMD line. So for HS200 the * sequence will be * 1. set bus width 4bit / 8 bit (1 bit not supported) * 2. switch to HS200 mode * 3. set the clock to > 52Mhz <=200MHz and * 4. execute tuning for HS200 */ if ((host->caps2 & MMC_CAP2_HS200) && card->host->ops->execute_tuning) { mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK_HS200); mmc_host_clk_release(card->host); } if (err) { pr_warning("%s: tuning execution failed\n", mmc_hostname(card->host)); goto err; } ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; err = mmc_select_powerclass(card, ext_csd_bits); if (err) pr_warning("%s: power class selection to bus width %d" " failed\n", mmc_hostname(card->host), 1 << bus_width); } /* * Activate wide bus and DDR (if supported). */ if (!mmc_card_hs200(card) && (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { static unsigned ext_csd_bits[][2] = { { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, }; static unsigned bus_widths[] = { MMC_BUS_WIDTH_8, MMC_BUS_WIDTH_4, MMC_BUS_WIDTH_1 }; unsigned idx, bus_width = 0; if (host->caps & MMC_CAP_8_BIT_DATA) idx = 0; else idx = 1; for (; idx < ARRAY_SIZE(bus_widths); idx++) { bus_width = bus_widths[idx]; if (bus_width == MMC_BUS_WIDTH_1) ddr = 0; /* no DDR for 1-bit width */ err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); if (err) pr_warning("%s: power class selection to " "bus width %d failed\n", mmc_hostname(card->host), 1 << bus_width); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][0], card->ext_csd.generic_cmd6_time); if (!err) { mmc_set_bus_width(card->host, bus_width); /* * If controller can't handle bus width test, * compare ext_csd previously read in 1 bit mode * against ext_csd at new bus width */ if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) err = mmc_compare_ext_csds(card, bus_width); else err = mmc_bus_test(card, bus_width); if (!err) break; } } if (!err && ddr) { err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); if (err) pr_warning("%s: power class selection to " "bus width %d ddr %d failed\n", mmc_hostname(card->host), 1 << bus_width, ddr); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][1], card->ext_csd.generic_cmd6_time); } if (err) { pr_warning("%s: switch to bus width %d ddr %d " "failed\n", mmc_hostname(card->host), 1 << bus_width, ddr); goto free_card; } else if (ddr) { /* * eMMC cards can support 3.3V to 1.2V i/o (vccq) * signaling. * * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. * * 1.8V vccq at 3.3V core voltage (vcc) is not required * in the JEDEC spec for DDR. * * Do not force change in vccq since we are obviously * working and no change to vccq is needed. * * WARNING: eMMC rules are NOT the same as SD DDR */ if (ddr == MMC_1_2V_DDR_MODE) { err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); if (err) goto err; } mmc_card_set_ddr_mode(card); mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); mmc_set_bus_width(card->host, bus_width); } } /* * Enable HPI feature (if supported) */ if (card->ext_csd.hpi) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HPI_MGMT, 1, card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; if (err) { pr_warning("%s: Enabling HPI failed\n", mmc_hostname(card->host)); err = 0; } else card->ext_csd.hpi_en = 1; } /* * If cache size is higher than 0, this indicates * the existence of cache and it can be turned on. */ if ((host->caps2 & MMC_CAP2_CACHE_CTRL) && card->ext_csd.cache_size > 0) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL, 1, card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; /* * Only if no error, cache is turned on successfully. */ if (err) { pr_warning("%s: Cache is supported, " "but failed to turn on (%d)\n", mmc_hostname(card->host), err); card->ext_csd.cache_ctrl = 0; err = 0; } else { card->ext_csd.cache_ctrl = 1; } } /* * The mandatory minimum values are defined for packed command. * read: 5, write: 3 */ if (card->ext_csd.max_packed_writes >= 3 && card->ext_csd.max_packed_reads >= 5 && host->caps2 & MMC_CAP2_PACKED_CMD) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_EXP_EVENTS_CTRL, EXT_CSD_PACKED_EVENT_EN, card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; if (err) { pr_warn("%s: Enabling packed event failed\n", mmc_hostname(card->host)); card->ext_csd.packed_event_en = 0; err = 0; } else { card->ext_csd.packed_event_en = 1; } } if (!oldcard) host->card = card; mmc_free_ext_csd(ext_csd); return 0; free_card: if (!oldcard) mmc_remove_card(card); err: mmc_free_ext_csd(ext_csd); return err; }
加上注释和空行有将近500行,恐怕是我们分析过的最长的函数了,既然这么长,看来也是很重要,那就分析下:
注释:
处理检测和初始化卡的操作;
如果是重新挂载,oldcard参数就是我们要重新识别的卡。
第10--18行:初始化局部变量,ext_csd
是emmc的扩展寄存器的意思。
第21--22行:设置总线模式,如果host不是SPI(串行外设接口)总线,则设置总线为MMC_BUSMODE_OPENDRAIN模式(具体是啥我也不懂);
第31行:设置mmc为空闲状态;
注释:自从我们改变了ocr寄存器的值,我们就必须设置某些卡回到空闲状态;我们需要等待一秒给卡做出回应;该函数对那些已经休眠的卡是不需的。
看下mmc_go_idle的详细代码:
int mmc_go_idle(struct mmc_host *host) { int err; struct mmc_command cmd = {0}; /* * Non-SPI hosts need to prevent chipselect going active during * GO_IDLE; that would put chips into SPI mode. Remind them of * that in case of hardware that won't pull up DAT3/nCS otherwise. * * SPI hosts ignore ios.chip_select; it's managed according to * rules that must accommodate non-MMC slaves which this layer * won't even know about. */ if (!mmc_host_is_spi(host)) { mmc_set_chip_select(host, MMC_CS_HIGH); mmc_delay(1); } cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; err = mmc_wait_for_cmd(host, &cmd, 0); mmc_delay(1); if (!mmc_host_is_spi(host)) { mmc_set_chip_select(host, MMC_CS_DONTCARE); mmc_delay(1); } host->use_spi_crc = 0; return err;第20行:向host发送了CMD0命令。
第16行和第29行,在不是spi总线的情况下,会通过ocr寄存器设置片选,mmc_set_chip_select 函数如下。
/* * Control chip select pin on a host. */ void mmc_set_chip_select(struct mmc_host *host, int mode) { mmc_host_clk_hold(host); host->ios.chip_select = mode; mmc_set_ios(host); mmc_host_clk_release(host); }回到第十节首部的mmc_init_card函数:
第34行,滴用了mmc_send_op_cond,等待上电完成,(1 << 30)表示当前设置的是sector模式(JESD84-B50):
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { struct mmc_command cmd = {0}; int i, err = 0; BUG_ON(!host); cmd.opcode = MMC_SEND_OP_COND; cmd.arg = mmc_host_is_spi(host) ? 0 : ocr; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; for (i = 100; i; i--) { err = mmc_wait_for_cmd(host, &cmd, 0); if (err) break; /* if we're just probing, do a single pass */ if (ocr == 0) break; /* otherwise wait until reset completes */ if (mmc_host_is_spi(host)) { if (!(cmd.resp[0] & R1_SPI_IDLE)) break; } else { if (cmd.resp[0] & MMC_CARD_BUSY) break; } err = -ETIMEDOUT; mmc_delay(10); } if (rocr && !mmc_host_is_spi(host)) *rocr = cmd.resp[0]; return err; }
该函数通过发送MMC_SEND_OP_COND(CMD1)来获取emmc上电状态,如果上电成功就跳出循环。
回到第十节首部的mmc_init_card函数:
第41--46行:spi模式下才处理,emmc不跑此流程;
第50--56行:从设备获取cid的值,命令码为MMC_ALL_SEND_CID(CMD2);
第57--78行:将cid初始化给host;
第83--86行:设置RCA的值,命令码为MMC_SET_RELATIVE_ADDR(CMD3);
第88行设置总线模式,关于以下两个宏,请参考http://www.cnblogs.com/zhangpengshou/p/3643546.html:
#define MMC_BUSMODE_OPENDRAIN 1
#define MMC_BUSMODE_PUSHPULL 2
第91--105行:从设备并设置host的csd寄存器和cid寄存器的值,命令码MMC_SEND_CSD(CMD9)。
第111行:选择card,命令码MMC_SELECT_CARD(CMD7);
第121行:从设备获取csd的值,命令码MMC_SEND_EXT_CSD(CMD8);
第124行:设置host的csd。
第133--134:设置块访问方式(byte访问,还是sector访问)。
第137行:设置擦除单元大小;
第144到最后:在初始化ext_csd的值,暂不细讲。
那么第十节的mmc_init_card函数就到这。
回到第九节的_mmc_resume函数:
第19行:清除挂载标记;
那么_mmc_resume函数也完了。
回到第八节的mmc_shutdown函数:
第17行:mmc的挂载。
十一、_mmc_suspend
static int _mmc_suspend(struct mmc_host *host, bool is_suspend) { int err = 0; unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT : EXT_CSD_POWER_OFF_LONG; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); if (mmc_card_suspended(host->card)) goto out; if (mmc_card_doing_bkops(host->card)) { err = mmc_stop_bkops(host->card); if (err) goto out; } err = mmc_cache_ctrl(host, 0); if (err) goto out; if (mmc_can_poweroff_notify(host->card) && ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend)) err = mmc_poweroff_notify(host->card, notify_type); else if (mmc_can_sleep(host->card)) err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); if (!err) { mmc_power_off(host); mmc_card_set_suspended(host->card); } out: mmc_release_host(host); return err; }mmc_shutdown函数传递给_mmc_suspend的第二个参数is_suspend的置为false;
说明第4--5行这里传给notify_type是EXT_CSD_POWER_OFF_LONG,应该代表的是长按关机;
第12行:如果已经挂载就直接退出;
第15行:如果当前正在处理块请求,则执行第16行:
第16行:停止块请求处理,调用了mmc_stop_bkops函数,如下:
/** * mmc_stop_bkops - stop ongoing BKOPS * @card: MMC card to check BKOPS * * Send HPI command to stop ongoing background operations to * allow rapid servicing of foreground operations, e.g. read/ * writes. Wait until the card comes out of the programming state * to avoid errors in servicing read/write requests. */ int mmc_stop_bkops(struct mmc_card *card) { int err = 0; BUG_ON(!card); err = mmc_interrupt_hpi(card); /* * If err is EINVAL, we can't issue an HPI. * It should complete the BKOPS. */ if (!err || (err == -EINVAL)) { mmc_card_clr_doing_bkops(card); err = 0; } return err; }
注释:停止正在处理的块请求;发送HPI(高优先级中断)命令去停止后台操作,从而加快前台操作,例如读写。会一直等待到跳出编程状态,以防止在读写的时候出错。
第15行:调用mmc_interrupt_hpi执行hpi中断,展开如下:
/** * mmc_interrupt_hpi - Issue for High priority Interrupt * @card: the MMC card associated with the HPI transfer * * Issued High Priority Interrupt, and check for card status * until out-of prg-state. */ int mmc_interrupt_hpi(struct mmc_card *card) { int err; u32 status; unsigned long prg_wait; BUG_ON(!card); if (!card->ext_csd.hpi_en) { pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host)); return 1; } mmc_claim_host(card->host); err = mmc_send_status(card, &status); if (err) { pr_err("%s: Get card status fail\n", mmc_hostname(card->host)); goto out; } switch (R1_CURRENT_STATE(status)) { case R1_STATE_IDLE: case R1_STATE_READY: case R1_STATE_STBY: case R1_STATE_TRAN: /* * In idle and transfer states, HPI is not needed and the caller * can issue the next intended command immediately */ goto out; case R1_STATE_PRG: break; default: /* In all other states, it's illegal to issue HPI */ pr_debug("%s: HPI cannot be sent. Card state=%d\n", mmc_hostname(card->host), R1_CURRENT_STATE(status)); err = -EINVAL; goto out; } err = mmc_send_hpi_cmd(card, &status); if (err) goto out; prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time); do { err = mmc_send_status(card, &status); if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN) break; if (time_after(jiffies, prg_wait)) err = -ETIMEDOUT; } while (!err); out: mmc_release_host(card->host); return err; }注释:执行高优先级中断,直到卡状态不是编程状态;
第16行:检查是否支持hpi;
第22行:获取卡状态,发送MMC_SELECT_CARD(CMD7);
第28--36行:判断卡的状态,如果是非编程状态,则跳转到out执行,相当于退出函数;如果是编程状态,则跳出switch,执行第48行;
第48行:执行mmc_send_hpi_cmd函数:
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) { struct mmc_command cmd = {0}; unsigned int opcode; int err; if (!card->ext_csd.hpi) { pr_warning("%s: Card didn't support HPI command\n", mmc_hostname(card->host)); return -EINVAL; } opcode = card->ext_csd.hpi_cmd; if (opcode == MMC_STOP_TRANSMISSION) cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; else if (opcode == MMC_SEND_STATUS) cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.opcode = opcode; cmd.arg = card->rca << 16 | 1; err = mmc_wait_for_cmd(card->host, &cmd, 0); if (err) { pr_warn("%s: error %d interrupting operation. " "HPI command response %#x\n", mmc_hostname(card->host), err, cmd.resp[0]); return err; } if (status) *status = cmd.resp[0]; return 0; }
第7--11行:如果不支持hpi,直接退出;
第13行:这里hpi_cmd是在mmc_read_ext_csd(mmc.c)函数中根据从设备读到的配置进行配置的。
第14--22行:根据hpi_cmd的值发送具体的命令MMC_STOP_TRANSMISSION(CMD12):MMC_SEND_STATUS(CMD13);
后面的代码:返回命令发送后的状态;
回到mmc_interrupt_hpi函数:
第52行及以后:等待设备切换出编程状态,当然这里设置了超时,如果在超时范围内没有切换出来,则退出。
那么mmc_interrupt_hpi函数讲完了,该函数主要就是向设备发送命令,让设备退出编程状态;
回到mmc_stop_bkops函数:
第21--24行:如果前面的hpi执行成功,则设置当前为非块操作状态。
那么mmc_interrupt_hpi函数讲完了,该函数让设备退出编程状态,并设置块操作状态;
回到_mmc_suspend函数:
第21行:调用mmc_cache_ctrl函数,将cache中的数据写到设备上,代码如下:
/* * Turn the cache ON/OFF. * Turning the cache OFF shall trigger flushing of the data * to the non-volatile storage. * This function should be called with host claimed */ int mmc_cache_ctrl(struct mmc_host *host, u8 enable) { struct mmc_card *card = host->card; unsigned int timeout; int err = 0; if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) || mmc_card_is_removable(host)) return err; if (card && mmc_card_mmc(card) && (card->ext_csd.cache_size > 0)) { enable = !!enable; if (card->ext_csd.cache_ctrl ^ enable) { timeout = enable ? card->ext_csd.generic_cmd6_time : 0; err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL, enable, timeout); if (err) pr_err("%s: cache %s error %d\n", mmc_hostname(card->host), enable ? "on" : "off", err); else card->ext_csd.cache_ctrl = enable; } } return err; }注释:切换缓存开关;切换缓存开关到关闭状态,引发数据写到非易失性存储上。
第13--15行:如果设备部支持缓存,或者卡是可移动设备(emmc是非可移动设备),则返回;
第17--18行:如果设备存在,是emmc且缓存大小大于0,则执行下面的代码,否则跳到最后返回;
第21--31行:调用mmc_switch函数(CMD6命令),并将EXT_CSD_CACHE_CTRL作为命令参数,来关闭缓存开关(插入的参数enable为0),从而触发数据从缓存到设备的操作。
mmc_cache_ctrl函数讲完,回到_mmc_suspend函数:
第25--27行:如果卡是上电状态并且还没被挂载,则设置为下电状态(不涉及命令的发送,host状态设置),否则执行下面的代码:
第28--29行:如果设备不是休眠状态,则发送MMC_SLEEP_AWAKE(CMD5)命令设置休眠,否则执行下面的代码:
第30--31行:如果不是spi模式(emmc是sdio模式),则通过发送MMC_SELECT_CARD(CMD7)删除卡。
第32行:取消高速模式;
第35行:调用mmc_power_off来设置设备的掉电,代码如下:
void mmc_power_off(struct mmc_host *host) { if (host->ios.power_mode == MMC_POWER_OFF) return; mmc_host_clk_hold(host); host->ios.clock = 0; host->ios.vdd = 0; if (!mmc_host_is_spi(host)) { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; } host->ios.power_mode = MMC_POWER_OFF; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); /* * Some configurations, such as the 802.11 SDIO card in the OLPC * XO-1.5, require a short delay after poweroff before the card * can be successfully turned on again. */ mmc_delay(1); mmc_host_clk_release(host); }
第8--17行:设置参数关闭时钟,关闭电压,设置总线模式为open drain,关闭片选,关闭电源,设置总线宽度等;
第18行:调用mmc_set_ios,通过修改ocr寄存器的方式来执行上面的设置,这个函数在本文的第9节有提到。
那么mmc_power_off函数就讲完了,该函数用来对设备进行掉电的操作。
回到_mmc_suspend函数:
第36行:设置卡状态为挂载,不设计命令的发送;
那么_mmc_suspend函数讲完了,实现流程如下:
1、如果设备是挂载状态,则直接返回,否则执行下面的流程:
2、如果设备当前正在执行块请求操作,则发送停止块请求操作(停止编程操作),会涉及到以下命令:
MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
MMC_SWITCH 6 /* ac [31:0] See below R1b */
MMC_SELECT_CARD 7 /* ac [31:16] RCA
#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
3、将缓存数据写到卡上,发送MMC_SWITCH 命令。
4、根据emmc的当前状态,可能会发送CMD6设置host状态为poweroff状态,或者是发送CMD1命令进入休眠,或者是发送CMD7命令设置host与card的断开;
5、通过设置设备的ocr寄存器设置设备断电。
6、设置设备为挂载状态。
那么我们本节研究的核心函数mmc_shutdown研究完了。
回到第三节的device_shutdown函数:
第45--48行:如果设备没有总线关闭函数,但是有驱动关闭函数,则执行驱动关闭函数,我们来到emmc驱动模块的block.c.通过以下结构体可以知道,驱动的shutdown函数指针指向的是mmc_blk_shutdown。
static struct mmc_driver mmc_driver = { .drv = { .name = "mmcblk", }, .probe = mmc_blk_probe, .remove = mmc_blk_remove, .suspend = mmc_blk_suspend, .resume = mmc_blk_resume, .shutdown = mmc_blk_shutdown, };mmc_blk_shutdown的实现如下:
static void mmc_blk_shutdown(struct mmc_card *card) { _mmc_blk_suspend(card); }
static int _mmc_blk_suspend(struct mmc_card *card) { struct mmc_blk_data *part_md; struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); } } return 0; }
/** * mmc_queue_suspend - suspend a MMC request queue * @mq: MMC queue to suspend * * Stop the block request queue, and wait for our thread to * complete any outstanding requests. This ensures that we * won't suspend while a request is being processed. */ void mmc_queue_suspend(struct mmc_queue *mq) { struct request_queue *q = mq->queue; unsigned long flags; if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { mq->flags |= MMC_QUEUE_SUSPENDED; spin_lock_irqsave(q->queue_lock, flags); blk_stop_queue(q); spin_unlock_irqrestore(q->queue_lock, flags); down(&mq->thread_sem); } }通过代码可以知道,这里只是将消息对列挂载起来,暂时不执行,并没有发送什么命令。
我们回到第三节的device_shutdown函数,回顾一系发现,该函数已经讲完,该函数依赖于总线和驱动的shutdown函数的实现。
回到第二节kernel_restart_prepare函数,也分析完毕;
回到第一节的kernel_restart函数:
第12行:跟cpu相关,略过;
第13行:调用syscore_shutdown关闭注册到系统内核的一些模块,代码如下。
十二、syscore_shutdown(分析发现,跟emmc没有大的关系,没有发送CMD命令)
/** * syscore_shutdown - Execute all the registered system core shutdown callbacks. */ void syscore_shutdown(void) { struct syscore_ops *ops; mutex_lock(&syscore_ops_lock); list_for_each_entry_reverse(ops, &syscore_ops_list, node) if (ops->shutdown) { if (initcall_debug) pr_info("PM: Calling %pF\n", ops->shutdown); ops->shutdown(); } mutex_unlock(&syscore_ops_lock); }第10行:这里遍历了注册到syscore_ops_list链表的每一个syscore_ops;
struct syscore_ops { struct list_head node; int (*suspend)(void); void (*resume)(void); void (*shutdown)(void); };
而通过搜索可以发现,如果想要将模块注入到syscore_ops_list链表,必须要调用:
/** * register_syscore_ops - Register a set of system core operations. * @ops: System core operations to register. */ void register_syscore_ops(struct syscore_ops *ops) { mutex_lock(&syscore_ops_lock); list_add_tail(&ops->node, &syscore_ops_list); mutex_unlock(&syscore_ops_lock); }进而,我们全局搜索register_syscore_ops函数,在mmc模块中只有s3c系列的host才调用了该函数,我们挑S3c2410.c进行分析。
int __init s3c2410_init(void) { printk("S3C2410: Initialising architecture\n"); #ifdef CONFIG_PM register_syscore_ops(&s3c2410_pm_syscore_ops); register_syscore_ops(&s3c24xx_irq_syscore_ops); #endif return device_register(&s3c2410_dev); }这里,将s3c2410_pm_syscore_ops和s3c24xx_irq_syscore_ops这两个syscore_ops变量注册到了内核,看下这两个变量的实现。
struct syscore_ops s3c2410_pm_syscore_ops = { .resume = s3c2410_pm_resume, };
struct syscore_ops s3c24xx_irq_syscore_ops = { .suspend = s3c24xx_irq_suspend, .resume = s3c24xx_irq_resume, };这两个变量并没有实现shutdown函数,但是考虑到后面我们在分析休眠和唤醒的时候,可能还要分析resume或者suspend函数,所以在这里先看下这些函数的实现。
先看s3c2410_pm_syscore_ops的resume函数:
static void s3c2410_pm_resume(void) { unsigned long tmp; /* unset the return-from-sleep flag, to ensure reset */ tmp = __raw_readl(S3C2410_GSTATUS2); tmp &= S3C2410_GSTATUS2_OFFRESET; __raw_writel(tmp, S3C2410_GSTATUS2); if (machine_is_aml_m5900()) { gpio_request_one(S3C2410_GPF(2), GPIOF_OUT_INIT_LOW, NULL); gpio_free(S3C2410_GPF(2)); } }应该是设置IO空间的相关寄存器的值,并没有发送命令。我看不懂。
再来看下s3c24xx_irq_syscore_ops的resume函数,还是看不懂,看懂的要告诉我哦:
static void s3c24xx_irq_resume(void) { unsigned int i; for (i = 0; i < ARRAY_SIZE(save_extint); i++) __raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4)); for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) __raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4)); s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); __raw_writel(save_eintmask, S3C24XX_EINTMASK); }再看起suspend函数,我还是不懂,不分析:
static int s3c24xx_irq_suspend(void) { unsigned int i; for (i = 0; i < ARRAY_SIZE(save_extint); i++) save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4)); for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4)); s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); save_eintmask = __raw_readl(S3C24XX_EINTMASK); return 0; }
好,回到第一节的kernel_restart函数:
后面的kmsg_dump和machine_restart跟存储设备没有关系,而且太复杂,暂时还没搞懂,就不说了。
分析错误了的和因为能力问题没分析到的,还请大家补充,写个博客,贴个链接给我,让我学习学习,先谢谢喽。
相关文章推荐
- 【Linux操作系统分析】设备驱动处理流程
- 嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
- 【Linux操作系统分析】设备驱动处理流程
- 【Linux操作系统分析】设备驱动处理流程
- Linux 关机重启流程分析
- Linux下usb驱动调用流程
- Linux内核中断处理流程
- Linux设备驱动编程之中断处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理
- linux中断导读之--处理流程
- Linux关机重启流程分析
- Linux的关机流程
- Linux发生问题怎么处理啊?建议流程是这样...[鸟哥的Linux私房菜]
- Linux的声卡驱动中ALSA与OSS的区别和简单流程介
- 使用 Anacron 处理 Linux 关机问题
- Linux 关机重启流程分析
- linux for arm的中断处理流程[转载自:http://hi.baidu.com/wudx05/blog/item/5314935c834f4e41fbf2c0dc.html]
- Linux 关机重启流程分析
- Linux2.4内核和2.6内核对Initrd处理流程
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理