imx8qm包含两路RGMII和MDIO接口,在电路中两路RGMII可共享同一路MDIO接口或各自采用独立的MDIO接口实现对PHY的读写和控制。下面根据不同的连接方式来实现内核中dts的配置。
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec1>;
phy-mode = "rgmii-txid";
phy-handle = <ðphy0>;
fsl,magic-packet;
nvmem-cells = <&fec_mac0>;
nvmem-cell-names = "mac-address";
fsl,rgmii_rxc_dly;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0>;
at803x,eee-disabled;
at803x,vddio-1p8v;
qca,clk-out-frequency = <125000000>;
};
};
};
&iomuxc {
pinctrl_fec1: fec1grp {
fsl,pins = <
IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETB_PAD 0x000014a0
IMX8QM_ENET0_MDC_CONN_ENET0_MDC 0x06000020
IMX8QM_ENET0_MDIO_CONN_ENET0_MDIO 0x06000020
IMX8QM_ENET0_RGMII_TX_CTL_CONN_ENET0_RGMII_TX_CTL 0x06000020
IMX8QM_ENET0_RGMII_TXC_CONN_ENET0_RGMII_TXC 0x06000020
IMX8QM_ENET0_RGMII_TXD0_CONN_ENET0_RGMII_TXD0 0x06000020
IMX8QM_ENET0_RGMII_TXD1_CONN_ENET0_RGMII_TXD1 0x06000020
IMX8QM_ENET0_RGMII_TXD2_CONN_ENET0_RGMII_TXD2 0x06000020
IMX8QM_ENET0_RGMII_TXD3_CONN_ENET0_RGMII_TXD3 0x06000020
IMX8QM_ENET0_RGMII_RXC_CONN_ENET0_RGMII_RXC 0x06000020
IMX8QM_ENET0_RGMII_RX_CTL_CONN_ENET0_RGMII_RX_CTL 0x06000020
IMX8QM_ENET0_RGMII_RXD0_CONN_ENET0_RGMII_RXD0 0x06000020
IMX8QM_ENET0_RGMII_RXD1_CONN_ENET0_RGMII_RXD1 0x06000020
IMX8QM_ENET0_RGMII_RXD2_CONN_ENET0_RGMII_RXD2 0x06000020
IMX8QM_ENET0_RGMII_RXD3_CONN_ENET0_RGMII_RXD3 0x06000020
>;
};
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec2>;
phy-mode = "rgmii-txid";
phy-handle = <ðphy1>;
fsl,magic-packet;
nvmem-cells = <&fec_mac1>;
nvmem-cell-names = "mac-address";
fsl,rgmii_rxc_dly;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
at803x,eee-disabled;
at803x,vddio-1p8v;
qca,clk-out-frequency = <125000000>;
};
};
};
&iomuxc {
pinctrl_fec2: fec2grp {
fsl,pins = <
IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETA_PAD 0x000014a0
IMX8QM_ENET1_MDC_CONN_ENET1_MDC 0x06000020
IMX8QM_ENET1_MDIO_CONN_ENET1_MDIO 0x06000020
IMX8QM_ENET1_RGMII_TX_CTL_CONN_ENET1_RGMII_TX_CTL 0x00000060
IMX8QM_ENET1_RGMII_TXC_CONN_ENET1_RGMII_TXC 0x00000060
IMX8QM_ENET1_RGMII_TXD0_CONN_ENET1_RGMII_TXD0 0x00000060
IMX8QM_ENET1_RGMII_TXD1_CONN_ENET1_RGMII_TXD1 0x00000060
IMX8QM_ENET1_RGMII_TXD2_CONN_ENET1_RGMII_TXD2 0x00000060
IMX8QM_ENET1_RGMII_TXD3_CONN_ENET1_RGMII_TXD3 0x00000060
IMX8QM_ENET1_RGMII_RXC_CONN_ENET1_RGMII_RXC 0x00000060
IMX8QM_ENET1_RGMII_RX_CTL_CONN_ENET1_RGMII_RX_CTL 0x00000060
IMX8QM_ENET1_RGMII_RXD0_CONN_ENET1_RGMII_RXD0 0x00000060
IMX8QM_ENET1_RGMII_RXD1_CONN_ENET1_RGMII_RXD1 0x00000060
IMX8QM_ENET1_RGMII_RXD2_CONN_ENET1_RGMII_RXD2 0x00000060
IMX8QM_ENET1_RGMII_RXD3_CONN_ENET1_RGMII_RXD3 0x00000060
>;
};
};
注意:在只使用一路以太网时请将另一路的网口的“status”属性设置为“disabled”。
参考“1”中方式进行如下修改:
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec1>;
phy-mode = "rgmii-txid";
phy-handle = <ðphy0>;
fsl,magic-packet;
nvmem-cells = <&fec_mac0>;
nvmem-cell-names = "mac-address";
fsl,rgmii_rxc_dly;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0>;
at803x,eee-disabled;
at803x,vddio-1p8v;
qca,clk-out-frequency = <125000000>;
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
at803x,eee-disabled;
at803x,vddio-1p8v;
qca,clk-out-frequency = <125000000>;
};
};
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec2>;
phy-mode = "rgmii-txid";
phy-handle = <ðphy1>;
fsl,magic-packet;
nvmem-cells = <&fec_mac1>;
nvmem-cell-names = "mac-address";
fsl,rgmii_rxc_dly;
status = "okay";
};
另外在pinctrl的配置中注释掉不用的 MDIO接口,将需要用的 MDIO接口配置在pinctrl_fec1中。
这种情况下的配置和“1”中的相同,将两个网口都使能。关于为啥需要在两个网口的节点中分别定义各自的“mdio”节点,这个可以参考驱动(drivers/net/ethernet/freescale/fec_main.c)中fec_enet_mii_init函数。
//下面是部分代码
static int fec_enet_mii_init(struct platform_device *pdev)
{
static struct mii_bus *fec0_mii_bus;
static bool *fec_mii_bus_share;
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
bool suppress_preamble = false;
struct device_node *node;
int err = -ENXIO;
u32 mii_speed, holdtime;
u32 bus_freq;
/*
* The i.MX28 dual fec interfaces are not equal.
* Here are the differences:
*
* - fec0 supports MII & RMII modes while fec1 only supports RMII
* - fec0 acts as the 1588 time master while fec1 is slave
* - external phys can only be configured by fec0
*
* That is to say fec1 can not work independently. It only works
* when fec0 is working. The reason behind this design is that the
* second interface is added primarily for Switch mode.
*
* Because of the last point above, both phys are attached on fec0
* mdio interface in board design, and need to be configured by
* fec0 mii_bus.
*/
if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) {
/* fec1 uses fec0 mii_bus */
if (mii_cnt && fec0_mii_bus) {
fep->mii_bus = fec0_mii_bus;
*fec_mii_bus_share = true;
mii_cnt++;
return 0;
}
return -ENOENT;
}
......
bus_freq = 2500000; /* 2.5MHz by default */
node = of_get_child_by_name(pdev->dev.of_node, "mdio");
if (node) {
of_property_read_u32(node, "clock-frequency", &bus_freq);
suppress_preamble = of_property_read_bool(node,
"suppress-preamble");
}
......
fep->mii_bus->name = "fec_enet_mii_bus";
fep->mii_bus->read = fec_enet_mdio_read;
fep->mii_bus->write = fec_enet_mdio_write;
snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, fep->dev_id + 1);
fep->mii_bus->priv = fep;
fep->mii_bus->parent = &pdev->dev;
err = of_mdiobus_register(fep->mii_bus, node);
if (err)
goto err_out_free_mdiobus;
......
}
上面代码可以看出,通过fec下的mdio子节点来获取一个device_node,of_mdiobus_register注册mdiobus时会将这个device_node传入。
仿照上面的修改后,如果系统启动时在打开eth1提示“fec 5b05000.ethernet eth1: Unable to connect to phy”。说明fec2上的PHY未被识别到。在fec_enet_mii_init函数中添加测试点会发现,这个函数被调用了两次,但是“of_mdiobus_register(fep->mii_bus, node);”注册mdiobus却只被调用了一次。按照DTS中的配置,因为我们在每路网口下都配置mdio节点,所以这里应该是需要调用两次“of_mdiobus_register(fep->mii_bus, node);”来注册各自的mdiobus的。在函数内的第一个“if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0)”前后添加测试log,会发现在fec_enet_mii_init被第二次调用的时候,在这个if中函数返回了。返回的原因if语句前的注释也进行了说明。所以现在需要让函数继续执行,而不是在这里返回,解决的思路就是fep->quirks中取消FEC_QUIRK_SINGLE_MDIO的设置。在“fec_probe”函数中能找到关于这个的设置,并且刚好得调用fec_enet_mii_init函数之前。
static int
fec_probe(struct platform_device *pdev)
{
......
/* board only enable one mii bus in default */
if (!of_get_property(np, "fsl,mii-exclusive", NULL))
fep->quirks |= FEC_QUIRK_SINGLE_MDIO;
ret = fec_enet_mii_init(pdev);
if (ret)
goto failed_mii_init;
......
}
从代码可以看出只要在dts中添加“fsl,mii-exclusive”属性会跳过FEC_QUIRK_SINGLE_MDIO的设置。如下:
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec2>;
phy-mode = "rgmii-txid";
phy-handle = <ðphy1>;
fsl,magic-packet;
fsl,mii-exclusive;
......
};
重新编译烧写验证可以发现eth1正常。