emmc检测及初始化

您所在的位置:网站首页 emmc如何测试 emmc检测及初始化

emmc检测及初始化

2023-12-24 02:13| 来源: 网络整理| 查看: 265

本文将基于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] 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初始化完毕了。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3