Using the second CPU ethernet port on Edgerouter-X SFP

I’m currently working with an Edgerouter X SFP, in the hopes of getting full duplex near gigabit out of it. With hardware offloading, 900Mbps half-duplex works now (with NAT and PPPoE), so now I am running into the limitation of having a single Gbit link between the switch and the CPU. Since the CPU has a second ethernet interface, I investigated whether that could be used, as is already done on the ER-X.

After digging through datasheets, DTS files and driver sources, I think it should be possible to make use of this second port (GMAC1).

The MT7621 switch programming manual suggests that PORT 5 on the switch can be configured in a few ways, but the docs and terms are super vague, so I’m not sure I’m understanding this correctly.

  1. Connecting port 5 to GMAC5 (switch side), which I think then connects to GMAC1 (CPU side). I think this connects GMAC1 to the switch, and could be maybe be used a second conduit / uplink (something that DSA now supports, but I read a bit of code in the switch driver that suggests it enables only one port, so maybe this will not fly).
  2. Connecting the GMAC1 to the external PHY, essentially bypassing the switch completely. This would end up with the SFP module GMAC1, which would allow high throughput if the SFP is used as the WAN port.
  3. Connecting GMAC1 to either the eth0 or eth4 PHY (disconnecting that PHY from the switch). This is what ER-X does with eth0, introduced with https://github.com/openwrt/openwrt/pull/10238 I presume this was not done for ER-X, since this probably disables the user of port5 (though I’m not sure if this is necessarily true, maybe GMAC1 can be connected to PHY0 while GMAC5 / switch port 5 is still connected to the external PHY/SFP?)
  4. Connecting GMAC5 / switch port 5 to the external PHY/SFP. This is what is currently the case, meaning the SFP shares bandwith with the other switched ports.

The most obvious one here would seem option 2, where the SFP gets its own CPU port. Is there something that prevents this from working? I think I might figure out how to change the DTS to configure this, but I don’t have an SFP at hand yet to test with :slight_smile:

Option 3 is also interesting. This will likely disable the SFP, but if you do not need that, you get a fast WAN port at eth0. I tried this by uploading the ER-X image to my board (since the changes between them seem to be limited to the setup of the network ports and some PoE GPIOs), but that did not work (it boots ok, I get an eth0 instead of eth0@dsa, and I can receive traffic on that port, but somehow not transmit anything…).

Just in case I missed some difference, I also compiled an ER-X-SFP image from scratch with just the network reconfigured, but that also did not work (not sure if the result is identical and can receive but not TX, since I built a minimal image without opkg so cannot install tcpdump. Will retest with a bigger image later). Edit: seems the self-built ER-X-SFP image (from openwrt master) is slightly different from the official ER-X image (24.10.5), since on my self-built image I do not receive anything on eth0. I rechecked with the ER-X image to see that I did not misinterpret, but I really see data from my network there, and with my own image only outgoing DHCP requests from the ER. So there must be some (relevant) detail I’m missing, or maybe a version difference…*

Built with this diff:


diff --git a/target/linux/ramips/dts/mt7621_ubnt_edgerouter-x-sfp.dts b/target/linux/ramips/dts/mt7621_ubnt_edgerouter-x-sfp.dts
index 9f1e0a8d20..4aaed91a46 100644
--- a/target/linux/ramips/dts/mt7621_ubnt_edgerouter-x-sfp.dts
+++ b/target/linux/ramips/dts/mt7621_ubnt_edgerouter-x-sfp.dts
@@ -4,12 +4,6 @@ / {
        model = "Ubiquiti EdgeRouter X SFP";
        compatible = "ubnt,edgerouter-x-sfp", "mediatek,mt7621-soc";
 
-       sfp_eth5: sfp_eth5 {
-               compatible = "sff,sfp";
-               i2c-bus = <&i2c>;
-               mod-def0-gpio = <&expander0 5 GPIO_ACTIVE_LOW>;
-               maximum-power-milliwatt = <1000>;
-       };
 };
 
 &i2c {
@@ -52,12 +46,6 @@ sfp_i2c_clk_gate {
        };
 };
 
-&mdio {
-       ephy7: ethernet-phy@7 {
-               reg = <7>;
-               sfp = <&sfp_eth5>;
-       };
-};
 
 &rgmii2_pins {
        rgmii2 {
@@ -65,16 +53,24 @@ rgmii2 {
        };
 };
 
+
+&gmac1 {
+       status = "okay";
+       openwrt,netdev-name = "eth0";
+       phy-handle = <&ethphy0>;
+
+       nvmem-cells = <&macaddr_factory_22 0>;
+       nvmem-cell-names = "mac-address";
+};
+
+&ethphy0 {
+       /delete-property/ interrupts;
+};
+
 &switch0 {
        ports {
-               port@5 {
-                       reg = <5>;
-                       openwrt,netdev-name = "eth5";
-                       phy-handle = <&ephy7>;
-                       phy-mode = "rgmii-rxid";
-                       nvmem-cells = <&macaddr_factory_22 5>;
-                       nvmem-cell-names = "mac-address";
-                       /delete-property/ label;
+               port@0 {
+                       status = "disabled";
                };
        };
 };

One additional part of my mystery is why the ER-X-SFP DTS configures the rgmii2 pins as gpio? If this is really needed, that could explain why the ER-X does not work. But arent the rgmii2 pins connected to the SFP and thus must be rgmii? Or is that the rgmii1 pins? (I thought those would be for the first CPU ethernet port / gmac0, but just realized that that is hardwired to the switch port 6, so there is actually a spare set of rgmii pins here. Could it be that one set is wired to GMAC1 (CPU) and one set to GMAC5 (switch)? So then option 2 above might not be possible, if the PHY is connected to the wrong set of rgmii pins? But how can you then use both sets of rgmii pins? No point in having two if you can use only one?

The MT7621 datasheet suggests the rgmii2 pins are muxed with GPIO#22-33 (which is confirmed by /sys/kernel/debug/pinctrl files). /sys/kernel/debug/pinctrl/pinctrl-mtmips-pinctrl/gpio-ranges suggests GPIO#0 is called 512 in Linux GPIO numbering, so this is 534-545, but there are no GPIOs in use in that range according to /sys/kernel/debug/gpio

The only two extra GPIOs that the ER-X-SFP DTS defines are for the SFP clock gating (gpio 7 on &gpio, which shows up as 512+7=519 as expected in the debug output), and the SFP mod-def0 hotplug detect, which is connected to the GPIO expander, so does not need any of the on-soc GPIOs. Also the GPIO expander seems to use only interrupt 8 on &gpio, which I presume also maps to gpio pin 8+512 (yup, gpio-mt7621.c confirms that each GPIO is usable as an interrupt, so they should be numbered the same…).

gigabit half-duples is non-standard....

The physical link is gigabit full duplex. It is the routing that is "half duplex" -- this is a quirk of the ER-X devices. The routing performance of the ER-X (even using the vendor firmware) is limited to 1Gbps total instantaneous bandwidth. That is to say in a simple wan/lan configuration, if you have a download running at 750Mbps, your uploads can only reach 250Mbps. The up/down numbers themselves are arbitrary and will change dynamically, but the total of up+down cannot exceed 1Gbps.

This was previously attributed to the way that the switch was connected to the CPU (I'm having trouble finding the relevant article). But more recently, there was a discovery that there was a second link that could unlock symmetric 1G bandwidth and that is what the OP is looking for on the ER-X-SFP.

IIRC, the ER-X (non SFP) has a spare switch port that is actually connected to the CPU (but not used in the vendor firmware and only recently enabled in OpenWrt). However, I seem to recall (and this may be entirely incorrect) that the ER-X-SFP doesn't have that extra link between the switch and the CPU because the port on the switch chip is instead wired to the SFP cage.

MT7621 can not make full gigabit duplex even with soft offload, probably ub measured it and added brakes, you need hw offload (and if you partition lan ports - bridger package)

The ER-X does achieve 1Gbps routing on the vendor firmware when using hardware offload. (It only hits ~250Mbps with that disabled, and some features are incompatible with the hardware offload engine, so ymmv).

I’ve never installed OpenWrt on an er-x myself, so I cannot speak to the bandwidth achieved with different configurations and offload settings.

Correct. Though the ER-X does not use the second link to talk to the switch as well, but reconfigures the SoC to disconnect eth0 from the switch and connect it to the second link / CPU port directly (though parts of the switch might still be involved, I’m not entirely sure about the actual internal signal routing).

Yeah, that is sortof correct. The link is there (in the sense that it is all internal to the SoC), but the external RGMII pins are wired to the SFP cage (via an AR8033 PHY/transceiver). Note that the SFP cage is not currently route into that second CPU port (that would be my option 2 above), but it is routed to switch port 5 instead (which is unused on ER-X). In theory, you could do this and also connect the CPU port to eth0 (like ER-X) at the same time (you would not be using any PHY or MAC twice for this), but in practice I think his is impossible, probably because 1) the RGMII pins are actually used (even if just internally) for this or 2) there are less MAC units than the documentation suggests (there is a GMAC5 and GMAC6 in the switch, and also a GMAC0 and GMAC1 in the CPU, but it could be that these are just the same ones?).

I’m actually hoping that someone more familiar with the SoC could clarify the actual internal connections and muxes a bit, since these are very vague to me. The datasheet only has a block diagram without connections:

So all info about connections comes from the register descriptions. I think the relevant registers are these:

What is especially weird, is that they talk (in csr_p5_intf_sel) about connecting the “P5 interface” to either GMAC5 (seems to make sense - connect the extra switch port to the external GMII pins or maybe the CPU link) or to PHY4/PHY0 (which makes no sense - if I connect the CPU MAC to PHY4/PHY0, then switch port 5 is not involved at all, right?).

Did a bit more testing here: it seems to be rather the other way around. When the GMAC1 → eth0 link is configured on ER-X-SFP (like done on ER-X), then configuring the rgmii2 pins as GPIO actually breaks RX on that port. If I remove the bit of DT below, the rgmii2 pins are muxed as rgmii2 again instead of gpio, and then RX on port eth0-to-CPU starts working again (just like when flashing the ER-X firmware directly). Still no TX, though.

-&rgmii2_pins {
-       rgmii2 {
-               function = "gpio";
-       };

I think this means that this pinctrl controls (only) the CPU access to the RGMII pins, and that if the switch port 5 is using the RGMII pins (as is the case in the default ER-X-SFP setup), then the CPU should reconfigure the pinmux to GPIO to prevent conflicts? But then this would also mean that the actual RGMII (external) pins are used (or at least internally wired to) the RGMII pins between GMAC1 and PHY0 when configured.

This would actually also explain why TX is not working - likely the AR8033 is driving the same pins as the GMAC1 now. I can see if I can force that into reset, but that will take a bit of care (no time now).

I think this thread is what you're thinking of - reading the list of devices that can't use that setup isn't positive for the ER/X SFP though...

1 Like

I think this thread is what you're thinking of - reading the list of devices that can't use that setup isn't positive for the ER/X SFP though...

From the list in the link in the first post is the “Devices That Can't Benefit This” with an expandable list. Third section in the list states:

RGMII2 pins are used with an external phy on these:

  • mt7621_gnubee_gb-pc2.dts
  • mt7621_mikrotik_routerboard-760igs.dts
  • mt7621_ubnt_edgerouter-x-sfp.dts
  • mt7621_zyxel_wap6805.dts

The third item there is the ER/X SFP.

Ah, seems that is a companion thread to the pull request that introduced this optimization for ER-X that I linked before. Interesting to get a bit more historical context, but most of the things there I already knew from the PR or otherwise.

One new thing here is that apparently @arinc9 actually tried this rerouting to PHY0/PHY4 on ER-X-SFP already, and also found it does not work (not even when forgoing the SFP or other functionality), which seems to match my testing so far.

@arinc9, can you maybe confirm my understanding that the reason it does not work on ER-X-SFP, is that the external RGMII pins on the SoC are always connected to the internal RGMII bus (which is used between GMAC1/5 and PHY0/4 in case of this muxing setup), so anything connected to the external pins (like the AR8033 PHY / SFP slot in the case of ER-X-SFP) messes up the internal signals? If so, did you consider clearing the EXT_PHY_P5 bit in the switch registers (which I think is default on)? Maybe that controls a switch/mux that disconnects the external pins?

Also, I found this comment, suggesting that @arinc9 was working on getting the second CPU port into the DSA subsystem in 2023, and this comment from 2024, so the CPU will have multiple links to the swtich and DSA can handle any connections between physical ports and CPU ports automatically. This is not a solution for ER-X-SFP, since the switch does not have enough ports for 5x RJ45 + SFP + 2x CPU, but would be a great solution for ER-X (and other devices), since it could in theory be controlled at runtime (preserving max switch performance as well if eth0/wan is bridged into the lan, which is this issue).

Looking around, it seems their kernel patches for this are already accepted upstream at https://lwn.net/Articles/956179/, https://lwn.net/Articles/960038/, https://lwn.net/Articles/962520/ and https://lwn.net/Articles/970525/, included since kernel 6.8.

@arinc9 I’m actually interested in looking into this as well (on the condition of finding some time…). You mentioned the kernel part is done, but there is some uci work to do, can you comment on that? Would it not be possible to handle this entirely with existing tools? AFAICS, if DSA is smart enough to handle multiple CPU uplinks, then on a WAN/LAN setup it could create two VLANs in the switch and route each of them to either GMAC0 or GMAC1, and with a LAN-only bridge, it could just put everything in the same VLAN (and use only GMAC0)? So instead of hardwiring GMAC1 to PHY0/PHY4/external PHY/port 5, just always wire it to port5 and do the same “muxing” (and more) via the switch instead? If that could work, then I think no uci work is needed, right?

And do you maybe have an example DTS file lying around (ideally for ER-X) that sets up this second CPU link to the switch? I expect I’ll be able to write it, but if you have one that could be easier.

UCI support is for being able to configure it using uci. You can change the affinity of a DSA user interface right now. Check out the ip-link manpage.

I remember finding that PHY register to be useless.

Yes, I believe the problem is because the signals interfere.

In the device-tree, describe the relevant PHY as connected to the GMAC without interrupt definitions.

Thanks, this is helpful info :slight_smile:

UCI support is for being able to configure it using uci. You can change the affinity of a DSA user interface right now. Check out the ip-link manpage.

Ah, did not realize this was manually set. Would be nice if DSA could handle this automatically, but I guess that would be easy in a 2-conduit 2-VLAN/bridge setup, but as soon as things get more complex, having explicit control over the port( or bridge)-to-conduit mapping is useful indeed.

I remember finding that PHY register to be useless.

Bummer.

In the device-tree, describe the relevant PHY as connected to the GMAC without interrupt definitions.

That sounds like the approach for muxing PHY0/PHY4? For connecting the second conduit, I would expect connecting switch port 5 to gmac1, like port 6 is now connected to gmac0 (no PHY involved)?

Give it a bit more thought and you’ll figure out why this is illogical.

Muxing is a driver operation to configure the switch. This actually is supposed to be a userspace operation but currently the driver treats phy0/4 device-tree definitions differently to do muxing.

I did (and read up on DSA some more), but haven’t figured this out yet. If there are two conduit interfaces available, and there are two bridges configured on the switch, then it would be efficient if the conduit affinity for ports in one bridge is the first conduit and the ports in the second swithc use the second conduit. This seems more efficient than the current default of everything using the first conduit (module details regarding offloading, maybe).

However, this would be a somewhat unpredictable default and maybe hard to reconfigure manually, so I guess the current simple default (everything on the first conduit) plus manual config is probably a better idea in the global picture (maybe this is what you mean with illogical?).

I’m not sure I expressed myself clearly. I mean that your suggestion for DT (“In the device-tree, describe the relevant PHY as connected to the GMAC without interrupt definitions”) sounds like configuring the PHY0/4 mapping.

What I was trying to do (and what I think you’re saying is now supported by the kernel), is to configure a second conduit. AFAICS there is no PHY involved, and the DT should tie gmac1 to switch port 5? More specifically, this is what I tried (on ER-X):

❯ cat target/linux/ramips/dts/mt7621_ubnt_edgerouter-x.dts
#include "mt7621_ubnt_edgerouter-x.dtsi"

/ {
        model = "Ubiquiti EdgeRouter X";
        compatible = "ubnt,edgerouter-x", "mediatek,mt7621-soc";
};

&gmac1 {
        status = "okay";
        openwrt,netdev-name = "dsa2";

        nvmem-cells = <&macaddr_factory_22 5>;
        nvmem-cell-names = "mac-address";

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

&switch0 {
        ports {
                port@5 {
                        reg = <5>;
                        ethernet = <&gmac1>;
                        phy-mode = "rgmii";

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

This gives me a dsa2 interface that looks ok, but trying to configure the affinity fails:

root@OpenWrt:~# ip link set eth4 type dsa master dsa2
Driver does not support changing DSA conduit

This makes sense, since mt7530.c does not define the port_change_conduit dsa operation, which seems to be required for this. It took me a while to find this patch by Richard van Schagen, submitted by you (I was looking for port_change_conduit and this still has port_change_master), so before I found the patch, I whipped up something pretty similar and applied that.

With that, it seems to work as expected!

One caveat is that after I change the affinity of a port, the port stops working for a (couple of) dozen of seconds. I suspect there might be a switch address table or something like that that might need to be updated/cleared here? edit: When testing the patch below, this no longer happened (tested once). Not sure why, since the patch should be functionally the same, but maybe there is some relevant difference I did not see.

To prove it actually works as expected, I changed wan to eth1 and ran:

ip link set eth1 type dsa master dsa2

And then the throughput went from 900Mbps to 1.8Gbps between wan (eth1) and lan (eth2), which the existing phy0/4 mapping could not allow.

For reference, here is the original patch by @richard, updated to the new conduit naming and taking into account the patch feedback (alignment, add LAG check, remove CPU port matrix writes):

diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 950cad343ce7f..9b84c5f32b176 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -1311,6 +1311,37 @@ mt7530_port_disable(struct dsa_switch *ds, int port)
                mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS);
 }
 
+static int
+mt7530_port_change_conduit(struct dsa_switch *ds, int port,
+                          struct net_device *conduit,
+                          struct netlink_ext_ack *extack)
+{
+       struct mt7530_priv *priv = ds->priv;
+       struct dsa_port *dp = dsa_to_port(ds, port);
+       struct dsa_port *cpu_dp = conduit->dsa_ptr;
+       int old_cpu = dp->cpu_dp->index;
+       int new_cpu = cpu_dp->index;
+
+       if (netif_is_lag_master(conduit)) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Driver does not support LAG DSA conduit");
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&priv->reg_mutex);
+
+       /* Move old to new cpu on User port */
+       priv->ports[port].pm &= ~PCR_MATRIX(BIT(old_cpu));
+       priv->ports[port].pm |= PCR_MATRIX(BIT(new_cpu));
+
+       mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+                  priv->ports[port].pm);
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
 static int
 mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
 {

After creating this updated patch (haven’t tested it yet - still compiling edit: Tested, works), I found a v2 of the patch by Richard, which is pretty much identical (except it still uses the “master” terminology and a slightly different LAG error message). That v2 got one reply suggesting to tackle MAC filtering first and other replies, so I presume this is why it was never merged?

@arinc9 I assume you were aware of this missing patch? Did you have an approach in mind? Add the patch to OpenWRT first?

Yes, however, it doesn't have to be a manual config. It just has to be done on userspace.

It's still in my task list to do the requirements to support affinity change on MT7530 DSA subdriver. I'm simply focused on other things right now and will be so for a long time. And no, I don't like out-of-tree patches and I think OpenWrt must gradually get rid of all of them.

Oh, I wasn’t wondering about the when of things (and definitely not trying to add pressure), but more wondering about the what, thinking that I could maybe contribute some code to move things forward. However, thinking on this a bit more, I think I should be wise and put down this project again (not sinking even more time into it), and continue actually putting my ER-X to use with the current interface mapping, which is sufficient for my needs. Maybe someone else can benefit from the stuff I wrote here, at least it mostly satisfied my curiosity.

One last thing I’ll try to do is update the wiki to document the current bandwidth limits and switch differences between ER-X and ER-X-SFP, since that will definitely benefit others.

One other thing I tried yesterday, was to modify the ER-X-SFP devicetree to put the SFP slot on a dedicated CPU link (GMAC1), which was the original topic of this thread, but I could not get this to work. Maybe the hardware cannot do this, or maybe I just did not find the right settings. For anyone that is interested, I tried the below DT, and (using this patch), fiddled with the MHWTRAP and PMCR registers, but without any traffic appearing on gmac1.

cat target/linux/ramips/dts/mt7621_ubnt_edgerouter-x-sfp.dts

#include "mt7621_ubnt_edgerouter-x.dtsi"

/ {
	model = "Ubiquiti EdgeRouter X SFP";
	compatible = "ubnt,edgerouter-x-sfp", "mediatek,mt7621-soc";

	sfp_eth5: sfp_eth5 {
		compatible = "sff,sfp";
		i2c-bus = <&i2c>;
		mod-def0-gpio = <&expander0 5 GPIO_ACTIVE_LOW>;
		maximum-power-milliwatt = <1000>;
	};
};

&i2c {
	status = "okay";

	/*
	 * PCA9655 GPIO expander
	 *  0-POE power port eth0
	 *  1-POE power port eth1
	 *  2-POE power port eth2
	 *  3-POE power port eth3
	 *  4-POE power port eth4
	 *  5-SFP_MOD_DEF0#
	 *  6-
	 *  7-
	 *  8-Pull up to VCC
	 *  9-Pull down to GND
	 * 10-Pull down to GND
	 * 11-Pull down to GND
	 * 12-Pull down to GND
	 * 13-Pull down to GND
	 * 14-Pull down to GND
	 * 15-Pull down to GND
	 */
	expander0: pca9555@25 {
		compatible = "nxp,pca9555";
		interrupt-parent = <&gpio>;
		interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
		gpio-controller;
		#gpio-cells = <2>;
		reg = <0x25>;
	};
};

&gpio {
	sfp_i2c_clk_gate {
		gpio-hog;
		gpios = <7 GPIO_ACTIVE_LOW>;
		output-high;
	};
};

&mdio {
	ephy7: ethernet-phy@7 {
		reg = <7>;
		sfp = <&sfp_eth5>;
	};
};

&gmac1 {
	status = "okay";
	openwrt,netdev-name = "gmac1";
	phy-handle = <&ephy7>;
	phy-mode = "rgmii-rxid";

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

The requirements are clear. You can implement them and submit a patch to netdev.

Did you check exapmle 6 from https://elixir.bootlin.com/linux/v6.12.74/source/Documentation/devicetree/bindings/net/mediatek,net.yaml docs?

# Example 6: MT7621: mux external phy to SoC's gmac1
  - |
    #include <dt-bindings/interrupt-controller/mips-gic.h>
    #include <dt-bindings/reset/mt7621-reset.h>

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

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

        mac@1 {
            compatible = "mediatek,eth-mac";
            reg = <1>;

            phy-mode = "rgmii";
            phy-handle = <&example6_ethphy7>;
        };

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

            /* External PHY */
            example6_ethphy7: ethernet-phy@7 {
                reg = <7>;
                phy-mode = "rgmii";
            };

            switch@1f {
                compatible = "mediatek,mt7621";
                reg = <0x1f>;

                mediatek,mcm;
                resets = <&sysc MT7621_RST_MCM>;
                reset-names = "mcm";

                interrupt-controller;
                #interrupt-cells = <1>;
                interrupt-parent = <&gic>;
                interrupts = <GIC_SHARED 23 IRQ_TYPE_LEVEL_HIGH>;

                ethernet-ports {
                    #address-cells = <1>;
                    #size-cells = <0>;

                    port@0 {
                        reg = <0>;
                        label = "lan1";
                    };

                    port@1 {
                        reg = <1>;
                        label = "lan2";
                    };

                    port@2 {
                        reg = <2>;
                        label = "lan3";
                    };

                    port@3 {
                        reg = <3>;
                        label = "lan4";
                    };

                    port@4 {
                        reg = <4>;
                        label = "wan";
                    };

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

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

Keypoints:

For the multi-chip module MT7530, in case of an external phy wired to
      gmac1 of the SoC, port 5 must not be enabled.

      In case of muxing PHY 0 or 4, the external phy must not be enabled.

      For the MT7621 SoCs, rgmii2 group must be claimed with rgmii2 function.

      Check out example 6.

Thanks, I had not looked at the bindings docs yet, had not expected them to be so extensive (note that you pasted the wrong link, the example is in https://www.kernel.org/doc/Documentation/devicetree/bindings/net/dsa/mediatek%2Cmt7530.yaml).

Good to see this confirms that connecting gmac1 to the external PHY should be possible. However, it seems the DT I’ve tried is effectively the same as example 6. Comparing it:

  • I have no explicit rgmii2 pinctrl, because I understood that was the default (so the ER-X-SFP DT changes it to gpio explicitly because then gmac1 is not involved). Checking this, in mt7621.dtsi rgmii2 is indeed set explicitly in rgmii2_pins node attached to the ethernet node, as shown in the example. I also checked in debugfs that the pins were indeed assigned to rgmii2.
  • I noticed my DT used rgmii-rxid on the external PHY, while the example uses rgmii. However, this was copied from the ER-X-SFP DT, which works when the external PHY is connected to port5, so I presume it should work here. rgmii-rxid implies that the external PHY needs to supply delay on the RX line and the delay on the TX line is hardcoded somewhere (PCB or internal lines). There is a chance that these internal delays are different when connecting to gmac1 instead of port5, but when I draw the RX/TX mux matrix that I think must exist in the SOC, and derive where the delays must be, then I think they are the same on both GMAC5/Port5 and GMAC1 (delay on their TX line - so you can also connect them together with rgmii mode as I did on the ER-X).
    Just in case I was wrong, I tried the other modes as well (rgmii, rgmii-txid, rgmii-id), but also not data transfer happening.