基于335X的UBOOT网口驱动分析
一、软硬件平台资料
1、 开发板:创龙AM3359核心板,网口采用RMII形式
2、 UBOOT版本:U-Boot-2016.05,采用FDT和DM。
参考链接:
https://blog.csdn.net/hahachenchen789/article/details/53339181
二、网口相关代码位置
1、 网口的PINMUX设置
RMII接口的相关PINMUX在MLO中进行设置,具体的设置代码为
|-board_init_f
|-board_early_init_f
|-set_mux_conf_regs
|-enable_board_pin_mux
configure_module_pin_mux(rmii1_pin_mux);
2、DTS文件中的CPSW的配置
&cpsw_emac0 {
phy_id = <&davinci_mdio>, <0x12>; //phy_id【1】为初始的phy_addr,为SW的PORT2口的ADDR。
phy-mode = "rmii"; //RMII 模式
};
&mac { //未使用此处配置
slaves = <1>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cpsw_default>;
pinctrl-1 = <&cpsw_sleep>;
status = "okay";
};
&phy_sel {
rmii-clock-ext; //RMII模式的时钟为外部时钟
};
&davinci_mdio { //未使用此处配置
pinctrl-names = "default", "sleep";
pinctrl-0 = <&davinci_mdio_default>;
pinctrl-1 = <&davinci_mdio_sleep>;
status = "okay";
reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
reset-delay-us = <2>; /* PHY datasheet states 1uS min */
};
3、 网口的初始化设置
网口的初始化在UBOOT中进行,具体设置代码为
|-board_init_r
|-init_sequence_r
|-initr_net
|- eth_initialize (eth-uclass.c)
三、有关网口的DM&FDT分析
1、 驱动实现方式
此版本的UBOOT中使用了FDT文件进行外设的相关配置,驱动模型使用了DM方式,有关FDT以及DM相关的知识请参考如下文章
https://blog.csdn.net/ooonebook/article/details/53206623
https://blog.csdn.net/ooonebook/article/details/53234020
2、 UBOOT中DM初始化
DM的初始化
.创建根设备root的udevice,存放在gd->dm_root中。
.根设备其实是一个虚拟设备,主要是为uboot的其他设备提供一个挂载点。
.初始化uclass链表gd->uclass_root
DM中udevice和uclass的解析
.udevice的创建和uclass的创建
.udevice和uclass的绑定
.uclass_driver和uclass的绑定
.driver和udevice的绑定
.部分driver函数的调用
(1)DM初始化调用过程
dm初始化的接口在dm_init_and_scan中。 可以发现在uboot relocate之前的initf_dm和之后的initr_dm都调用了这个函数。
static int initf_dm(void)
{
#if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN)
int ret;
ret = dm_init_and_scan(true); // 调用dm_init_and_scan对DM进行初始化和设备的解析
if (ret)
return ret;
#endif
return 0;
}
#ifdef CONFIG_DM
static int initr_dm(void)
{
int ret;
/* Save the pre-reloc driver model and start a new one */
gd->dm_root_f = gd->dm_root; // 存储relocate之前的根设备
gd->dm_root = NULL;
ret = dm_init_and_scan(false); // 调用dm_init_and_scan对DM进行初始化和设备的解析
if (ret)
return ret;
return 0;
}
#endif
主要区别在于参数。
首先说明一下dts节点中的“u-boot,dm-pre-reloc”属性,当设置了这个属性时,则表示这个设备在relocate之前就需要使用。
当dm_init_and_scan的参数为true时,只会对带有“u-boot,dm-pre-reloc”属性的节点进行解析。而当参数为false的时候,则会对所有节点都进行解析。
由于“u-boot,dm-pre-reloc”的情况比较少,所以这里只学习参数为false的情况。也就是initr_dm里面的dm_init_and_scan(false);。
dm_init_and_scan(driver/core/root.c)说明
int dm_init_and_scan(bool pre_reloc_only)
{
int ret;
ret = dm_init(); // DM的初始化
if (ret) {
debug("dm_init() failed: %d\n", ret);
return ret;
}
ret = dm_scan_platdata(pre_reloc_only); // 从平台设备中解析udevice和uclass
if (ret) {
debug("dm_scan_platdata() failed: %d\n", ret);
return ret;
}
if (CONFIG_IS_ENABLED(OF_CONTROL)) {
ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); // 从dtb中解析udevice和uclass
if (ret) {
debug("dm_scan_fdt() failed: %d\n", ret);
return ret;
}
}
ret = dm_scan_other(pre_reloc_only);
if (ret)
return ret;
return 0;
}
DM的初始化—dm_init(driver/core/root.c)
#define DM_ROOT_NON_CONST (((gd_t *)gd)->dm_root) // 宏定义根设备指针gd->dm_root
#define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root) // 宏定义gd->uclass_root,uclass的链表
int dm_init(void)
{
int ret;
if (gd->dm_root) {
// 根设备已经存在,说明DM已经初始化过了
dm_warn("Virtual root driver already exists!\n");
return -EINVAL;
}
INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);
// 初始化uclass链表
ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
// DM_ROOT_NON_CONST是指根设备udevice,root_info是表示根设备的设备信息
// device_bind_by_name会查找和设备信息匹配的driver,然后创建对应的udevice和uclass并进行绑定,最后放在DM_ROOT_NON_CONST中。
// device_bind_by_name后续我们会进行说明,这里我们暂时只需要了解root根设备的udevice以及对应的uclass都已经创建完成。
if (ret)
return ret;
#if CONFIG_IS_ENABLED(OF_CONTROL)
DM_ROOT_NON_CONST->of_offset = 0;
#endif
ret = device_probe(DM_ROOT_NON_CONST);
// 对根设备执行probe操作,
// device_probe后续再进行说明
if (ret)
return ret;
return 0;
}
这里就完成的DM的初始化了
1)创建根设备root的udevice,存放在gd->dm_root中。
2)初始化uclass链表gd->uclass_root
(2)从平台设备中解析udevice和uclass—dm_scan_platdata(不涉及)
(3)从dtb中解析udevice和uclass——dm_scan_fdt
对应代码如下driver/core/root.c
int dm_scan_fdt(const void *blob, bool pre_reloc_only)
// 此时传进来的参数blob=gd->fdt_blob, pre_reloc_only=0
{
return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);
// 直接调用dm_scan_fdt_node
}
int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
bool pre_reloc_only)
// 此时传进来的参数
// parent=gd->dm_root,表示以root设备作为父设备开始解析
// blob=gd->fdt_blob,指定了对应的dtb
// offset=0,从偏移0的节点开始扫描
// pre_reloc_only=0,不只是解析relotion之前的设备
{
int ret = 0, err;
/* 以下步骤相当于是遍历每一个dts节点并且调用lists_bind_fdt对其进行解析 */
for (offset = fdt_first_subnode(blob, offset);
// 获得blob设备树的offset偏移下的节点的第一个子节点
offset > 0;
offset = fdt_next_subnode(blob, offset)) {
// 循环查找下一个子节点
if (!fdtdec_get_is_enabled(blob, offset)) {
// 判断节点状态是否是disable,如果是的话直接忽略
dm_dbg(" - ignoring disabled device\n");
continue;
}
err = lists_bind_fdt(parent, blob, offset, NULL);
// 解析绑定这个节点,dm_scan_fdt的核心,下面具体分析
if (err && !ret) {
ret = err;
debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL),
ret);
}
}
return ret;
}
lists_bind_fdt是从dtb中解析udevice和uclass的核心。
其具体实现如下: driver/core/lists.c
int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
struct udevice **devp)
// parent指定了父设备,通过blob和offset可以获得对应的设备的dts节点,对应udevice结构通过devp返回
{
struct driver *driver = ll_entry_start(struct driver, dr