您的位置:首页 > 运维架构 > Linux

linux关机时emmc驱动处理流程

2016-08-05 09:53 363 查看
关于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_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跟存储设备没有关系,而且太复杂,暂时还没搞懂,就不说了。

分析错误了的和因为能力问题没分析到的,还请大家补充,写个博客,贴个链接给我,让我学习学习,先谢谢喽。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: