ZYNQ Linux 双网口,MDIO共用,RESET-GPIO不共用


前言

本文硬件方案: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 1
	reset-1-gpios = <&gpio0 55 1>; //GPIO_ACTIVE_HIGH 0 GPIO_ACTIVE_LOW 1
	
	phy0: 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 20210510
	gpiod = 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.cvoid 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 Link
		0100 = Blink - Activity, Off - No Activity
LED[0] 0000 = On - Link, Off - No Link
		0010 = 3 blinks - 1000 Mbps
				2 blinks - 100 Mbps
				1 blink - 10 Mbps
				0 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 eth0

auto 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.1

auto 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

  1. 设备树修改(目前只测试了一个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 = <&ethernet_phy_0>;
	
	reset-gpios = <&gpio0 54 0>; //GPIO_ACTIVE_HIGH 0
	//reset-1-gpios = <&gpio0 55 0>; //GPIO_ACTIVE_HIGH 0
	
	ethernet_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 = <&ethernet_phy_1>;
};*/
  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-gpiosreset-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 20210514
	zynq_phy_reset(dev);//phy reset

	pdata->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.cint 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),可参考下面连接,暂未测试。

  1. petalinux在zynq平台移植和双网口实现
  2. ZYNQ petalinux双网口88E1512设计
  3. 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 = <&ethernet_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 = <&ethernet_phy1>;	   
	phy-reset-gpio = <&gpio0 55 1>;
	phy-reset-duration = <20>;
	phy-reset-active-low;	 
};

注:内核版本为:4.14 可使用该设备树,4.19不可用

  • 5
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值