随着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;
}
经过分析,我们需要的参数如下:
所以可以推导出设备树如下:
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