基于zynq的phy调试记录

ZYNQ系列文章目录

第一章:基于zynq的phy调试记录
第二章:qemu制作ubuntu文件系统



前言

记录zynq调试:

从petalinux的搭建,到uboot、kernel、rootfs的调试、打包


提示:以下是本篇文章正文内容,下面案例可供参考

一、关于PHY的配置

博主基于双网口(ZYNQ7010-PS-RGMII),PHY芯片为RTL8211I-CG。本人5.10的内核,有些地方可能不一致。

1.1 内核配置

内核之下执行

make menuconfig ARCH =arm CROSS_COMPILE=arm-linux-gnueabihf

配置xiilinx自己的macb驱动。

配置本人需要使用的PHY驱动
在这里插入图片描述

附注:打开动态调试开关

CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y

在这里插入图片描述
在这里插入图片描述

动态调试在系统起来之后可以用echo开启动态调试

动态调试的开启

echo  "file macb_main.c +p" > /sys/kernel/debug/dynamic_debug/control

动态调试的关闭

echo  "file macb_main.c -p" > /sys/kernel/debug/dynamic_debug/control

1.2设备树配置

前提:双网口都基于PS的RGMII接口
打开system_user.dtsi zynq-uboot.dts 内核与uboot配置一样

ethernet0 = &gem0;
ethernet1 = &gem1;
&gem0 {
	status = "okay";
	phy-mode = "rgmii-id";
	phy-handle = <&ethernet_phy>;
	local-mac-address = [00 0a 33 00 01 b2]; 
	ethernet_phy: ethernet-phy@7 {
		reg = <7>;
		device_type = "ethernet-phy";
	};
};

&gem1 {
	status = "okay";
	phy-mode = "rgmii-id";
	phy-handle = <&ethernet_phy1>;
l	ocal-mac-address = [00 0d 36 01 01 b2]; 
	ethernet_phy1: ethernet-phy@0 {
		reg = <0>;
		device_type = "ethernet-phy";
	};
};

二、内核中PHY的运行流程

Fixed MDIO Bus初始化(此部分对于xilinx-gem无效)

fixed_mdio_bus_init(void)
|->printk("fixed_phy-init&register miibus\r\n");
|->ret = mdiobus_register(fmb->mii_bus);
	|->phydev = mdiobus_scan(bus, i);//轮询找phy id去匹配
	|->pr_info("%s: probed\n", bus->name); //bus->name为Fixed MDIO Bus

在这里插入图片描述
在这里插入图片描述
正篇开始

1、mdio总线初始化

mdio总线随内核启动初始化–subsys_initcall比module_init先执行

static int __init phy_init(void)
{
	int rc;
	rc = mdio_bus_init();
	if (rc)
		return rc;
	ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops);
	features_init();

	rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);
	if (rc)
		goto err_c45;

	rc = phy_driver_register(&genphy_driver, THIS_MODULE);
	if (rc) {
		phy_driver_unregister(&genphy_c45_driver);
err_c45:
		mdio_bus_exit();
	}

	return rc;
}
subsys_initcall(phy_init);
struct bus_type mdio_bus_type = {
	.name		= "mdio_bus",
	.dev_groups	= mdio_bus_dev_groups,
	.match		= mdio_bus_match,
	.uevent		= mdio_uevent,
};
EXPORT_SYMBOL(mdio_bus_type);

int __init mdio_bus_init(void)
{
	int ret;

	ret = class_register(&mdio_bus_class);
	if (!ret) {
		ret = bus_register(&mdio_bus_type);
		if (ret)
			class_unregister(&mdio_bus_class);
	}

	return ret;
}

2、macb设备初始化

在这里插入图片描述

根据DTS中识别到的“macb”,加载该驱动,创建struct phy_device 类型的设备

static struct platform_driver macb_driver = {
	.probe		= macb_probe,
	.remove		= macb_remove,
	.driver		= {
		.name		= "macb",
		.of_match_table	= of_match_ptr(macb_dt_ids),
		.pm	= &macb_pm_ops,
	},
};
module_platform_driver(macb_driver);

macb_probe继而触发macb_mii_init

/**gem-macb运行流程**/
macb_mii_init(struct macb *bp)//xilinx-gem初始化
|->bp->mii_bus->name = "MACB_mii_bus";
|->of_mdiobus_register(bp->mii_bus, mdio_np);//注册mii_bus结构体并扫描phy设备
   |->mdiobus_register(mdio);//调用device_register将mii_bus设备注册进设备模型
   |->of_mdiobus_register_phy(mdio, child, addr);
      |->get_phy_device(mdio, addr, is_c45);//获取phy设备信息 读取phy芯片的id号
      	|->phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);//分配phy_device结构体并对其进行初始化
      	  |->phy_bus_match 
      	    |->phydrv->match_phy_device
      	  |->INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);//这里就是 phy device 的轮询任务
      |->of_mdiobus_phy_device_register(mdio, phy, child, addr);
        |->phy_device_register(phy);//phy_device_register中首先调用mdiobus_register_device将该phy_device添加进mii_bus的mdio_map数组中,最终调用device_add将phy_device注册进驱动模型并初始化该phy_device的phy状态机。
|->macb_mii_probe(bp->dev);//初始化和启动phy硬件
   |->phylink_create(&bp->phylink_config, bp->pdev->dev.fwnode,bp->phy_interface, &macb_phylink_ops);
	  |->of_phy_connect
	  |->phy_connect_direct//判断phy_device是否绑定了phy驱动,如果没有的话则将通用phy驱动genphy_driver作为phy_device的驱动
	  	 

2、PHY驱动匹配

下图为刚开始匹配,顺利匹配上了phy地址,并读取了phy寄存器内容,但最终发现设备的ID与通用驱动中不匹配,开始重新匹配。
在这里插入图片描述
重新匹配后,识别到设备为RTL8211F,开始匹配专用驱动,如下图所示
在这里插入图片描述

然后根据设备树的配置将PHY与网络设备连接。

struct phy_device *of_phy_connect(struct net_device *dev,
				  struct device_node *phy_np,
				  void (*hndlr)(struct net_device *), u32 flags,
				  phy_interface_t iface)
{
	struct phy_device *phy = of_phy_find_device(phy_np);
	int ret;

	if (!phy)
		return NULL;

	phy->dev_flags = flags;

	ret = phy_connect_direct(dev, phy, hndlr, iface);

	/* refcount is held by phy_connect_direct() on success */
	put_device(&phy->mdio.dev);

	return ret ? NULL : phy;
}
EXPORT_SYMBOL(of_phy_connect);

3、PHY的LINK阶段及初步自适应

/**
 * phy_connect_direct - connect an ethernet device to a specific phy_device
 * @dev: the network device to connect
 * @phydev: the pointer to the phy device
 * @handler: callback function for state change notifications
 * @interface: PHY device's interface
 */
int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
		       void (*handler)(struct net_device *),
		       phy_interface_t interface)
{
	int rc;

	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
	if (rc)
		return rc;

	phy_prepare_link(phydev, handler);
	phy_start_machine(phydev);
	if (phydev->irq > 0)
		phy_start_interrupts(phydev);

	return 0;
}
EXPORT_SYMBOL(phy_connect_direct);

phy_prepare_link用于在链路状态发生变化时接收通知。当PHY基础设施检测到链路状态(如连接或断开)发生变化时,它会调用这个回调函数。
phy_state_machine(),这个就是 phy_device 查询任务的主体,用来查询 phy 芯片的状态维护 phy
状态机。后期的循环任务会用到这个函数,很重要。
主要作用:启动一个延迟工作队列,该队列负责跟踪PHY设备的状态。这个工作队列会周期性地检查PHY的状态,并根据需要更新或响应这些状态变化。

4、PHY的循环任务

ndo_open()
`-| macb_open()
   `-| phylink_start()
        `-| phylink_run_resolve()  //(1) 启动 `phy_link` 的轮询任务 link状态更新
             `-| queue_work(system_power_efficient_wq, &pl->resolve);
     | phy_start()   //(2) 启动 `phy_device` 的轮询任务 各种phy相关操作及配置
            `-| phy_trigger_machine()
               `-| phy_queue_state_machine(phydev, 0);
                  `-| queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0);
shell命令与c函数的匹配
ifconfig eth0 up 
(macb_main.c) macb_open --> phy_start --> phy_trigger_machine

ifconfig eth0  down 
(macb_main.c) macb_close --> phy_disconnect ---> (phy.c)phy_stop_machine --> (workqueue.c)cancel_delayed_work_sync---> __cancel_work_timer

总结

这里对文章进行总结:
本次要讲述的zynq的phy驱动加载小知识就说到这里了,兄弟萌要有什么指导意见或疑问可以在评论区留下"足迹"。

  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Zynq的EMIO调试UART主要涉及到使用EMIO将外部设备与PS(处理器系统)中的PL(可编程逻辑)进行连接,并进行UART通信的调试工作。首先,我们需要在Vivado中设计包含UART功能的PL部分,并将其连接到EMIO接口。接着,在软件开发工具中配置UART的参数,如波特率、数据位、校验位和停止位等,并编写相应的驱动程序来实现数据的收发。在调试过程中,可以通过硬件调试工具来监视和分析UART通信的数据流,以确保数据的准确传输和正确解析。另外,也可以通过逐步调试程序和逻辑电路的方式来定位和解决通信过程中出现的问题。最后,通过在Zynq平台上进行实际测试,可以验证UART通信的稳定性和可靠性,并进行性能优化和改进。 在这个过程中,需要注意PL和PS之间的时序同步、数据格式的匹配、中断处理等方面的问题,以确保UART通信能够正确地工作。同时,还需要充分了解Zynq的架构和特性,熟练掌握Vivado和SDK等工具的使用方法,以提高调试的效率和成功率。最后,在完成UART通信的基本功能后,还可以考虑添加数据校验、流控和DMA等功能来提升通信性能和稳定性。整体而言,基于Zynq的EMIO调试UART需要综合运用硬件设计和软件开发的知识和技能,同时需要耐心和细心地进行调试工作,以确保UART通信系统能够满足设计要求并稳定可靠地工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值