目录
- 前言
- 一、硬件方案
- 二、第一种方法:只配置设备树
- 二、第二种方法:修改内核驱动和设备树
- 1. 修改设备树
- 2. 修改设备树kernel中 PHY GPIO 复位程序修改
- 3. kernel中 PHY LED指示灯配置修改
- 三、文件系统中 网络配置文件修改
- 四、U-Boot 中添加PHY GPIO Reset
- 五、其他方案
前言
本文硬件方案:ZYNQ上两个PHY芯片共用一个MDIO,两个PHY芯片GPIO Reset相互独立。
本文解决问题:两个PHY芯片的设备树配置,两个PHY芯片的GPIO复位,两个PHY芯片LED灯配置
一、硬件方案
ZYNQ使用PS的两个网口,两个PHY芯片共用ENET0的MDIO,PHY芯片的复位管脚使用PL端的引脚。
使用的是88E1512 PHY芯片,根据硬件设计方案在使用前需要GPIO复位PHY芯片。
将PHY芯片复位管脚使用EMIO映射到PS端。
二、第一种方法:只配置设备树
/** CAUTION: This file is automatically generated by Xilinx.* Version: * Today is: Tue Sep 15 13:54:36 2020*//include/ "system-top.dts"/ {model = "Zynq Board";compatible = "xlnx,zynq-MZ7X", "xlnx,zynq-7000";chosen {bootargs = "earlycon";stdout-path = "serial0:115200n8";};aliases {ethernet0 = &gem0;ethernet1 = &gem1;serial0 = &uart0;serial1 = &uart1;spi0 = &qspi;};memory {device_type = "memory";reg = <0x0 0x40000000>;};
};&gem0 {compatible = "cdns,zynq-gem";status = "okay";phy-mode = "rgmii-id";phy-handle = <&phy0>;local-mac-address = [00 0a 35 11 22 34];phy0: phy@0 {device_type = "ethernet-phy";reg = <0>;reset-gpios = <&gpio0 54 1>;/* kernel/drivers/net/phy/mdio_bus.c* mdiobus_register_gpiod / __mdiobus_register* gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode,* "reset-gpios", 0, GPIOD_OUT_LOW,* "PHY reset");*/marvell,reg-init = <0x3 0x10 0xff00 0x40>;/* kernel/drivers/net/phy/marvell.c* marvell,reg-init = <reg-page reg mask value>,...;* There may be one or more sets of <reg-page reg mask value>:* reg-page: which register bank to use.* reg: the register.* mask: if non-zero, ANDed with existing register value.* value: ORed with the masked value and written to the regiser.*/};phy1: phy@1 {device_type = "ethernet-phy";reg = <1>;reset-gpios = <&gpio0 55 1>;marvell,reg-init = <0x3 0x10 0xff00 0x40>;};
};&gem1 {compatible = "cdns,zynq-gem";status = "okay";phy-mode = "rgmii-id";phy-handle = <&phy1>;local-mac-address = [00 0a 35 11 22 35];};&qspi {u-boot,dm-pre-reloc;is-dual = <0>;num-cs = <1>;status = "okay";flash@0 {compatible = "s25fl512s";reg = <0x0>;spi-tx-bus-width = <1>;spi-rx-bus-width = <4>;spi-max-frequency = <50000000>;};
};&uart0 {device_type = "serial";port-number = <0>;status = "okay";
};&uart1 {device_type = "serial";port-number = <1>;status = "okay";
};
二、第二种方法:修改内核驱动和设备树
1. 修改设备树
将两个PHY节点都添加到GEM0中,MDIO设备初始化时,会读取两个PHY芯片信息。
将两个PHY芯片复位管脚添加到GEM0中, MIDIO设备初始化前复位两个PHY芯片。
system-user.dtsi:
/include/ "system-top.dts"
/ {model = "Zynq Board";compatible = "xlnx,zynq-MZ7X", "xlnx,zynq-7000";chosen {bootargs = "earlycon";stdout-path = "serial0:115200n8";};aliases {ethernet0 = &gem0;ethernet1 = &gem1;serial0 = &uart0;serial1 = &uart1;spi0 = &qspi;spi1 = &spi0;};memory {device_type = "memory";reg = <0x0 0x40000000>;};
};&gem0 {compatible = "cdns,zynq-gem";status = "okay";phy-mode = "rgmii-id";phy-handle = <&phy0>;reset-gpios = <&gpio0 54 1>; //GPIO_ACTIVE_HIGH 0 GPIO_ACTIVE_LOW 1reset-1-gpios = <&gpio0 55 1>; //GPIO_ACTIVE_HIGH 0 GPIO_ACTIVE_LOW 1phy0: phy@0 {device_type = "ethernet-phy";reg = <0>;};phy1: phy@1 {device_type = "ethernet-phy";reg = <1>;};
};&gem1 {compatible = "cdns,zynq-gem";status = "okay";phy-mode = "rgmii-id";phy-handle = <&phy1>;
};&gpio0 {emio-gpio-width = <10>;gpio-mask-high = <0x0>;gpio-mask-low = <0x5600>;
};
2. 修改设备树kernel中 PHY GPIO 复位程序修改
内核版本:4.19,其他版本可能程序不一样
MDIO总线程序中默认只有一个复位管脚,我们要复位两个PHY芯片,所以要添加复位程序。
(1)在 /kernel/driver/net/phy/mdio_bus.c: __mdiobus_register 中 修改 gpio复位程序:
- 添加一个读取设备树中 reset-1 GPIO信息并复位的程序。
- 添加复位打印信息。
/* de-assert bus level PHY GPIO reset */gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW);if (IS_ERR(gpiod)) {dev_err(&bus->dev, "mii_bus %s couldn't get reset GPIO\n",bus->id);return PTR_ERR(gpiod);} else if (gpiod) {bus->reset_gpiod = gpiod;dev_info(&bus->dev, "%s: reset-gpio = %d, gpio_offset = %d\n", __func__, desc_to_gpio(gpiod), \desc_to_gpio(gpiod)-gpiod_to_chip(gpiod)->base);gpiod_set_value_cansleep(gpiod, 1);udelay(bus->reset_delay_us);gpiod_set_value_cansleep(gpiod, 0);}//add 20210510gpiod = devm_gpiod_get_optional(&bus->dev, "reset-1", GPIOD_OUT_LOW);if (IS_ERR(gpiod)) {dev_err(&bus->dev, "mii_bus %s couldn't get reset-1 GPIO\n",bus->id);//return PTR_ERR(gpiod);} else if (gpiod) {//bus->reset_gpiod = gpiod;dev_info(&bus->dev, "%s: reset-1-gpio = %d, gpio_offset = %d\n", __func__, desc_to_gpio(gpiod), \desc_to_gpio(gpiod)-gpiod_to_chip(gpiod)->base);gpiod_set_value_cansleep(gpiod, 1);udelay(bus->reset_delay_us);gpiod_set_value_cansleep(gpiod, 0);}
程序会根据 reset-1 到设备树中读取复位管脚信息,然后复位。
(2)添加GPIO复位打印信息
在 kernel/drivers/gpio/gpiolib.c 的 void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
函数中添加打印 :
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{struct gpio_chip *chip;int offset = gpio_chip_hwgpio(desc);chip = desc->gdev->chip;trace_gpio_value(desc_to_gpio(desc), 0, value);chip->set(chip, offset, value);gpiod_info(desc,"%s: base = %d, offset = %d, value = %d\n", __func__, chip->base, offset, value);
}
3. kernel中 PHY LED指示灯配置修改
88E1512 PHY芯片使用的是88E1510驱动,LED灯定义可能不一样,导致LED灯可能不亮或者状态不对。
在 /kernel/driver/net/phy/marvell.c: 中修改 MII_88E1510_PHY_LED_DEF 定义:
原来定义为
#define MII_88E1510_PHY_LED_DEF 0x1177
修改为:
/* 88E1512 LED 设置
LED[1] 0001 = On - Link, Blink - Activity, Off - No Link0100 = Blink - Activity, Off - No Activity
LED[0] 0000 = On - Link, Off - No Link0010 = 3 blinks - 1000 Mbps2 blinks - 100 Mbps1 blink - 10 Mbps0 blink - No Link
*/
#define MII_88E1510_PHY_LED_DEF 0x1040
修改后LED0表示Link状态,LED1表示Activity状态。
三、文件系统中 网络配置文件修改
修改/etc/network/interface文件:
设置MAC和IP:eht0和eth1只能设置一个网关,修改MAC地址要在网卡启动前(使用pre-up)
auto lo
iface lo inet loopback# A. For DHCP on eth0
# auto eth0
# iface eth0 inet dhcp# B. For static on eth0auto eth0
iface eth0 inet static
pre-up ifconfig $IFACE down
pre-up ifconfig $IFACE hw ether 00:0A:35:00:02:11
pre-up ifconfig $IFACE up
address 192.168.0.210
netmask 255.255.255.0
gateway 192.168.0.1auto eth1
iface eth1 inet static
pre-up ifconfig $IFACE down
pre-up ifconfig $IFACE hw ether 00:0A:35:00:02:12
pre-up ifconfig $IFACE up
address 192.168.1.210
netmask 255.255.255.0
#gateway 192.168.1.1
四、U-Boot 中添加PHY GPIO Reset
- 设备树修改(目前只测试了一个PHY芯片)
/ {model = "ZYNQ Board";compatible = "xlnx,zynq-7000";aliases {ethernet0 = &gem0;//ethernet1 = &gem1;serial0 = &uart0;spi0 = &qspi;mmc0 = &sdhci0;};
};&gpio0 {emio-gpio-width = <8>;gpio-mask-high = <0x0>;gpio-mask-low = <0x5600>;
};&gem0 {status = "okay";phy-mode = "rgmii-id";phy-handle = <ðernet_phy_0>;reset-gpios = <&gpio0 54 0>; //GPIO_ACTIVE_HIGH 0//reset-1-gpios = <&gpio0 55 0>; //GPIO_ACTIVE_HIGH 0ethernet_phy_0: ethernet_phy@0 {device_type = "ethernet-phy";reg = <0>;};/*ethernet_phy_1: ethernet_phy@1 {device_type = "ethernet-phy";reg = <1>;};*/
};/*gem1 {status = "okay";phy-mode = "rgmii-id";phy-handle = <ðernet_phy_1>;
};*/
- 添加GPIO复位程序
(1) 在 u-boot/drivers/net/zynq_gem.c 中:
添加 头文件:
#include <asm/gpio.h>
添加 gpio_desc 定义:在结构体 zynq_gem_priv 中添加
/* Initialized, rxbd_current, rx_first_buf must be 0 after init */
struct zynq_gem_priv {struct emac_bd *tx_bd;struct emac_bd *rx_bd;char *rxbuffers;u32 rxbd_current;u32 rx_first_buf;int phyaddr;int init;struct zynq_gem_regs *iobase;phy_interface_t interface;struct phy_device *phydev;int phy_of_handle;struct mii_dev *bus;struct clk clk;u32 max_speed;bool int_pcs;struct gpio_desc reset_gpio;
};
(2) 在 u-boot/drivers/net/zynq_gem.c 中添加 zynq_phy_reset(struct udevice *dev)
函数
函数功能:读取设备树中 reset-gpios 和 reset-1-gpios 的GPIO信息,如果GPIO信息有效就先将GPIO置0,延时10ms后再置1
/*
* phy gpio reset
* add 20210514
*/
static void zynq_phy_reset(struct udevice *dev)
{struct zynq_gem_priv *priv = dev_get_priv(dev);gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio,GPIOD_IS_OUT);if (dm_gpio_is_valid(&priv->reset_gpio)) {printf("%s: reset-gpios = %d\n", __func__, priv->reset_gpio.offset);dm_gpio_set_value(&priv->reset_gpio, 0);udelay(10000);dm_gpio_set_value(&priv->reset_gpio, 1);}gpio_request_by_name(dev, "reset-1-gpios", 0, &priv->reset_gpio,GPIOD_IS_OUT);if (dm_gpio_is_valid(&priv->reset_gpio)) {printf("%s: reset-1-gpios = %d\n", __func__, priv->reset_gpio.offset);dm_gpio_set_value(&priv->reset_gpio, 0);udelay(10000);dm_gpio_set_value(&priv->reset_gpio, 1);}
}
(3) 在 int zynq_gem_ofdata_to_platdata(struct udevice *dev)
函数中调用 zynq_phy_reset(struct udevice *dev)
函数
static int zynq_gem_ofdata_to_platdata(struct udevice *dev)
{struct eth_pdata *pdata = dev_get_platdata(dev);struct zynq_gem_priv *priv = dev_get_priv(dev);int node = dev_of_offset(dev);const char *phy_mode;//add 20210514zynq_phy_reset(dev);//phy resetpdata->iobase = (phys_addr_t)devfdt_get_addr(dev);priv->iobase = (struct zynq_gem_regs *)pdata->iobase;/* Hardcode for now */priv->phyaddr = -1;priv->phy_of_handle = fdtdec_lookup_phandle(gd->fdt_blob, node,"phy-handle");if (priv->phy_of_handle > 0)priv->phyaddr = fdtdec_get_int(gd->fdt_blob,priv->phy_of_handle, "reg", -1);phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL);if (phy_mode)pdata->phy_interface = phy_get_interface_by_name(phy_mode);if (pdata->phy_interface == -1) {debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);return -EINVAL;}priv->interface = pdata->phy_interface;priv->max_speed = fdtdec_get_uint(gd->fdt_blob, priv->phy_of_handle,"max-speed", SPEED_1000);priv->int_pcs = fdtdec_get_bool(gd->fdt_blob, node,"is-internal-pcspma");printf("ZYNQ GEM: %lx, phyaddr %x, interface %s\n", (ulong)priv->iobase,priv->phyaddr, phy_string_for_interface(priv->interface));return 0;
}
(4) 添加GPIO复位打印信息
在 u-boot/drivers/gpio/gpio-uclass.c 的 int dm_gpio_set_value(const struct gpio_desc *desc, int value)
函数中添加打印 :
int dm_gpio_set_value(const struct gpio_desc *desc, int value)
{int ret;ret = check_reserved(desc, "set_value");if (ret)return ret;if (desc->flags & GPIOD_ACTIVE_LOW)value = !value;gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value);printf("%s: gpio offset = %d, value = %d\n", __func__, desc->offset, value);return 0;
}
五、其他方案
需要给内核打补丁(0001-net-macb-Add-MDIO-driver-for-accessing-multiple-PHY-.patch),可参考下面连接,暂未测试。
- petalinux在zynq平台移植和双网口实现
- ZYNQ petalinux双网口88E1512设计
- Dual Ethernet over MII/MDIO not working in Petalinux SDK 2018.2
Kernel补丁(Petalinux 2018.2):
0001-net-macb-Add-MDIO-driver-for-accessing-multiple-PHY-.zip
Uboot补丁(Petalinux 2018.2):
0001_u-boot_multiple_phy_on_mdio.zip
设备树(system-user.dtsi):
/include/ "system-conf.dtsi"
/ {mdio {compatible = "cdns,macb-mdio";reg = <0xe000b000 0x1000>;clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;clock-names = "pclk", "hclk", "tx_clk";#address-cells = <1>;#size-cells = <0>;ethernet_phy0: ethernet-phy@0 {compatible = "marvell,88e1510";device_type = "ethernet-phy";reg = <0>;};ethernet_phy1: ethernet-phy@1 {compatible = "marvell,88e1510";device_type = "ethernet-phy";reg = <1>;};};
};
&gem0 {status = "okay";phy-mode = "rgmii-id";local-mac-address = [00 0a 35 00 1e 53];phy-handle = <ðernet_phy0>;phy-reset-gpio = <&gpio0 54 1>;phy-reset-duration = <20>;phy-reset-active-low;
};
&gem1 {status = "okay";phy-mode = "rgmii-id";local-mac-address = [00 0a 35 00 1e 54];phy-handle = <ðernet_phy1>; phy-reset-gpio = <&gpio0 55 1>;phy-reset-duration = <20>;phy-reset-active-low;
};
注:内核版本为:4.14 可使用该设备树,4.19不可用