内核版本:kernel5.10.35
1、硬件连接
两路网口都采用的RMII方式连接。fec1连接的PHY地址"0",fec2连接的PHY地址"1"。PHY地址由LAN8720第10引脚"REER/PHYAD0"的电平决定,高电平是1,低电平是0。时钟由imx6ull的ref_clk(50MHz)提供,所以LAN8720第14引脚需要上拉。
2、dts配置。
参考imx6ll的evb板修改dts如下:
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio4 15 GPIO_ACTIVE_LOW>; /* GPIO4_15 */
phy-reset-duration = <50>;
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-reset-gpios = <&gpio4 12 GPIO_ACTIVE_LOW>; /* GPIO4_12 */
phy-reset-duration = <50>;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <1000000>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0>;
smsc,disable-energy-detect;
clocks = <&clks IMX6UL_CLK_ENET_REF>;
clock-names = "rmii-ref";
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
smsc,disable-energy-detect;
clocks = <&clks IMX6UL_CLK_ENET2_REF>;
clock-names = "rmii-ref";
};
};
};
&iomuxc {
pinctrl-names = "default";
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
MX6UL_PAD_NAND_CLE__GPIO4_IO15 0x800050b0
>;
};
pinctrl_enet2: enet2grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0
MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
MX6UL_PAD_NAND_READY_B__GPIO4_IO12 0x800050b0
>;
};
};
对于复位操作,有一下属性可配置:
phy-reset-gpios = <&gpio4 15 GPIO_ACTIVE_LOW>; //定义复位用的的gpio
phy-reset-duration = <50>; //复位保持时间
phy-reset-active-high; //定义高电平复位,未定义低电平复位
phy-reset-post-delay = <50>; //复位后需要延时的时间。
驱动中对上面属性的使用参见"drivers/net/ethernet/freescale/fec_main.c"中fec_reset_phy函数。
static int fec_reset_phy(struct platform_device *pdev)
{
int err, phy_reset;
bool active_high = false;
int msec = 1, phy_post_delay = 0;
struct device_node *np = pdev->dev.of_node;
if (!np)
return 0;
err = of_property_read_u32(np, "phy-reset-duration", &msec);
/* A sane reset duration should not be longer than 1s */
if (!err && msec > 1000)
msec = 1;
phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
if (phy_reset == -EPROBE_DEFER)
return phy_reset;
else if (!gpio_is_valid(phy_reset))
return 0;
err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay);
/* valid reset duration should be less than 1s */
if (!err && phy_post_delay > 1000)
return -EINVAL;
active_high = of_property_read_bool(np, "phy-reset-active-high");
err = devm_gpio_request_one(&pdev->dev, phy_reset,
active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
"phy-reset");
if (err) {
dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
return err;
}
if (msec > 20)
msleep(msec);
else
usleep_range(msec * 1000, msec * 1000 + 1000);
gpio_set_value_cansleep(phy_reset, !active_high);
if (!phy_post_delay)
return 0;
if (phy_post_delay > 20)
msleep(phy_post_delay);
else
usleep_range(phy_post_delay * 1000,
phy_post_delay * 1000 + 1000);
return 0;
}
3、遇到的问题。
(1).fec1无法连接到PHY,但是reboot重启后是正常,错误log提示如下:
mdio_bus 20b4000.ethernet-1: MDIO device at address 0 is missing.
fec 2188000.ethernet eth1: Unable to connect to phy
结果办法:将ref_clk 50MHZ时钟让提前产生。
修改如下:
@@ -357,6 +357,7 @@
&iomuxc {
pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_eth_ref_clk>;
+
+ pinctrl_eth_ref_clk: ethfredclkgrp {
+ fsl,pins = <
+ MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
+ MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
+ >;
+ };
+
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
@@ -384,7 +392,7 @@
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
- MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
+// MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
MX6UL_PAD_NAND_CLE__GPIO4_IO15 0x800050b0
>;
};
@@ -400,7 +408,7 @@
MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
- MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
+// MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
MX6UL_PAD_NAND_READY_B__GPIO4_IO12 0x800050b0
>;
};
总结:
MDIO注册后,imx6ull和PHY通信的第一件事就是获取phy_id。所以可以在"drivers/net/phy/phy_device.c"的get_phy_device函数中可以添加log将phy_id打印出来,可以看到当故障时读取的phy_id一直是0xffff。从这里可以判断出通过MDIO读取PHY时出现了错误。又因为MDIO读取另一路上的PHY是正常的,所以排除imx6ull端MDIO出问题,所以问题就集中在PHY上面,又因为reboot是正常的,所以排除硬件固件。接下来就是排查时钟和上电时序。通过对时钟的调整,phy可以被正常识别。