linux裝置模型之mmc,sd子系統
本文系本站原創,歡迎轉載!
轉載請註明出處:http://blog.csdn.net/gdt_a20
####看一下重要的卡掃描函式,mmc_rescan,卡就指著他
####活著呢,
/driver/mmc/core/core.c
void mmc_rescan(struct work_struct *work)
{
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; //掃描試驗的頻率段
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
if (host->rescan_disable) //disable 直接返回
return;
mmc_bus_get(host); //增加bus引用計數
/*
* 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); //存在熱插拔卡,不包括emmc,呼叫探測函式
/*
* 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) //有卡,退出
goto out;
mmc_claim_host(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);
}
#####
/**
* mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
*
* Claim a host for a set of operations.
*/
static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL);
}
#####
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @abort: whether or not the operation should be aborted
*
* Claim a host for a set of operations. If @abort is non null and
* dereference a non-zero value then this will return prematurely with
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
DECLARE_WAITQUEUE(wait, current); //定義一個等待佇列
unsigned long flags;
int stop;
might_sleep(); //排程點
add_wait_queue(&host->wq, &wait);
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current)
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
spin_lock_irqsave(&host->lock, flags);
} //只有claim為空的時候還會跳出迴圈,否則就一直等待釋放
set_current_state(TASK_RUNNING);
if (!stop) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
if (!stop)
mmc_host_enable(host); //使能該host
return stop;
}
#####
mmc_host_enable函式可以做一些底電流工作
#####
/**
* mmc_host_enable - enable a host.
* @host: mmc host to enable
*
* Hosts that support power saving can use the 'enable' and 'disable'
* methods to exit and enter power saving states. For more information
* see comments for struct mmc_host_ops.
*/
int mmc_host_enable(struct mmc_host *host)
{
if (!(host->caps & MMC_CAP_DISABLE))
return 0;
if (host->en_dis_recurs)
return 0;
if (host->nesting_cnt++)
return 0;
cancel_delayed_work_sync(&host->disable);
if (host->enabled)
return 0;
if (host->ops->enable) {
int err;
host->en_dis_recurs = 1;
err = host->ops->enable(host);
host->en_dis_recurs = 0;
if (err) {
pr_debug("%s: enable error %d\n",
mmc_hostname(host), err);
return err;
}
}
host->enabled = 1;
return 0;
}
#####
重點的卡探測函式,
#####
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); //電源啟用
/*
* 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); //針對emmc的硬體reset
/*
* 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); //這部分與sd協議相關,可以結合前面內容來看
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;
}
####在卡型別探測初始化過程中,重要的一個環節是和塊裝置的關聯,這部分程式碼大致相同,
####例如mmc_attach_sd中,會有,
/drivers/mmc/core/sd.c
/*
* Starting point for SD card init.
*/
int mmc_attach_sd(struct mmc_host *host)
{
int err;
u32 ocr;
BUG_ON(!host);
WARN_ON(!host->claimed);
/* Make sure we are at 3.3V signalling voltage */
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false);
if (err)
return err;
/* Disable preset value enable if already set since last time */
if (host->ops->enable_preset_value)
host->ops->enable_preset_value(host, false);
err = mmc_send_app_op_cond(host, 0, &ocr);
if (err)
return err;
mmc_sd_attach_bus_ops(host); //重要,bus操作的繫結
if (host->ocr_avail_sd)
host->ocr_avail = host->ocr_avail_sd;
/*
* We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
mmc_go_idle(host);
err = mmc_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
if ((ocr & MMC_VDD_165_195) &&
!(host->ocr_avail_sd & MMC_VDD_165_195)) {
pr_warning("%s: SD card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}
host->ocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr) {
err = -EINVAL;
goto err;
}
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, 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);
host->card = NULL;
mmc_claim_host(host);
err:
mmc_detach_bus(host);
pr_err("%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err);
return err;
}
#######首先是繫結操作mmc_sd_attach_bus_ops
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
{
const struct mmc_bus_ops *bus_ops;
if (!mmc_card_is_removable(host)) //emmc和sd區別對待
bus_ops = &mmc_sd_ops_unsafe;
else
bus_ops = &mmc_sd_ops;
mmc_attach_bus(host, bus_ops);
}
####mmc_attach_bus函式會將bus的操作繫結到host上.
####core.c中
/*
* 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);
}
#####再看另外一個函式mmc_add_card,
####這個函式在bus.c 中,
/*
* Register a new MMC card with the driver model.
*/
int mmc_add_card(struct mmc_card *card)
{
int ret;
const char *type;
dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
switch (card->type) {
case MMC_TYPE_MMC:
type = "MMC";
break;
case MMC_TYPE_SD:
type = "SD";
if (mmc_card_blockaddr(card)) {
if (mmc_card_ext_capacity(card))
type = "SDXC";
else
type = "SDHC";
}
break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
case MMC_TYPE_SD_COMBO:
type = "SD-combo";
if (mmc_card_blockaddr(card))
type = "SDHC-combo";
break;
default:
type = "?";
break;
}
if (mmc_host_is_spi(card->host)) {
pr_info("%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_sd_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca);
}
#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
#endif
ret = device_add(&card->dev);
if (ret)
return ret;
mmc_card_set_present(card);
return 0;
}
####關注一下device_add(&card->dev);會把卡作為一個裝置註冊,
####這個註冊會觸發到bus裡的match操作,這部分不理解的可以回顧前面講到的裝置模型,
####這裡的bus對應mmc,呼叫的match函式對應於bus.c中
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_attrs = mmc_dev_attrs,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.suspend = mmc_bus_suspend,
.resume = mmc_bus_resume,
.pm = MMC_PM_OPS_PTR,
};
/*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method.
*/
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
####總為真,那麼直接會觸發bus的probe操作,
####
static int mmc_bus_probe(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
return drv->probe(card);
}
####會呼叫mmc_driver 裡的probe,
####這裡的mmc_driver就指的是block.c中的mmc_driver,
####還是看一下,這個過程吧,
####drivers/mmc/card/block.c
static int __init mmc_blk_init(void)
{
int res;
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
max_devices = 256 / perdev_minors;
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); //註冊mmc塊裝置,這個後面來講
if (res)
goto out;
res = mmc_register_driver(&mmc_driver); //重點是這個
if (res)
goto out2;
return 0;
out2:
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
out:
return res;
}
#####
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,
};
####在bus.c中
/**
* mmc_register_driver - register a media driver
* @drv: MMC media driver
*/
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;
return driver_register(&drv->drv);
}
####bus型別被指定成了mmc,也就是掛到了mmc上,這樣剛才的卡註冊預設全部匹配,
####所以也就會直接觸發block裡的probe,mmc_blk_probe
####下面的東西塊裝置關聯比較多,放到下一章來講
總結:描述了卡的識別階段,並沒有深入到卡的具體探測,這部分結合前面的協議轉換圖就不難理解,這部分主要集中在sd到塊裝置部分的過度
下一篇主要是針對mmc中的塊裝置。
Thanks
相關文章
- Linux下驅動SD/MMC讀卡器(轉)Linux
- Linux的input輸入子系統:裝置驅動之按鍵驅動Linux
- Linux裝置模型(3)Linux模型
- Linux驅動之GPIO子系統和pinctrl子系統Linux
- Linux之Win10 安裝子系統 GUI 介面LinuxWin10GUI
- Linux之Win10-安裝子系統-GUI-介面LinuxWin10GUI
- Linux裝置模型(一) 概覽Linux模型
- Windows 的 Linux 子系統之 Arch LinuxWindowsLinux
- 裝置管理系統AI大模型應用RAG案例AI大模型
- Linux2.6.29裝置模型分析-概述Linux模型
- Linux 2.6核心的裝置模型(轉)Linux模型
- Linux 效能優化之 IO 子系統Linux優化
- Window10安裝linux子系統及子系統安裝1Panel皮膚Linux
- 未來蘋果裝置將移植 Linux 系統蘋果Linux
- Linux裝置驅動之字元裝置驅動Linux字元
- 醫療裝置管理系統-智慧裝置管理系統平臺
- 製作Linux系統SD啟動卡Linux
- Linux系統中如何檢視塊裝置資訊?Linux
- 列舉系統裝置
- linux平臺匯流排驅動裝置模型之點亮LEDLinux模型
- linux核心--子系統Linux
- Linux系統常見的三種裝置分類!Linux
- 通過Linux系統檢視硬體裝置資訊Linux
- 作業系統裝置管理作業系統
- 實踐 | MES系統之裝置管理的基礎功能
- 計算機系統005 - 硬體元件之IO裝置計算機元件
- 深入淺出:Linux裝置驅動之字元裝置驅動Linux字元
- 乾坤合一:Linux裝置驅動之塊裝置驅動Linux
- Win10啟用Linux子系統安裝UbuntuWin10LinuxUbuntu
- win10在任意位置安裝Linux子系統Win10Linux
- Linux中斷子系統Linux
- 蛻變成蝶:Linux裝置驅動之字元裝置驅動Linux字元
- 蛻變成蝶~Linux裝置驅動之字元裝置驅動Linux字元
- 電子裝置推薦
- linux系統中 SElinux安全子系統Linux
- Simulink中封裝子系統封裝
- PLM裝置維修系統技術
- 【AIX 學習】配置系統裝置AI