linux关机时emmc驱动处理流程
关于android的重启,推荐一篇博文:x86平台下Android系统的Linux部分的重启分析。
由于我的工作与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_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;
}
后面的kmsg_dump和machine_restart跟存储设备没有关系,而且太复杂,暂时还没搞懂,就不说了。
分析错误了的和因为能力问题没分析到的,还请大家补充,写个博客,贴个链接给我,让我学习学习,先谢谢喽。
更多推荐
所有评论(0)