IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Orin 10G网络分析 二 -> 正文阅读

[系统运维]Orin 10G网络分析 二

平台:nvidia orin

内核版本:linux-5.10

Orin 支持10G Tx 网络,所用的PHY芯片为AQR113,硬件在上篇文章已经介绍,本文主要从软件流程介绍:

设备树文件:

/ {
	/* MGBE - A */
	ethernet@6810000 {
		status = "okay";
		nvidia,mac-addr-idx = <0>;
		nvidia,max-platform-mtu = <16383>;
		/* 0=enable, 1=disable */
		nvidia,pause_frames = <0>;
		phy-handle = <&mgbe0_aqr113c_phy>;
		phy-mode = "10gbase-r";
		/* 0:XFI 10G, 1:XFI 5G, 2:USXGMII 10G, 3:USXGMII 5G */
		nvidia,phy-iface-mode = <0>;

		mdio {
			compatible = "nvidia,eqos-mdio";
			#address-cells = <1>;
			#size-cells = <0>;

			mgbe0_aqr113c_phy: ethernet_phy@0 {
				compatible = "ethernet-phy-ieee802.3-c45";
				reg = <0x0>;
				interrupt-parent = <&tegra_main_gpio>;
				interrupts = <TEGRA234_MAIN_GPIO(Y, 3) IRQ_TYPE_LEVEL_LOW>;
			};
		};
	};
};

通过XFI接口 连接,设置10G模式:

mgbe0_aqr113c_phy: ethernet_phy@0 {

? ? ? ? ? ? ? ? compatible = "ethernet-phy-ieee802.3-c45";

可以看到使用的PHY芯片,及使用MDIO协议为c45

compatible = "nvidia,eqos-mdio"; 找到对应的驱动文件:

kernel/nvidia/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c

static struct platform_driver ether_driver = {
	.probe = ether_probe,
	.remove = ether_remove,
	.shutdown = ether_shutdown,
	.driver = {
		.name = "nvethernet",
		.of_match_table = ether_of_match,
#ifdef CONFIG_PM
		.pm = &ether_pm_ops,
#endif
	},
};

platform_driver_register(&ether_driver);注册网络平台设备,进入porbe函数:

ether_probe

??????? -> alloc_etherdev_mq? 申请网络设备,网络的eth0 接口

??????? ->ether_parse_dt 解析设备树文件信息

?????? ->ether_set_ethtool_ops 设置ethtool操作函数

??????? ->ether_alloc_napi 支持napi

??????? ->ndev->netdev_ops = &ether_netdev_ops; 重点函数,设置网络操作的函数指针

???? ?? ->register_netdev 注册网络设备

?????? INIT_DELAYED_WORK(&pdata->ether_stats_work, ether_stats_work_func);

?????? INIT_DELAYED_WORK(&pdata->set_speed_work, set_speed_work_func);

static const struct net_device_ops ether_netdev_ops = {
	.ndo_open = ether_open,
	.ndo_stop = ether_close,
	.ndo_start_xmit = ether_start_xmit,
	.ndo_do_ioctl = ether_ioctl,
	.ndo_set_mac_address = ether_set_mac_addr,
	.ndo_change_mtu = ether_change_mtu,
	.ndo_select_queue = ether_select_queue,
	.ndo_set_features = ether_set_features,
	.ndo_set_rx_mode = ether_set_rx_mode,
	.ndo_vlan_rx_add_vid = ether_vlan_rx_add_vid,
	.ndo_vlan_rx_kill_vid = ether_vlan_rx_kill_vid,
#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
	.ndo_setup_tc = ether_setup_tc,
#endif
};

进入ether_open

->ether_mdio_register? 注册mii_bus结构体并扫描phy设备

->devm_mdiobus_alloc? 分配mdiobus

->new_bus->read = ether_mdio_read; 设置mdio读的回调函数

->new_bus->write = ether_mdio_write; 设置mdio写的回调函数

->of_mdiobus_register device_register将mii_bus设备注册进设备模型,之后获取设备树中记录的phy节点信息

??????? ->of_mdiobus_register_phy 探测并创建phy_device设备

??????? ->is_c45 = of_device_is_compatible(child,

? ? ? ? ? ? ? ? ? ? ?"ethernet-phy-ieee802.3-c45");

????????????? ->get_phy_device(mdio, addr, is_c45); 调用get_phy_id读取phy芯片的id号

?????????????? -> phy_device_create 分配了phy_device结构体并对其进行初始化

?扫描到phy device后,创建phy device

struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
	struct phy_c45_device_ids c45_ids;
	u32 phy_id = 0;
	int r;

	c45_ids.devices_in_package = 0;
	c45_ids.mmds_present = 0;
	memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));

	if (is_c45)
		r = get_phy_c45_ids(bus, addr, &c45_ids);
	else
		r = get_phy_c22_id(bus, addr, &phy_id);

	if (r)
		return ERR_PTR(r);

	return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);

?c45协议

static int get_phy_c45_ids(struct mii_bus *bus, int addr,
			   struct phy_c45_device_ids *c45_ids)
{
	const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
	u32 devs_in_pkg = 0;
	int i, ret, phy_reg;

	/* Find first non-zero Devices In package. Device zero is reserved
	 * for 802.3 c45 complied PHYs, so don't probe it at first.
	 */
	for (i = 1; i < MDIO_MMD_NUM && (devs_in_pkg == 0 ||
	     (devs_in_pkg & 0x1fffffff) == 0x1fffffff); i++) {
		if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
			/* Check that there is a device present at this
			 * address before reading the devices-in-package
			 * register to avoid reading garbage from the PHY.
			 * Some PHYs (88x3310) vendor space is not IEEE802.3
			 * compliant.
			 */
			ret = phy_c45_probe_present(bus, addr, i);
			if (ret < 0)
				return -EIO;

			if (!ret)
				continue;
		}
		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg);
		if (phy_reg < 0)
			return -EIO;
	}

	if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) {
		/* If mostly Fs, there is no device there, then let's probe
		 * MMD 0, as some 10G PHYs have zero Devices In package,
		 * e.g. Cortina CS4315/CS4340 PHY.
		 */
		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg);
		if (phy_reg < 0)
			return -EIO;

		/* no device there, let's get out of here */
		if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff)
			return -ENODEV;
	}

	/* Now probe Device Identifiers for each device present. */
	for (i = 1; i < num_ids; i++) {
		if (!(devs_in_pkg & (1 << i)))
			continue;

		if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
			/* Probe the "Device Present" bits for the vendor MMDs
			 * to ignore these if they do not contain IEEE 802.3
			 * registers.
			 */
			ret = phy_c45_probe_present(bus, addr, i);
			if (ret < 0)
				return ret;

			if (!ret)
				continue;
		}

		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1);
		if (phy_reg < 0)
			return -EIO;
		c45_ids->device_ids[i] = phy_reg << 16;

		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2);
		if (phy_reg < 0)
			return -EIO;
		c45_ids->device_ids[i] |= phy_reg;
	}

	c45_ids->devices_in_package = devs_in_pkg;
	/* Bit 0 doesn't represent a device, it indicates c22 regs presence */
	c45_ids->mmds_present = devs_in_pkg & ~BIT(0);

	return 0;
}

上述函数中mdiobus_c45_read的调用关系

mdiobus_c45_read

?????????->mdiobus_read

? ????????->__mdiobus_read

?? ?????? ->retval = bus->read(bus, addr, regnum);

最终是调用的ether_mdio_register 注册mdio总线时的回调函数:

如下两个

new_bus->read = ether_mdio_read;

new_bus->write = ether_mdio_write;

phy device注册的流程:

of_mdiobus_register

of_mdiobus_register_phy

of_mdiobus_phy_device_register

phy_device_register

mdiobus_register_device

此函数主要功能是赋值? mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;

phy_scan_fixups

device_add? 会调用kobject_add将device注册进sysfs,再调用bus_add_device和bus_probe_device。bus_add_device将设备挂接到其总线(mdio_bus_type)的设备列表。bus_probe_device将遍历总线上已注册的所有驱动程序,调用总线的match函数检查驱动是否能和该设备匹配。若匹配则将设备和驱动程序绑定,然后调用驱动的probe函数,这里完成device_add之后就进入了phy_probe函数中。

再进入PHY 的probe函数 phy_probe

这个是同样的 PHY 的驱动函数,在kernel-5.10/drivers/net/phy/phy_device.c 函数中已经提前初始化了驱动函数:

subsys_initcall(phy_init);? - > static int __init phy_init(void) ->phy_driver_register(&genphy_c45_driver, THIS_MODULE);

略去此段,

进入phy_probe 函数之后

->phy_device_reset

genphy_c45_pma_read_abilities 此函数主要读取PHY的能力

phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);

........

再真正进入PHY的初始化:

ether_phy_init

????????->of_phy_connect

????????->phy_connect_direct

????????->phy_attach_direct 判断phy_device是否绑定了phy驱动,如果没有的话则将通用phy驱动genphy_driver作为phy_device的驱动,然后调用genphy_driver的probe函数,即phy_probe函数进行初始化处理,再调用device_bind_driver将设备和驱动正式绑定

????????->get_device

????????->phy_sysfs_create_links

????????->phy_init_hw? 初始化

??????? ->phy_scan_fixups

????????->phy_disable_interrupts 禁止中断

????????->phy_resume? 唤醒PHY

????????->phy_led_triggers_register? LED显示

ether_adjust_link 真正判断link状态的是在这个函数中进行

cancel_delayed_work_sync(&pdata->set_speed_work);

phy_print_status(phydev);? 打印网络状态

最后说一下Orin mdio_read 的过程:

ether_mdio_read

osi_read_phy_reg

?l_core->if_ops_p->if_read_phy_reg(osi_core, phyaddr, phyreg)

if_ops_p->if_read_phy_reg = osi_hal_read_phy_reg;

l_core->ops_p->read_phy_reg(osi_core, phyaddr, phyreg);

ops->read_phy_reg = mgbe_read_phy_reg;

这复杂的调用关系,晕了晕了,到此结束

static int mgbe_read_phy_reg(struct osi_core_priv_data *osi_core,
			     unsigned int phyaddr,
			     unsigned int phyreg)
{
	unsigned int reg;
	unsigned int data;
	int ret = 0;
	ret = mgbe_mdio_busy_wait(osi_core);
	if (ret < 0) {
		OSI_CORE_ERR(osi_core->osd,
			OSI_LOG_ARG_HW_FAIL,
			"MII operation timed out\n",
			0ULL);
		return ret;
	}

	/* set MDIO address register */
	/* set device address */
	reg = ((phyreg >> MGBE_MDIO_C45_DA_SHIFT) & MGBE_MDIO_SCCA_DA_MASK) <<
	       MGBE_MDIO_SCCA_DA_SHIFT;
	/* set port address and register address */
	reg |= (phyaddr << MGBE_MDIO_SCCA_PA_SHIFT) |
		(phyreg & MGBE_MDIO_SCCA_RA_MASK);
	osi_writela(osi_core, reg, (unsigned char *)
		    osi_core->base + MGBE_MDIO_SCCA);

	/* Program Data register */
	reg = (MGBE_MDIO_SCCD_CMD_RD << MGBE_MDIO_SCCD_CMD_SHIFT) |
	       MGBE_MDIO_SCCD_SBUSY;

	 /**
         * On FPGA AXI/APB clock is 13MHz. To achive maximum MDC clock
         * of 2.5MHz need to enable CRS and CR to be set to 1.
         * On Silicon AXI/APB clock is 408MHz. To achive maximum MDC clock
         * of 2.5MHz only CR need to be set to 5.
         */
        if (osi_core->pre_si) {
                reg |= (MGBE_MDIO_SCCD_CRS |
                        ((0x1U & MGBE_MDIO_SCCD_CR_MASK) <<
                        MGBE_MDIO_SCCD_CR_SHIFT));
        } else {
                reg &= ~MGBE_MDIO_SCCD_CRS;
                reg |= ((0x5U & MGBE_MDIO_SCCD_CR_MASK) <<
                        MGBE_MDIO_SCCD_CR_SHIFT);
        }

	osi_writela(osi_core, reg, (unsigned char *)
		    osi_core->base + MGBE_MDIO_SCCD);

	ret = mgbe_mdio_busy_wait(osi_core);
	if (ret < 0) {
		OSI_CORE_ERR(osi_core->osd,
			OSI_LOG_ARG_HW_FAIL,
			"MII operation timed out\n",
			0ULL);
		return ret;
	}

	reg = osi_readla(osi_core, (unsigned char *)
			 osi_core->base + MGBE_MDIO_SCCD);

	data = (reg & MGBE_MDIO_SCCD_SDATA_MASK);
	return (int)data;
}

再提一下,mdio phy 设备名称的过程:可以看到/sys/class/mdio_bus/?? mdio_bus 下面有

可以看到 设备 6810000.ethernet 的名称

这个名字是如何注册来的呢?

ether_mdio_register

snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));

static inline const char *dev_name(const struct device *dev)
{
	/* Use the init name until the kobject becomes available */
	if (dev->init_name)
		return dev->init_name;

	return kobject_name(&dev->kobj);
}

此时的dev_name(dev) 正是 6810000.ethernet

??????? dev的名字是如何来的呢?? ether_probe(struct platform_device *pdev)? 时通过platform_device 解析设备树节点信息,传递过来的。这又是一个可以写一大篇的过程了,略去不表,总之是内核启动时自动将device_node转换为platform_device。

接着在phy_device_create 函数 phy设备创建时。

dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr); 创建了名字6810000.ethernet:00;

而 6810000.ethernet:00 即为 phy 设备的信息

参考文章:

基于对zynq以太网驱动的分析理解linux phy子系统_xiangweiky的博客-CSDN博客


慢慢欣赏linux phy驱动初始化_天麓的博客-CSDN博客

以太网PHY寄存器分析_kunkliu的博客-CSDN博客_phy 寄存器

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-06-08 19:16:18  更:2022-06-08 19:20:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 14:22:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码