文章目錄
前奏
流程
驅動匹配
分析需要的屬性
調試
補充
結果
前奏
隨著u-boot版本的變遷,u-boot的功能也越來越多越來越強大,在較新的u-boot中已經支持很多不同類型的文件系統,告別了以前需要移植者自己手動規劃eMMC/SD等鏡像存儲介質的分區信息(并非真正的磁盤分區,只是一個標號而已),可直接使用相關的命令就可以讀取出各種文件系統類型的存儲介質中的文件,使燒錄、更新、啟動等操作變得更加方便,所以在u-boot中支持eMMC/SD卡是很有必要的。
流程
在我現在用的u-boot版本2020.04中mmc相關的驅動默認使用了驅動模型,為了不破壞原有的結構,所以也決定使用驅動模型進行移植調試。通過閱讀源碼發現mmc相關的driver與uclass_driver都已經有了,唯一缺的就是driver_info(這里需要了解dm驅動模型相關的知識),驅動模型建議使用設備樹傳遞driver_info,但不幸的是三星(什么東西都藏著掖著)并沒有提供我這塊板子mmc相關的設備樹參考,便只好再次閱讀源碼,下面跟著源碼重點分析一下,該怎么從源碼中提取設備樹必須提供的屬性。
uclass_driver:
/*drivers\mmc\mmc-uclass.c*/
UCLASS_DRIVER(mmc) = {
.id = UCLASS_MMC,
.name = "mmc",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct mmc_uclass_priv),
};
driver:
/*drivers\mmc\s5p_sdhci.c*/
U_BOOT_DRIVER(s5p_sdhci_drv) = {
.name = "s5p_sdhci",
.id = UCLASS_MMC,
.of_match = s5p_sdhci_ids,
.bind = s5p_sdhci_bind,
.ops = &sdhci_ops,
.probe = s5p_sdhci_probe,
.priv_auto_alloc_size = sizeof(struct sdhci_host),
.platdata_auto_alloc_size = sizeof(struct s5p_sdhci_plat),
};
本文略過dm驅動模型相關的內容
驅動匹配
在u-boot啟動過程中會有一個加載并驅動匹配的過程,當驅動匹配的時候會執行對應的probe函數,這個probe函數就是本次的重點,這里對應s5p_sdhci_probe函數。
那怎么才能讓驅動知道該和誰進行匹配呢?
這里就要看其中的of_match參數了,下面是s5p對應的of_match參數:
static const struct udevice_id s5p_sdhci_ids[] = {
{ .compatible = "samsung,exynos4412-sdhci"},
{ }
};
從這里我們知道首先得定義一個compatible屬性,并指定其值為samsung,exynos4412-sdhci,如下:
/*如果不懂,可先了解一下設備樹相關的知識*/
sdhci0:sdhci@eb000000 {
compatible = "samsung,exynos4412-sdhci";
};
這步完了后,驅動和數據就能匹配了,但是我們還需要其它參數,接著往下看。
分析需要的屬性
我們看一下s5p_sdhci_probe函數:
static int s5p_sdhci_probe(struct udevice *dev)
{
/*
省略
*/
/*這個函數就是從設備樹解析我們想要的數據*/
ret = sdhci_get_config(gd->fdt_blob, dev_of_offset(dev), host);
if (ret)
return ret;
/*
省略
*/
}
從源碼知道了sdhci_get_config函數中會從設備樹獲取相關的數據,看一下到底哪些數據是必須的:
static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
{
int bus_width, dev_id;
unsigned int base;
/* Get device id */
dev_id = pinmux_decode_periph_id(blob, node);
if (dev_id < PERIPH_ID_SDMMC0 || dev_id > PERIPH_ID_SDMMC3) {
debug("MMC: Can't get device id\n");
return -EINVAL;
}
host->index = dev_id - PERIPH_ID_SDMMC0;
/* Get bus width */
bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
if (bus_width <= 0) {
debug("MMC: Can't get bus-width\n");
return -EINVAL;
}
host->bus_width = bus_width;
/* Get the base address from the device node */
base = fdtdec_get_addr(blob, node, "reg");
if (!base) {
debug("MMC: Can't get base address\n");
return -EINVAL;
}
host->ioaddr = (void *)base;
gpio_request_by_name_nodev(offset_to_ofnode(node), "pwr-gpios", 0,
&host->pwr_gpio, GPIOD_IS_OUT);
gpio_request_by_name_nodev(offset_to_ofnode(node), "cd-gpios", 0,
&host->cd_gpio, GPIOD_IS_IN);
return 0;
}
經過分析,我們需要的參數如下:
設備id
總線寬度
寄存器基地址
電源引腳(可選)
卡檢測引腳(可選)
所以可以推導出設備樹如下:
sdhci0:sdhci@eb000000 {
compatible = "samsung,exynos4412-sdhci";
/*根據芯片手冊得知*/
reg = <0xeb000000 0x100000>;
/*根據硬件原理圖得知*/
samsung,bus-width = <8>;
/*設備id定義于arch/arm/mach-s5pc1xx/include/mach/periph.h*/
id = <75>;
/*通道0是eMMC不可熱插拔*/
non-removable;
/*使能該設備*/
status = "okay";
};
調試
經過上面的步驟,driver_info也就準備好了,但經過測試,啟動u-boot后還是probe失敗,調了好久沒辦法,只能開啟調試信息看輸出log,最后定位到讀取id失敗,明明設置了id為什么還是失敗。看一下id讀取函數:
/*drivers\mmc\s5p_sdhci.c*/
int pinmux_decode_periph_id(const void *blob, int node)
{
return 0;
}
找到問題了,原來是id讀取函數是空的,之前也沒有仔細把每個函數都看一遍,導致在這里卡了很久,修改成下面的代碼,從設備樹中獲取id即可:
/*drivers\mmc\s5p_sdhci.c*/
int pinmux_decode_periph_id(const void *blob, int node)
{
return fdtdec_get_int(blob, node, "id", 0);
}
補充
另外再補充一個關于SD卡的問題,當你發現SD卡探測成功,但無論如何都檢測不到卡的時候,不妨試試通過menuconfig將CONFIG_MMC_BROKEN_CD配置宏打開,其作用是使用輪詢的方式來檢測卡,有時候檢測不到卡可能是因為卡檢測引腳沒有被正確的配置,使用輪詢方式檢測就可以了:
Device Drivers --->
MMC Host controller Support --->
[*] Poll for broken card detection case
結果
修改后再次測試已經能夠正確的讀取到eMMC并且各種操作也沒有問題。
歡迎掃碼關注我的微信公眾號