How to Configure RTL8211 to mt7621 Device Tree

I have a board with mt7621 cpu, which has GE2_TX / GE2_RX connected to RTL8211 chip:

MDC
MDIO
GE2_TXD0
GE2_TXD1
GE2_TXD2
GE2_TXD3
GE2_TXEN
GE2_TXCLK
GE2_RXD0
GE2_RXD1
GE2_RXD2
GE2_RXD3
GE2_RXDV
GE2_RXCLK

image

&eth {
	status = "okay";

	pinctrl-names = "default";
	pinctrl-0 = <&rgmii1_pins &rgmii2_pins>;

	gmac0: mac@0 {
		compatible = "mediatek,eth-mac";
		mtd-mac-address = <&factory 0xe000>;
		reg = <0>;
		phy-mode = "rgmii";
		fixed-link {
			speed = <1000>;
			full-duplex;
			pause;
		};
    };

	gmac1: mac@1 {
		compatible = "mediatek,eth-mac";
		mtd-mac-address = <&factory 0xe006>;
		reg = <1>;
		phy-mode = "rgmii";
		fixed-link {
			speed = <1000>;
			full-duplex;
			pause;
		};
	};

	mdio: mdio-bus {
		#address-cells = <1>;
		#size-cells = <0>;

		phy1f: ethernet-phy@1f {
			reg = <0x1f>;
			phy-mode = "rgmii";
		};
	};
};

Is it configured to the ethernet node of the device tree, how should I configure it? Is there a similar one I can refer to as an example?

Oh, I need configure phy mdio on DTS.

You need to know RTL8211 phy address on mdio bus. Example for phy address 7:

		compatible = "mediatek,eth-mac";
		mtd-mac-address = <&factory 0xe006>;
		reg = <1>;
		phy-mode = "rgmii";
		phy-handle = <&phy7>;
	};

	mdio: mdio-bus {
		#address-cells = <1>;
		#size-cells = <0>;

		phy7: ethernet-phy@7 {
			reg = <0x07>;
			phy-mode = "rgmii";
		};
	};

and install realtek phy driver. Very often you need to set rgmii tx internal delay explicitly: phy-mode = "rgmii-txid";

1 Like

I understand that GE2 is the second Gigabit Ethernet for the mt7621 CPU.
When I configure the mdio on it, it is as follows:


#include "mt7621.dtsi"

...

&gmac0 {
        nvmem-cells = <&macaddr_factory_e000>;
        nvmem-cell-names = "mac-address";
};

&gmac1 {
        status = "okay";

        nvmem-cells = <&macaddr_factory_e006>;
        nvmem-cell-names = "mac-address";
};

&mdio {
        phy5: ethernet-phy@0 {
                reg = <0x05>;
                phy-mode = "rgmii-txid";
        };
};

&switch0 {
        status = "okay";

        ports {
                port@0 {
                        status = "okay";
                        label = "wan";
                        phy-mode = "rgmii-txid";
                        phy-handle = <&phy5>;

                        nvmem-cells = <&macaddr_factory_e000>;
                        nvmem-cell-names = "mac-address";
                        mac-address-increment = <1>;
                };

                port@1 {
                        status = "okay";
                        label = "lan1";
                };
...

Configured mdio phy5, port@0 using phy-handle = <&phy5>, no wan NICs appear in this configuration, am I misconfiguring?

you still didn't tell the mac driver about the phy link with a phy-handle property like @123serge123 showed you

Removing phy-mode = "rgmii-txid" from the port@0 node brings up the wan NIC, but it doesn't have an IP. is it ok to configure the DTS this way?

The DTS configuration is as follows:

#include "mt7621.dtsi"

...

&gmac0 {
        nvmem-cells = <&macaddr_factory_e000>;
        nvmem-cell-names = "mac-address";
};

&gmac1 {
        status = "okay";

        nvmem-cells = <&macaddr_factory_e006>;
        nvmem-cell-names = "mac-address";
};

&mdio {
        phy5: ethernet-phy@0 {
                reg = <0x05>;
                phy-mode = "rgmii-txid";
        };
};

&switch0 {
        status = "okay";

        ports {
                port@0 {
                        status = "okay";
                        label = "wan";
                        phy-handle = <&phy5>;

                        nvmem-cells = <&macaddr_factory_e000>;
                        nvmem-cell-names = "mac-address";
                        mac-address-increment = <1>;
                };
...

The MT7621 external RGMII interface is an infinite source of confusion. You need to figure out if the phy is supposed to be connected to the switch or the 2nd gmac. It seems to me that you want the latter, but I could be wrong there. I still have problems bending my head around this.

See the great explanation and pointer @Thirsty gave here: Which registers to use to enable RGMII2 on the MT7621 - #3 by telecom21

Thanks for the response. RTL8211FS use Fiber Optic to RGMII Mode, fiber port replaces the WAN port.

How should I configure the device tree for this scenario? Is it already supported by current openwrt?

Is it necessary to add a port@5 in the DTS for the MT7621 to support this mode?

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v5.4-rc3&id=4f358cbd054d905329718443a4dd781d26ea1079

I didn't see port@5 in the mt7621.dtsi file. Can it take effect if I add it directly?

The answer depends on how you connected the phy. The DTS is supposed to describe your hardware.

You'll find examples of both the possible variants in the tree:

1 Like

The MT7621 RGMII schematic is as follows

Does it mean gmac0 or gmac1 or port@5 of the corresponding device tree?

You can see more examples in kernel docs

1 Like

There is no phy in that schematic. Not sure how you thought it would help anyone else when it didn't help you. Do you think the schematic looks different in my end?

I found a Chinese reference, it's not the MT7628 CPU, it uses the same RTL8211FS-CG.

The device tree is updated as follows:

&mdio {
        ephy5: ethernet-phy@5 {
                compatible = "ethernet-phy-ieee802.3-c22";
                reg = <5>;
        };
};

&gmac1 {
        status = "okay";

        label = "sfp";
        phy-mode = "rgmii-rxid";

        phy-handle = <&ephy5>;

        nvmem-cells = <&macaddr_factory_e000>;
        nvmem-cell-names = "mac-address";
        mac-address-increment = <2>;
};

Flush image to the device, there appears the sfp network card:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1504 qdisc fq_codel state UP qlen 1000
    link/ether 02:dc:f9:92:c4:4d brd ff:ff:ff:ff:ff:ff
    inet6 fe80::dc:f9ff:fe92:c44d/64 scope link 
       valid_lft forever preferred_lft forever
3: sfp: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
    link/ether 9a:fb:48:1f:bb:e7 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::98fb:48ff:fe1f:bbe7/64 scope link 
       valid_lft forever preferred_lft forever

When I plug in and unplug the fiber, the system has a message:

[  577.574074] mt7530 mdio-bus:1f wan: Link is Down
[  579.093948] mtk_soc_eth 1e100000.ethernet sfp: Link is Down
[  579.313868] mtk_soc_eth 1e100000.ethernet sfp: PHY [mdio-bus:05] driver [RTL8211F Gigabit Ethernet] (irq=POLL)
[  579.323973] mtk_soc_eth 1e100000.ethernet sfp: configuring for phy/rgmii-rxid link mode
...
[  825.094029] mtk_soc_eth 1e100000.ethernet sfp: Link is Up - 100Mbps/Full - flow control off
[  825.094658] mt7530 mdio-bus:1f wan: Link is Up - 100Mbps/Full - flow control off
[  825.102404] IPv6: ADDRCONF(NETDEV_CHANGE): sfp: link becomes ready

The sfp NIC doesn't have an IP address, do I need to configure a switch for sfp?

In my case it's Example 6:

I configured sfp to ./target/linux/ramips/mt7621/base-files/etc/board.d/02_network, as follow:

ramips_setup_interfaces()
{
        local board="$1"

        case $board in
        my-board|\
                ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "sfp"
                ;;
...

The DTS as follow:

&gmac0 {
        nvmem-cells = <&macaddr_factory_e000>;
        nvmem-cell-names = "mac-address";
};


&mdio {
        ephy5: ethernet-phy@5 {
                compatible = "ethernet-phy-ieee802.3-c22";
                reg = <5>;
        };
};

&gmac1 {
        status = "okay";

        label = "sfp";
        phy-mode = "rgmii-rxid";

        phy-handle = <&ephy5>;

        nvmem-cells = <&macaddr_factory_e000>;
        nvmem-cell-names = "mac-address";
        mac-address-increment = <2>;
};


&switch0 {
        status = "okay";

        ports {
                port@0 {
                        status = "okay";
                        label = "lan1";
                };

                port@1 {
                        status = "okay";
                        label = "wan";

                        nvmem-cells = <&macaddr_factory_e000>;
                        nvmem-cell-names = "mac-address";
                        mac-address-increment = <1>;
                };

                port@2 {
                        status = "okay";
                        label = "lan2";
                };

                port@3 {
                        status = "okay";
                        label = "lan3";
                };

                port@4 {
                        status = "okay";
                        label = "lan4";
                };

                port@6 {
                        ethernet = <&gmac0>;
                        phy-mode = "trgmii";

                        fixed-link {
                                speed = <1000>;
                                full-duplex;
                                pause;
                        };
                };
        };
};

Computer connected to lan port, and run ping command:

% ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2
Request timeout for icmp_seq 3
ping: sendto: No route to host
Request timeout for icmp_seq 4
ping: sendto: Host is down
Request timeout for icmp_seq 5
ping: sendto: Host is down
Request timeout for icmp_seq 6
...

Is it misconfigured somewhere?

Oh, Since the kernel does not have an rtl8211f driver, patch it and configure the device tree as follows:

&mdio {
    #address-cells = <1>;
    #size-cells = <0>;

    ephy5: ethernet-phy@5 {
        reg = <5>;
        phy-mode = "rgmii-rxid";
        realtek,mode = "fiber";
    };
};

Then you can get the IP, thank you all.

The realtek.c patch:

--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -15,6 +15,7 @@
  */
 #include <linux/phy.h>
 #include <linux/module.h>
+#include <linux/delay.h>

 #define RTL821x_PHYSR                          0x11
 #define RTL821x_PHYSR_DUPLEX                   BIT(13)
@@ -51,6 +52,15 @@
 #define RTL8366RB_POWER_SAVE                   0x15
 #define RTL8366RB_POWER_SAVE_ON                        BIT(12)

+#define RTL8211FS_FIBER_ESR                    0x0F
+#define RTL8211FS_MODE_MASK                    0xC000
+
+enum rtl8211fs_mode {
+       RTL8211FS_MODE_COPPER = 0,
+       RTL8211FS_MODE_FIBER = 1,
+       RTL8211FS_MODE_AUTO = 2
+};
+
 #define RTL_SUPPORTS_5000FULL                  BIT(14)
 #define RTL_SUPPORTS_2500FULL                  BIT(13)
 #define RTL_SUPPORTS_10000FULL                 BIT(0)
@@ -127,21 +137,115 @@
        return err;
 }

-static int rtl8211f_config_init(struct phy_device *phydev)
+static int rtl8211f_wait_for_ready(struct phy_device *phydev)
+{
+       udelay(1000);
+       return 0;
+}
+
+static int rtl8211f_phy_read(struct phy_device *phydev, u16 page, u32 regnum)
 {
-       u16 txdly = 0, rxdly = 0;
        u16 val;
+       u16 old_page;

-       /* set to page 0xa43 */
-       phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43);
+       old_page = phy_read(phydev, RTL8211F_PAGE_SELECT);

-       val = phy_read(phydev, RTL8211F_PHYCR1);
-       val |= RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_XTAL_OFF;
+       phy_write(phydev, RTL8211F_PAGE_SELECT, page);
+       rtl8211f_wait_for_ready(phydev);
+       val = phy_read(phydev, regnum);
+       rtl8211f_wait_for_ready(phydev);

-       phy_write(phydev, RTL8211F_PHYCR1, val);
+       phy_write(phydev, RTL8211F_PAGE_SELECT, old_page);
+       rtl8211f_wait_for_ready(phydev);

-       /* restore to default page 0 */
-       phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
+       return val;
+}
+
+static int rtl8211f_phy_write(struct phy_device *phydev, u16 page, u32 regnum, u16 val)
+{
+       int ret;
+       u16 old_page;
+
+       old_page = phy_read(phydev, RTL8211F_PAGE_SELECT);
+
+       phy_write(phydev, RTL8211F_PAGE_SELECT, page);
+       rtl8211f_wait_for_ready(phydev);
+       phy_write(phydev, regnum, val);
+       rtl8211f_wait_for_ready(phydev);
+
+       phy_write(phydev, RTL8211F_PAGE_SELECT, old_page);
+       ret = rtl8211f_wait_for_ready(phydev);
+       return ret;
+}
+
+static int rtl8211f_phy_soft_reset(struct phy_device *phydev)
+{
+       u16 val;
+
+       dev_info(&phydev->dev, "soft reset.\n");
+
+       val = rtl8211f_phy_read(phydev, 0, 0);
+
+       val |= BIT(15);
+
+       phy_write(phydev, 0, val);
+
+       return rtl8211f_wait_for_ready(phydev);
+}
+
+static int rtl8211f_mode(struct phy_device *phydev)
+{
+       u16 val;
+
+       val = phy_read(phydev, RTL8211FS_FIBER_ESR);
+       val &= RTL8211FS_MODE_MASK;
+
+       if(val)
+               return RTL8211FS_MODE_FIBER;
+       else
+               return RTL8211FS_MODE_COPPER;
+}
+
+static int rtl8211f_set_mode(struct phy_device *phydev, enum rtl8211fs_mode mode)
+{
+       u16 val;
+
+       dev_info(&phydev->dev, "force set %s mode !", mode == RTL8211FS_MODE_AUTO? "auto": mode == RTL8211FS_MODE_FIBER? "fiber" : "copper");
+
+       val = rtl8211f_phy_read(phydev, 0xd40, 16);
+
+       val &= (u16)(~RTL8211FS_MODE_MASK);
+
+       val |= mode;
+
+       rtl8211f_phy_write(phydev, 0xd40, 16, val);
+
+       return rtl8211f_wait_for_ready(phydev);
+}
+
+static void rtl8211f_disable_broadcast(struct phy_device *phydev)
+{
+       u16 phy_val;
+
+       /* Disable response on MDIO addr 0 (!) */
+       phy_val = rtl8211f_phy_read(phydev, 0xa43, 24);
+       phy_val &= ~(1<<13);    // PHYAD_0 Disable
+       rtl8211f_phy_write(phydev, 0xa43, 24, phy_val);
+}
+
+static int rtl8211f_set_rgmii_delay(struct phy_device *phydev)
+{
+       u16 txdly = 0, rxdly = 0;
+       u16 val;
+
+       if (rtl8211f_mode(phydev) == RTL8211FS_MODE_FIBER) {
+               dev_info(&phydev->dev, "phy working in fiber mode");
+               //rtl8211f_phy_write(phydev, 0, 0, 0x9100);
+       } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_COPPER) {
+               dev_info(&phydev->dev, "phy working in copper mode");
+       } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_AUTO) {
+               dev_info(&phydev->dev, "phy working in auto mode");
+       }

        switch (phydev->interface) {
        case PHY_INTERFACE_MODE_RGMII:
@@ -150,8 +254,8 @@
                break;

        case PHY_INTERFACE_MODE_RGMII_RXID:
-               txdly = 1;
-               rxdly = 0;
+               txdly = 0;
+               rxdly = 1;
                break;

        case PHY_INTERFACE_MODE_RGMII_TXID:
@@ -168,29 +272,125 @@
                return 0;
        }

-       /* set to page 0xd08 */
-       phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08);
-
-       if(txdly) {
-               /* enable TXDLY */
-               val = phy_read(phydev, 0x15);
+       /* Rx Delay */
+       val = rtl8211f_phy_read(phydev, 0xd08, 0x15);
+       val &= ~RTL8211F_RX_DELAY;
+       if(rxdly) {
                val |= RTL8211F_RX_DELAY;
-               phy_write(phydev, 0x15, val);
        }
+       rtl8211f_phy_write(phydev, 0xd08, 0x15, val);

-       if(rxdly) {
-               /* enable RXDLY */
-               val = phy_read(phydev, 0x11);
+       /* Tx Delay */
+       val = rtl8211f_phy_read(phydev, 0xd08, 0x11);
+       val &= ~RTL8211F_TX_DELAY;
+       if(txdly) {
                val |= RTL8211F_TX_DELAY;
-               phy_write(phydev, 0x11, val);
        }

-       /* restore to default page 0 */
-       phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
+       rtl8211f_phy_write(phydev, 0xd08, 0x11, val);
+
+       dev_info(&phydev->dev, "RGMII Rx delay %s, Tx delay %s.\n", rxdly?"enabled":"disable", txdly?"enabled":"disable");
+
+       return 0;
+}
+
+int rtl8211f_read_status(struct phy_device *phydev)
+{
+       u16 val;
+
+       if (rtl8211f_mode(phydev) == RTL8211FS_MODE_COPPER)
+               return genphy_read_status(phydev);
+
+       val = rtl8211f_phy_read(phydev, 0xdf0, 0x10);
+
+       /* for fiber status */
+       if(val & BIT(12)) {
+               phydev->pause = 0;
+               phydev->asym_pause = 0;
+
+               phydev->link = 1;
+               phydev->duplex = DUPLEX_FULL;
+               if(!(val & BIT(5)) && (val & BIT(4))) {
+                       phydev->speed = SPEED_100;
+               } else if ((val & BIT(5)) && !(val & BIT(4))) {
+                       phydev->speed = SPEED_1000;
+               } else {
+                       phydev->speed = SPEED_1000;
+               }
+       } else
+               phydev->link = 0;

        return 0;
 }

+int rtl8211f_config_aneg(struct phy_device *phydev)
+{
+       rtl8211f_set_rgmii_delay(phydev);
+
+       return genphy_config_aneg(phydev);
+}
+
+static int rtl8211f_config_init(struct phy_device *phydev)
+{
+       u16 phy_val;
+       const char *force_mode;
+       int ret;
+
+       struct device *dev = &phydev->dev;
+       struct device_node *of_node = dev->of_node;
+
+       if (!of_node && dev->parent->of_node)
+               of_node = dev->parent->of_node;
+
+       rtl8211f_disable_broadcast(phydev);
+
+       ret = of_property_read_string(of_node, "realtek,mode", &force_mode);
+
+       if (!ret) {
+               if (!strcmp(force_mode, "fiber"))
+                       rtl8211f_set_mode(phydev, RTL8211FS_MODE_FIBER);
+               else if (!strcmp(force_mode, "copper"))
+                       rtl8211f_set_mode(phydev, RTL8211FS_MODE_COPPER);
+               else
+                       rtl8211f_set_mode(phydev, RTL8211FS_MODE_AUTO);
+       }
+
+       /*  FIXME: no reset need? */
+       rtl8211f_phy_soft_reset(phydev);
+
+       rtl8211f_set_rgmii_delay(phydev);
+#if 0
+       /* Disable Green Ethernet */
+       rtl8211f_phy_write(phydev, 0xa43, 27, 0x8011);
+       rtl8211f_phy_write(phydev, 0xa43, 27, 0x573f);
+
+       /* Enable flow control by default */
+       phy_val = rtl8211f_phy_read(phydev, 0xa43, 4);
+       phy_val |=  (1<<10);            // Enable pause ability
+       rtl8211f_phy_write(phydev, 0xa43, 4, phy_val);
+#endif
+       if (rtl8211f_mode(phydev) == RTL8211FS_MODE_FIBER) {
+               dev_info(&phydev->dev, "phy working in fiber mode");
+       } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_COPPER) {
+               dev_info(&phydev->dev, "phy working in copper mode");
+       } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_AUTO) {
+               dev_info(&phydev->dev, "phy working in auto mode");
+       }
+
+#if 0
+       /*
+        * The rtl8211fs not working under fiber/utp mode, comment it.
+        */
+
+       val = rtl8211f_phy_read(phydev, 0xa43, RTL8211F_PHYCR1);
+       val |= RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_XTAL_OFF;
+       rtl8211f_phy_write(phydev, 0xa43, RTL8211F_PHYCR1, val);
+#endif
+       return 0;
+}
+
 static struct phy_driver realtek_drvs[] = {
        {
                .phy_id         = 0x00008201,
@@ -244,11 +444,11 @@
                .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
                .flags          = PHY_HAS_INTERRUPT,
-               .config_aneg    = &genphy_config_aneg,
+               .config_aneg    = &rtl8211f_config_aneg,
                .config_init    = &rtl8211f_config_init,
-               .read_status    = &genphy_read_status,
                .ack_interrupt  = &rtl8211f_ack_interrupt,
                .config_intr    = &rtl8211f_config_intr,
+               .read_status    = &rtl8211f_read_status,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
                .driver         = { .owner = THIS_MODULE },

Reference:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.