emmc检测及初始化

来源:互联网 发布:淘宝双11全天交易额 编辑:程序博客网 时间:2024/06/12 21:56

本文将基于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_DEBUGunsigned 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));} elsestatus = !!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_DEBUGpr_info("%s: %s: trying to init card at %u Hz\n",mmc_hostname(host), __func__, host->f_init);#endifmmc_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初始化完毕了。














0 0
原创粉丝点击