emmc检测及初始化

本文将基于emmc驱动来描述系统是如何检测到emmc设备,并进行初始化操作的。

一、中断处理函数mmci_cd_irq

在前面关于mmc驱动的系列文章emmc/sd host层解析中有关于mmci的的分析,在文章中有分析过一个名为mmci_probe的函数,该函数比较长,这里就不完全贴出来了,只贴出跟emmc检测相关的代码,如下:

/*
		 * A gpio pin that will detect cards when inserted and removed
		 * will most likely want to trigger on the edges if it is
		 * 0 when ejected and 1 when inserted (or mutatis mutandis
		 * for the inverted case) so we request triggers on both
		 * edges.
		 */
		ret = request_any_context_irq(gpio_to_irq(plat->gpio_cd),
				mmci_cd_irq,
				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
				DRIVER_NAME " (cd)", host);
注释说:一个gpio的pin脚会检测卡的插入和移除。

这段代码是注册一个中断号对应的中断函数,而这个中断就是对应卡插入或者移除的。

下面来看一下中断函数的实现(mmci.c):

static irqreturn_t mmci_cd_irq(int irq, void *dev_id)
{
	struct mmci_host *host = dev_id;


	mmc_detect_change(host->mmc, msecs_to_jiffies(500));


	return IRQ_HANDLED;
}
里面调用了mmc_detect_change(core/core.c)函数来检测拔插变化,实现如下:

/**
 *	mmc_detect_change - process change of state on a MMC socket
 *	@host: host which changed state.
 *	@delay: optional delay to wait before detection (jiffies)
 *
 *	MMC drivers should call this when they detect a card has been
 *	inserted or removed. The MMC layer will confirm that any
 *	present card is still functional, and initialize any newly
 *	inserted.
 */
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
	_mmc_detect_change(host, delay, true);
}
再看_mmc_detect_change(core.c):

static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
				bool cd_irq)
{
#ifdef CONFIG_MMC_DEBUG
	unsigned long flags;
	spin_lock_irqsave(&host->lock, flags);
	WARN_ON(host->removed);
	spin_unlock_irqrestore(&host->lock, flags);
#endif


	/*
	 * If the device is configured as wakeup, we prevent a new sleep for
	 * 5 s to give provision for user space to consume the event.
	 */
	if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
		device_can_wakeup(mmc_dev(host)))
		pm_wakeup_event(mmc_dev(host), 5000);


	host->detect_change = 1;
	mmc_schedule_delayed_work(&host->detect, delay);
}
这里,重点看下最后一行代码。

二、mmc_schedule_delayed_work(&host->detect, delay)

mmc_schedule_delayed_work(&host->detect, delay);
这里延时调度了一个工作队列,再展开看一下:

/*
 * Internal function. Schedule delayed work in the MMC work queue.
 */
static int mmc_schedule_delayed_work(struct delayed_work *work,
				     unsigned long delay)
{
	return queue_delayed_work(workqueue, work, delay);
}
从传递的参数类型我们可以知道,host->detect是个delayed_work类型的变量,那么它是在什么时候被赋值的呢?

这个我们得从mmci_probe函数说起,该函数中有这样一段代码:

mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
而mmc_alloc_host函数中有如下代码:

INIT_DELAYED_WORK(&host->detect, mmc_rescan);
这里将mmc_rescan处理函数赋值给了detect下的工作队列处理函数指针,也就是说如果以后调度detect队列时,就会使用mmc_rescan函数去处理。

也就是说mmc_schedule_delayed_work实际调用了mmc_rescan函数。
三、mmc_rescan

void mmc_rescan(struct work_struct *work)
{
	struct mmc_host *host =
		container_of(work, struct mmc_host, detect.work);
	int i;

	if (host->rescan_disable)
		return;

	/* If there is a non-removable card registered, only scan once */
	if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
		return;
	host->rescan_entered = 1;

	mmc_bus_get(host);

	/*
	 * if there is a _removable_ card registered, check whether it is
	 * still present
	 */
	if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
	    && !(host->caps & MMC_CAP_NONREMOVABLE))
		host->bus_ops->detect(host);

	host->detect_change = 0;

	/*
	 * Let mmc_bus_put() free the bus/bus_ops if we've found that
	 * the card is no longer present.
	 */
	mmc_bus_put(host);
	mmc_bus_get(host);

	/* if there still is a card present, stop here */
	if (host->bus_ops != NULL) {
		mmc_bus_put(host);
		goto out;
	}

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

	if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
		mmc_claim_host(host);
		mmc_power_off(host);
		mmc_release_host(host);
		goto out;
	}

	mmc_claim_host(host);
	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
			break;
		if (freqs[i] <= host->f_min)
			break;
	}
	mmc_release_host(host);

 out:
	if (host->caps & MMC_CAP_NEEDS_POLL)
		mmc_schedule_delayed_work(&host->detect, HZ);
}
第46行之前,自己看注释;主要是将host的bus_ops置空;

第46行,调用了host的ops的get_cd函数指针对应的函数,在这里就不分析ops指向了谁,直接上函数指针对应的函数(mmci.c):

static int mmci_get_cd(struct mmc_host *mmc)
{
	struct mmci_host *host = mmc_priv(mmc);
	struct mmci_platform_data *plat = host->plat;
	unsigned int status;

	if (host->gpio_cd == -ENOSYS) {
		if (!plat->status)
			return 1; /* Assume always present */

		status = plat->status(mmc_dev(host->mmc));
	} else
		status = !!gpio_get_value_cansleep(host->gpio_cd)
			^ plat->cd_invert;

	/*
	 * Use positive logic throughout - status is zero for no card,
	 * non-zero for card inserted.
	 */
	return status;
}
从最后的注释可以知道,如果函数返回0表示没有卡插入,如果返回非0表示有卡插入。

回到mmc_rescan的第46行,如果此时没有卡则进入if语句中,关闭电源并退出,否则执行下面的流程。

第54--59行:这里出现了一个名为freqs的数组,先看下它的定义:

static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
这个应该是一个时钟频率数组。

看第55行的mmc_rescan_try_freq:

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

#ifdef CONFIG_MMC_DEBUG
	pr_info("%s: %s: trying to init card at %u Hz\n",
		mmc_hostname(host), __func__, host->f_init);
#endif
	mmc_power_up(host, host->ocr_avail);

	/*
	 * Some eMMCs (with VCCQ always on) may not be reset after power up, so
	 * do a hardware reset if possible.
	 */
	mmc_hw_reset_for_init(host);

	/*
	 * sdio_reset sends CMD52 to reset card.  Since we do not know
	 * if the card is being re-initialized, just send it.  CMD52
	 * should be ignored by SD/eMMC cards.
	 */
	sdio_reset(host);
	mmc_go_idle(host);

	mmc_send_if_cond(host, host->ocr_avail);

	/* Order's important: probe SDIO, then SD, then MMC */
	if (!mmc_attach_sdio(host))
		return 0;
	if (!mmc_attach_sd(host))
		return 0;
	if (!mmc_attach_mmc(host))
		return 0;

	mmc_power_off(host);
	return -EIO;
}
第9行:给设备上电;

第15行:给emmc设备硬件重置;

第22行:如果是sdio重置;

第23行:发送CMD0命令,设置idle状态;

第25行:发送SD_SEND_IF_COND(SD卡的CMD8)命令,判断当前工作电压是否符合要求。emmc不会响应。

第28行:内部通过发送SD_IO_SEND_OP_COND(CMD5)检测是否为sdio设备,是调用mmc_sdio_init_card进行初始化;

第30行:内部通过发送SD_APP_OP_COND(CMD41)检测是否为sd设备,是调用mmc_sd_init_card进行初始化;

第32行:内部通过发送MMC_SEND_OP_COND(CMD1)检测是否为emmc设备,是则调用mmc_init_card函数进行初始化,关于mmc_init_card函数在linux关机时emmc驱动处理流程文章的第十节有详细讲解,有兴趣的自行阅读

如果上面的三种检测操作有一个操作成功就直接返回0。如果不 成功则执行第35行的下电操作,并返回非0.


到此我们的emmc初始化完毕了。














  • 1
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值