Toradex Aquila AM69 SOM - Serdes2 Lane 3 QSGMII link to VSC8514 PHY - no copper link

Toradex Aquila AM69 SOM - Serdes2 Lane 3 QSGMII link to VSC8514 PHY - no copper link

I trying to get 4 ports communicating on a custom mainboard with a Toradex Aquila AM69 SOM connecting to the VSC8514 Quad Phy.

  • uboot loads the base overlay
  • uboot loads the device tree overlay
  • uboot invokes the nReset pin for the VSC8514 Phy
  • mscc kernel driver goes through the datasheet aligned 12 step process for initialisation of the phy
  • ports appear to come up and show in linux after kernel has loaded.
  • problem: no leds on the 4 ports
  • problem: speed unknown / Duplex Unknown
  • unknown at this stage if there is a5GHz sync on the serdes link to the phy

below are the snippets of the relevant files, dtb, dtbo, and linux command line feedback

===== u-boot - Base and Device tree overlay loaded, then invokes gpio for Phy nReset pin =====

Loading DeviceTree: k3-am69-aquila-rvh.dtb
143478 bytes read in 42 ms (3.3 MiB/s)
56 bytes read in 41 ms (1000 Bytes/s)
Working FDT set to 90200000
Applying Overlay: k3-j784s4-am69-aquila1_1-rvh2-overlay.dtbo
14312 bytes read in 43 ms (324.2 KiB/s)
7929274 bytes read in 74 ms (102.2 MiB/s)

gpio: pin gpio@600000_33 (gpio 130) value is 1
Warning: value of pin is still 0
gpio: pin gpio@600000_33 (gpio 130) value is 0

========= k3-am69-aquila-rvh.dtb =====================

/dts-v1/;

/*

  • ===============================================================
  • BIA5 PEWMAS RVH Base Overlay
  • Board: Remote Video Hub Controlboard P/N 10800202
  •     Rev B
    
  • Base: k3-am69-aquila-rvh.dtb
  • Toradex Base: k3-am69-aquila-dev.dts
  • Aquila AM69A SOM
  • Overlay Revision: 1.2 for Mainboard Rev B
  • Author: Rob Kennedy
  • Date: 21 May 2026
  • RVH QSGMII / VSC8514 hardware mapping
  • ===============================================================
  • Signal | SOM Pin | AM69 V1.1 Func | AM69 V1.1 Pin SoC
  • ===================================================================
  • ETH_2_QSGMII_RX_N B83 SGMII2_RXN0 SERDES2_RX3_N (AT1)
  • ETH_2_QSGMII_RX_P B84 SGMII2_RXP0 SERDES2_RX3_P (AT2)
  • ETH_2_QSGMII_TX_N B86 SGMII2_TXN0 SERDES2_TX3_N (AP1)
  • ETH_2_QSGMII_TX_P B87 SGMII2_TXP0 SERDES2_TX3_P (AP2)
  • ETH_2_INT# B81 GPIO0_44 MCASP1_AXR3 (AL33)
  • PHY_NRESET# B46 GPIO0_33 MCASP0_AXR5 (AG38)
  • COMA-MODE B58 GPIO0_16 MCASP0_AXR0 (AF37)
  • ETH_2_MDC B90 MDIO0_MDC MCASP2_AFSX (AE37)
  • ETH_2_MDIO B89 MDIO0_MDIO MCASP2_AXR0 (AC36)
  • Physical SerDes path:
  • VSC8514 QSGMII → B83/84/86/87 → AM69A SERDES2 lane 3
  • → wiz@5020000 / serdes@5020000 / phy@3
  • → CPSW9G (ethernet@c000000) QSGMII_LANE8
  • → CPSW9G port 8 (QSGMII main), ports 5/6/7 (QSGMII sub)
  • CPSW9G MAC to VSC8514 PHY mapping:
  • port@5 → VSC8514 PHY0 → MDIO 0x04
  • port@6 → VSC8514 PHY1 → MDIO 0x05
  • port@7 → VSC8514 PHY2 → MDIO 0x06
  • port@8 → VSC8514 PHY3 → MDIO 0x07
  • SerDes lane controller (serdes_ln_ctrl) note:
  • The Aquila SOM default idle-states already route SERDES2 lanes 0-3
  • to QSGMII_LANE5-8 respectively. No serdes_ln_ctrl change is needed.
  • Only the SerDes phy type (SGMII → QSGMII) and the CPSW9G port
  • configuration need to be changed.
    ** ============================================================== */

#include “k3-am69-aquila-dev.dts”
#include <dt-bindings/phy/phy.h>

/ {
model = “Aquila AM69 RVH Mainboard”;
compatible = “toradex,aquila-am69”, “ti,j784s4”;

aliases {
	ethernet0 = "/bus@100000/bus@28380000/ethernet@46000000/ethernet-ports/port@1";

	ethernet1 = "/bus@100000/ethernet@c000000/ethernet-ports/port@5";
	ethernet2 = "/bus@100000/ethernet@c000000/ethernet-ports/port@6";
	ethernet3 = "/bus@100000/ethernet@c000000/ethernet-ports/port@7";
	ethernet4 = "/bus@100000/ethernet@c000000/ethernet-ports/port@8";
};

};

/* ================================================================

    1. SerDes2 lane 3: change from SGMII (PHY_TYPE_SGMII = 8) to
  • QSGMII (PHY_TYPE_QSGMII = 9).
  • Physical: AM69A SERDES2_TX3/RX3 → Aquila B83/84/86/87 → VSC8514.
  • cdns,num-lanes = 1 means one 5 Gbps QSGMII lane carrying all 4
  • GbE streams; no other phy node changes needed.
  • ================================================================ */
    &{/bus@100000/wiz@5020000/serdes@5020000/phy@3} {
    /delete-property/ cdns,phy-type;
    cdns,phy-type = <PHY_TYPE_QSGMII>;
    };

/* ================================================================

    1. CPSW9G GMII sel: make port 8 the QSGMII main port.
  • SERDES2 lane 3 → QSGMII_LANE8 → CPSW9G port 8 = main.
  • Port 8 gets J72XX_GMII_SEL_MODE_QSGMII; ports 5/6/7 get
  • J72XX_GMII_SEL_MODE_QSGMII_SUB (share the same 5 Gbps stream).
  • ================================================================ */
    &{/bus@100000/bus@100000/phy@4044} {
    /delete-property/ ti,qsgmii-main-ports;
    ti,qsgmii-main-ports = <8>, <8>;
    };

/* ================================================================

    1. CPSW9G ports 5-8: QSGMII mode, VSC8514 PHY handles.
  • All four ports reference the same SerDes2 phy@3 node because
  • QSGMII multiplexes all four 1G streams over one 5 Gbps lane.
  • Port 8 is the QSGMII main port (owns the SerDes clock/sync).
  • ================================================================ */
    &{/bus@100000/ethernet@c000000/ethernet-ports/port@5} {
    /delete-property/ ti,mac-only;
    phy-handle = <&vsc8514_phy0>;
    phy-mode = “qsgmii”;
    local-mac-address = [00 14 2d d0 2d c5];
    phys = <&{/bus@100000/bus@100000/phy@4044} 5>,
    <&{/bus@100000/wiz@5020000/serdes@5020000/phy@3}>;
    phy-names = “mac”, “serdes”;
    status = “okay”;
    };

&{/bus@100000/ethernet@c000000/ethernet-ports/port@6} {
/delete-property/ ti,mac-only;
phy-handle = <&vsc8514_phy1>;
phy-mode = “qsgmii”;
local-mac-address = [00 14 2d d0 2d c6];
phys = <&{/bus@100000/bus@100000/phy@4044} 6>,
<&{/bus@100000/wiz@5020000/serdes@5020000/phy@3}>;
phy-names = “mac”, “serdes”;
status = “okay”;
};

&{/bus@100000/ethernet@c000000/ethernet-ports/port@7} {
/delete-property/ ti,mac-only;
phy-handle = <&vsc8514_phy2>;
phy-mode = “qsgmii”;
local-mac-address = [00 14 2d d0 2d c7];
phys = <&{/bus@100000/bus@100000/phy@4044} 7>,
<&{/bus@100000/wiz@5020000/serdes@5020000/phy@3}>;
phy-names = “mac”, “serdes”;
status = “okay”;
};

/* Port 8 is the QSGMII main port (channel 0): replace existing SGMII config */
&{/bus@100000/ethernet@c000000/ethernet-ports/port@8} {
/delete-property/ phy-mode;
/delete-property/ phy-handle;
/delete-property/ phys;
/delete-property/ phy-names;
/delete-property/ ti,mac-only;
phy-handle = <&vsc8514_phy3>;
phy-mode = “qsgmii”;
local-mac-address = [00 14 2d d0 2d c8];
phys = <&{/bus@100000/bus@100000/phy@4044} 8>,
<&{/bus@100000/wiz@5020000/serdes@5020000/phy@3}>;
phy-names = “mac”, “serdes”;
status = “okay”;
};

/* ================================================================

    1. MDIO: VSC8514 PHY nodes at MDIO addresses 4-7.
  • PHY IDs are strapped to 0x04-0x07 on the RVH board.

  • The base port (addr 4, PHY0) owns the COMA_MODE and nRESET

  • GPIOs for the whole VSC8514 package reset sequence (Steps 1-12).

  • PHY1-3 (addr 5-7) are non-base ports; they defer to PHY0 via

  • the EPROBE_DEFER mechanism until PHY0 finishes package init.

  • ================================================================ /
    &{/bus@100000/ethernet@c000000/mdio@f00} {
    /
    PHY0: label for the existing ethernet-phy@4 (MDIO addr 4 = VSC8514 base port).

    • GPIO COMA_MODE and nRESET are confirmed working via external config. */
      vsc8514_phy0: ethernet-phy@4 {
      };

    /* PHY1-3: non-base ports, no additional GPIOs needed */
    vsc8514_phy1: ethernet-phy@5 {
    reg = <5>;
    };

    vsc8514_phy2: ethernet-phy@6 {
    reg = <6>;
    };

    vsc8514_phy3: ethernet-phy@7 {
    reg = <7>;
    };
    };

========= k3-j784s4-am69-aquila1_1-rvh2-overlay.dtbo =========================

/* ============================================================
 * (CPSW/QSGMII fragments for Ethernet Switch Phi)
 * ============================================================ */
/*
 *	Signal			|	SOM Pin	 |  AM69 V1.1 Func | AM69 V1.1 Pin SoC
 * ===================================================================
 *	ETH_2_QSGMII_RX_N	B83			SGMII2_RXN0		SERDES2_RX3_N (AT1)
 *	ETH_2_QSGMII_RX_P	B84			SGMII2_RXP0		SERDES2_RX3_P (AT2)
 *	ETH_2_QSGMII_TX_N	B86			SGMII2_TXN0		SERDES2_TX3_N (AP1)
 *	ETH_2_QSGMII_TX_P	B87			SGMII2_TXP0		SERDES2_TX3_P (AP2)
 *  
 *	ETH_2_INT#			B81			GPIO0_44		MCASP1_AXR3   (AL33)
 *  PHY_NRESET#			B46			GPIO0_33		MCASP0_AXR5	  (AG38)	
 *  COMA-MODE			B58			GPIO0_16		MCASP0_AXR0	  (AF37)
 *
 *  CPSW9X
 *	ETH_2_MDC			B90			MDIO0_MDC		MCASP2_AFSX   (AE37)
 *	ETH_2_MDIO			B89			MDIO0_MDIO		MCASP2_AXR0   (AC36)
 * 
 * SoC internal pinctrl index
 * 
 *  PIN22 → 0x5c → AC36 → MCASP2_AXR0 → SoM B89 → MDIO
 *  PIN23 → 0x58 → AE37 → MCASP2_AFSX → SoM B90 → MDC
  =================================================================== */

/* =================================================================
 * RVH ETH_2 QSGMII / VSC8514 final mapping
 *
 * Physical path:
 *   RVH VSC8514 copper PHY quad
 *     -> QSGMII differential pair ETH_2_QSGMII
 *     -> Aquila SOM pins B83/B84/B86/B87
 *     -> AM69A SERDES2 lane 3
 *     -> WIZ2 / SERDES2: /bus@100000/wiz@5020000/serdes@5020000/phy@3
 *     -> AM69A CPSW9G: /bus@100000/ethernet@c000000
 *
 * Management path:
 *   RVH VSC8514 MDIO/MDC
 *     -> Aquila SOM B89/B90
 *     -> AM69A MAIN CPSW9G MDIO pins
 *     -> /bus@100000/ethernet@c000000/mdio@f00
 *
 * Proven on hardware:
 *   VSC8514 PHY IDs are strapped at 0x04, 0x05, 0x06, 0x07.
 *   The active MDIO bus is c000f00.mdio, not c200f00.mdio.
 *
 * CPSW9G MAC mapping:
 *   CPSW9G port@5 -> VSC8514 PHY0 -> MDIO 0x04 -> PHY Handle 0x1FE
 *   CPSW9G port@6 -> VSC8514 PHY1 -> MDIO 0x05 -> PHY Handle 0x200
 *   CPSW9G port@7 -> VSC8514 PHY2 -> MDIO 0x06 -> PHY Handle 0x201
 *   CPSW9G port@8 -> VSC8514 PHY3 -> MDIO 0x07 -> PHY Handle 0x202
 *
 * Critical Linux fix:
 *   Base DT marks CPSW9G ports as ti,mac-only. These ports are PHY-backed,
 *   so delete ti,mac-only or Linux phylink/ethtool behaves as MII/PHYAD 0
 *   and ip link set up can fail with EIO.
 * ================================================================ */

fragment@150 {
	target-path = "/bus@100000/ethernet@c000000"; /* main_cpsw0 */
	__overlay__ { status = "okay"; };
};

fragment@151 {
	target-path = "/bus@100000/wiz@5020000"; /* serdes_wiz2 */
	__overlay__ { status = "okay"; };
};

fragment@152 {
	target-path = "/bus@100000/wiz@5020000/serdes@5020000"; /* serdes2 */
	__overlay__ {
		status = "okay";
		#address-cells = <1>;
		#size-cells = <0>;

		serdes2_qsgmii_link: phy@3 {
			reg = <3>;
			status = "okay";
			#phy-cells = <0>;
			cdns,num-lanes = <1>;
			cdns,phy-type = <PHY_TYPE_QSGMII>;
			resets = <&serdes_wiz2 4>;
		};
	};
};

/* =================================================================
 * RVH SerDes lane mux — explicit idle-states for J784S4 Aquila
 *
 * The Aquila base DTSI already programs SERDES2 lanes 0-3 to QSGMII,
 * but it leaves SERDES0 set to PCIe1/PCIe3/USB (all disabled on RVH)
 * and SERDES1 lane 3 set to QSGMII_LANE2 (Aquila MSP_9, unused on RVH).
 * This fragment overrides the full 16-lane idle-state array so the
 * lane-mux controller parks disabled consumers in their UNUSED states.
 *
 * Layout (4 SerDes × 4 lanes = 16 entries):
 *   SERDES0 (wiz@5060000, disabled): PCIe1/PCIe3/USB0 — all parked UNUSED
 *   SERDES1 (wiz@5070000, active  ): PCIe0 L0-L1 (NVMe), PCIe2 L0 (USB3), L3 UNUSED
 *   SERDES2 (wiz@5020000, active  ): QSGMII_LANE5-8 — VSC8514 QSGMII interface
 *   SERDES4 (wiz@5050000, active  ): EDP L0-L3 — kept as EDP (DSS disabled, harmless)
 * ================================================================= */
fragment@153 {
	target = <&serdes_ln_ctrl>;
	__overlay__ {
		idle-states =
			/* SERDES0: WIZ0 disabled; PCIe1, PCIe3, USB0 all disabled on RVH */
			<J784S4_SERDES0_LANE0_IP3_UNUSED>,
			<J784S4_SERDES0_LANE1_IP3_UNUSED>,
			<J784S4_SERDES0_LANE2_IP3_UNUSED>,
			<J784S4_SERDES0_LANE3_IP4_UNUSED>,
			/* SERDES1: PCIe0 2-lane (NVMe), PCIe2 1-lane (USB3 hub), lane 3 unused */
			<J784S4_SERDES1_LANE0_PCIE0_LANE0>,
			<J784S4_SERDES1_LANE1_PCIE0_LANE1>,
			<J784S4_SERDES1_LANE2_PCIE2_LANE0>,
			<J784S4_SERDES1_LANE3_IP4_UNUSED>,
			/* SERDES2: QSGMII for VSC8514 (physical lane 3 = QSGMII_LANE8 = CPSW port 8) */
			<J784S4_SERDES2_LANE0_QSGMII_LANE5>,
			<J784S4_SERDES2_LANE1_QSGMII_LANE6>,
			<J784S4_SERDES2_LANE2_QSGMII_LANE7>,
			<J784S4_SERDES2_LANE3_QSGMII_LANE8>,
			/* SERDES4: EDP/DisplayPort lanes (DSS disabled on RVH, lanes kept as EDP) */
			<J784S4_SERDES4_LANE0_EDP_LANE0>,
			<J784S4_SERDES4_LANE1_EDP_LANE1>,
			<J784S4_SERDES4_LANE2_EDP_LANE2>,
			<J784S4_SERDES4_LANE3_EDP_LANE3>;
	};
};

fragment@154 {
	target = <&cpsw0_phy_gmii_sel>;
	__overlay__ {
		ti,qsgmii-main-ports = <8>, <8>;
	};
};

fragment@155 {
	target-path = "/bus@100000/ethernet@c000000/mdio@f00"; /* main_cpsw0_mdio */
	__overlay__ {
		status = "okay";
		#address-cells = <1>;
		#size-cells = <0>;

		bus_freq = <250000>;          /* 250 kHz MDC — conservative for bring-up */

		/*
		 * VSC8514 nRESET and COMA_MODE are intentionally NOT placed here
		 * on the MDIO bus node. Placing reset-gpios here would cause the
		 * mdiobus driver to release nRESET before any PHY driver runs,
		 * bypassing the mandatory COMA_MODE → nRESET sequencing required
		 * by the VSC8514 datasheet.
		 *
		 * Instead, the custom RVH mscc PHY driver owns both GPIOs via the
		 * properties on qsgmii_phy0 (the package-base PHY at MDIO addr 4):
		 *   coma-mode-gpios  → "coma-mode" gpiod key → GPIO0_16 via inverter
		 *   rvh-reset-gpios  → "rvh-reset"  gpiod key → GPIO0_33 via MOSFET
		 *
		 * The driver performs the full datasheet sequence (Steps 1-12):
		 *   1. Assert COMA_MODE  (raw=0 → GPIO0_16 LOW → inverter → COMA high)
		 *   2-5. Cycle nRESET    (raw=1 assert, raw=0 release while COMA high)
		 *   6. Apply PHY API patch via MDIO
		 *   6B. Release COMA_MODE (required before 8051 MCB commit)
		 *   7-11. QSGMII SerDes config, micro reset, copper media setup
		 *   12. Init complete
		 *
		 * U-Boot note: the driver handles the full reset sequence at kernel
		 * probe time. U-Boot pre-initialisation of the VSC8514 is not
		 * required for correct operation, but initialising SERDES2 QSGMII
		 * in U-Boot before kernel handoff can reduce VSC8514 RCPLL lock
		 * time at first interface open.
		 */

		qsgmii_phy0: ethernet-phy@4 {
			compatible = "ethernet-phy-id000f.0dc7";
			reg = <0x04>;
			#phy-cells = <0>;
			max-speed = <1000>;
			phy-mode = "qsgmii";
			vsc8531,led-0-mode = <VSC8531_LINK_ACTIVITY>;
			vsc8531,led-1-mode = <VSC8531_LINK_1000_ACTIVITY>;
			vsc8531,led-2-mode = <VSC8531_LINK_100_ACTIVITY>;
			vsc8531,led-3-mode = <VSC8531_DUPLEX_COLLISION>;
			/*
			 * COMA_MODE control for the VSC8514.
			 * Active high: asserted during early PHY package init,
			 * then released low by the mscc PHY driver after QSGMII
			 * package configuration is complete.
			 *
			 * This GPIO is intentionally owned by the PHY driver, not
			 * gpio-leds and not a gpio-hog, so production software has
			 * deterministic control over the VSC8514 sequencing.
			 */
			/*
			 * VSC8514 package-level COMA_MODE control.
			 * Active high: hold PHY package in COMA.
			 * Low: release PHY package.
			 *
			 * Only the base/package PHY node owns this GPIO.
			 */
			coma-mode-gpios = <&main_gpio0 16 GPIO_ACTIVE_HIGH>;

			/*
			 * RVH shared VSC8514 reset drive on Aquila B46 / GPIO0_33.
			 * GPIO raw high drives the MOSFET and asserts the PHY's
			 * active-low QSGMII_RESETn. GPIO raw low releases nRESET high.
			 * This property is intentionally RVH-specific so generic MDIO
			 * bus reset handling does not release reset before COMA is high.
			 */
			rvh-reset-gpios = <&main_gpio0 33 GPIO_ACTIVE_HIGH>;
		};

		qsgmii_phy1: ethernet-phy@5 {
			compatible = "ethernet-phy-id000f.0dc7";
			reg = <0x05>;
			phy-mode = "qsgmii";
			#phy-cells = <0>;
			max-speed = <1000>;
			vsc8531,led-0-mode = <VSC8531_LINK_ACTIVITY>;
			vsc8531,led-1-mode = <VSC8531_LINK_1000_ACTIVITY>;
			vsc8531,led-2-mode = <VSC8531_LINK_100_ACTIVITY>;
			vsc8531,led-3-mode = <VSC8531_DUPLEX_COLLISION>;
		};

		qsgmii_phy2: ethernet-phy@6 {
			compatible = "ethernet-phy-id000f.0dc7";
			reg = <0x06>;
			phy-mode = "qsgmii";
			#phy-cells = <0>;
			max-speed = <1000>;
			vsc8531,led-0-mode = <VSC8531_LINK_ACTIVITY>;
			vsc8531,led-1-mode = <VSC8531_LINK_1000_ACTIVITY>;
			vsc8531,led-2-mode = <VSC8531_LINK_100_ACTIVITY>;
			vsc8531,led-3-mode = <VSC8531_DUPLEX_COLLISION>;
		};

		qsgmii_phy3: ethernet-phy@7 {
			compatible = "ethernet-phy-id000f.0dc7";
			reg = <0x07>;
			phy-mode = "qsgmii";
			#phy-cells = <0>;
			max-speed = <1000>;
			vsc8531,led-0-mode = <VSC8531_LINK_ACTIVITY>;
			vsc8531,led-1-mode = <VSC8531_LINK_1000_ACTIVITY>;
			vsc8531,led-2-mode = <VSC8531_LINK_100_ACTIVITY>;
			vsc8531,led-3-mode = <VSC8531_DUPLEX_COLLISION>;
		};
	};
};
fragment@156 {
	target-path = "/bus@100000/ethernet@c000000/ethernet-ports/port@5"; /* main_cpsw0_port5 */
	__overlay__ {
		status = "okay";
		label = "port1";
		/delete-property/ ti,mac-only;
		phy-mode = "qsgmii";
		phy-handle = <&qsgmii_phy0>;
		phys = <&cpsw0_phy_gmii_sel 5>, <&serdes2_qsgmii_link>;
		phy-names = "mac", "serdes";
	};
};

fragment@157 {
	target-path = "/bus@100000/ethernet@c000000/ethernet-ports/port@6"; /* main_cpsw0_port6 */
	__overlay__ {
		status = "okay";
		label = "port2";
		/delete-property/ ti,mac-only;
		phy-mode = "qsgmii";
		phy-handle = <&qsgmii_phy1>;
		phys = <&cpsw0_phy_gmii_sel 6>, <&serdes2_qsgmii_link>;
		phy-names = "mac", "serdes";
	};
};

fragment@158 {
	target-path = "/bus@100000/ethernet@c000000/ethernet-ports/port@7"; /* main_cpsw0_port7 */
	__overlay__ {
		status = "okay";
		label = "port3";
		/delete-property/ ti,mac-only;
		phy-mode = "qsgmii";
		phy-handle = <&qsgmii_phy2>;
		phys = <&cpsw0_phy_gmii_sel 7>, <&serdes2_qsgmii_link>;
		phy-names = "mac", "serdes";
	};
};

fragment@159 {
	target-path = "/bus@100000/ethernet@c000000/ethernet-ports/port@8"; /* main_cpsw0_port8 */
	__overlay__ {
		status = "okay";
		label = "port4";
		/delete-property/ ti,mac-only;
		phy-mode = "qsgmii";
		phy-handle = <&qsgmii_phy3>;
		phys = <&cpsw0_phy_gmii_sel 8>, <&serdes2_qsgmii_link>;
		phy-names = "mac", "serdes";
	};
};

============= inside linux checks =================

root@aquila-am69-12594628:~# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.50 netmask 255.255.255.0 broadcast 192.168.1.255
ether 00:14:2d:c0:2d:c4 txqueuelen 1000 (Ethernet)
RX packets 26 bytes 2902 (2.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8376 bytes 4575057 (4.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.1 netmask 255.255.255.0 broadcast 192.168.50.255
ether 00:14:2d:d0:2d:c4 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.2 netmask 255.255.255.0 broadcast 192.168.50.255
ether 00:14:2d:d0:2d:c6 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

eth3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.3 netmask 255.255.255.0 broadcast 192.168.50.255
ether 00:14:2d:d0:2d:c7 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

eth4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.4 netmask 255.255.255.0 broadcast 192.168.50.255
ether 00:14:2d:d0:2d:c8 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 29049 bytes 2445930 (2.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 29049 bytes 2445930 (2.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

root@aquila-am69-12594628:~# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:14:2d:c0:2d:c4 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:14:2d:d0:2d:c4 brd ff:ff:ff:ff:ff:ff
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:14:2d:d0:2d:c6 brd ff:ff:ff:ff:ff:ff
5: eth3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:14:2d:d0:2d:c7 brd ff:ff:ff:ff:ff:ff
6: eth4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:14:2d:d0:2d:c8 brd ff:ff:ff:ff:ff:ff

root@aquila-am69-12594628:~# ethtool eth1
Settings for eth1:
Supported ports: [ TP MII ]
Supported link modes: 10baseT/Full
100baseT/Half 100baseT/Full
1000baseT/Full
Supported pause frame use: Symmetric
Supports auto-negotiation: Yes
Supported FEC modes: Not reported
Advertised link modes: 10baseT/Full
100baseT/Half 100baseT/Full
1000baseT/Full
Advertised pause frame use: Symmetric
Advertised auto-negotiation: Yes
Advertised FEC modes: Not reported
Speed: Unknown!
Duplex: Unknown! (255)
Auto-negotiation: on
Port: Twisted Pair
PHYAD: 4
Transceiver: external
MDI-X: off (auto)
Supports Wake-on: d
Wake-on: d
Current message level: 0x000020f7 (8439)
drv probe link ifdown ifup rx_err tx_err hw
Link detected: yes

=============== tdx-info ===========

root@aquila-am69-12594628:~# tdx-info

Software summary

Bootloader: U-Boot

Kernel version: 6.6.138-rvh-mscc-bind1-g81fb03d37946-dirty #58 SMP PREEMPT Tue May 26 15:06:12 AEST 2026
Kernel command line: root=PARTUUID=5a1d15ea-02 ro rootwait console=tty1 console=ttyS2,115200
Distro name: NAME=“BIA5 RVH AM69A”
Distro version: VERSION_ID=7.5.0-devel-20260205045228-build.0
Distro variant: -
Hostname: aquila-am69-12594628

Hardware info

HW model: BIA5 RVH AM69A Embedded Platform

Toradex version: 0211 V1.1A
Serial number: 12594628
Processor arch: aarch64

Hi, I’m the Toradex AI Assistant.

I might make mistakes or occasionally get you wrong. Our support team will step in if needed.


(1) (2) (3) (4)

Looking at your setup, I can see you have the Aquila AM69 SOM with a custom VSC8514 QSGMII configuration. Your device tree configuration appears comprehensive, but there are several potential issues that could cause the “Speed: Unknown! / Duplex: Unknown!” status and lack of copper link detection.

Key Issues to Investigate

1. SerDes Clock and Reference Configuration

The AM69 SerDes requires proper reference clock configuration for QSGMII operation (1). Your configuration shows:

  • SERDES2 lane 3 configured for QSGMII
  • VSC8514 at MDIO addresses 0x04-0x07
  • Proper GPIO reset sequencing

However, ensure the SerDes reference clock is correctly configured for the 5 Gbps QSGMII link rate.

2. Device Tree Overlay Verification

Based on the Toradex documentation for device tree overlays (4), verify that your overlay is properly applied:

# Check if overlays are applied correctly
ls /proc/device-tree/
cat /proc/device-tree/compatible

3. MDIO Bus Communication

Since ethtool shows “PHYAD: 4” and “Link detected: yes” but with unknown speed/duplex, this suggests:

  • MDIO communication is working (PHY is detected)
  • The PHY initialization sequence may have issues
  • The SerDes link between AM69 and VSC8514 may not be established

4. VSC8514 Initialization Sequence

Your device tree shows comprehensive GPIO control for COMA_MODE and nRESET. Verify the initialization sequence:

# Check if the MSC PHY driver loaded correctly
dmesg | grep -i vsc
dmesg | grep -i mscc
dmesg | grep -i qsgmii

5. SerDes Link Status

Check the SerDes link status:

# Check SerDes status
cat /sys/kernel/debug/phy/5020000.serdes/phy-5020000.serdes.3/phy_power_on
# Look for SerDes-related messages
dmesg | grep -i serdes

Troubleshooting Steps

Step 1: Verify PHY Detection

# Check MDIO bus and PHY detection
cat /sys/bus/mdio/devices/c000f00:04/phy_id
cat /sys/bus/mdio/devices/c000f00:05/phy_id
cat /sys/bus/mdio/devices/c000f00:06/phy_id
cat /sys/bus/mdio/devices/c000f00:07/phy_id

Step 2: Check GPIO Status

# Verify GPIO states for reset and COMA_MODE
cat /sys/kernel/debug/gpio
# Look for GPIO 16 (COMA_MODE) and GPIO 33 (nRESET)

Step 3: Examine Network Interface Details

# Get detailed interface information
ethtool -i eth1
ethtool -S eth1  # Statistics
ip link show eth1

Common QSGMII Issues

Based on Toradex device tree overlay documentation (2), common issues with QSGMII configurations include:

  1. Incorrect SerDes lane mapping - Verify SERDES2_LANE3 maps to QSGMII_LANE8
  2. Missing or incorrect reference clock - QSGMII requires specific clock frequencies
  3. PHY initialization timing - The VSC8514 has strict timing requirements for COMA_MODE and nRESET sequencing
  4. SerDes power management - Ensure the SerDes PHY is properly powered and configured

Next Steps

  1. Capture and analyze the full dmesg output focusing on SerDes, MDIO, and PHY initialization
  2. Verify the VSC8514 datasheet timing requirements are met in your hardware design
  3. Check if the 5 GHz SerDes clock is stable using oscilloscope measurements
  4. Consider testing with a single PHY first (PHY0 at address 0x04) to isolate the issue

The fact that Linux detects the interfaces and shows “Link detected: yes” suggests the basic device tree configuration is working, but the SerDes-to-PHY communication or PHY initialization may need refinement.

Best regards, Toradex AI Assistant.


Did you like my answer? :+1: :-1:

If I answered your question, please tick the “Solution” checkbox.

For follow-up questions to me, please use @ToradexAI in your post.

@ToradexAI
What’s correct:

  • port@5 phy-handle = 0x201ethernet-phy@4 (VSC8514 PHY0, MDIO 4) ✓
  • port@8 phy-handle = 0x205ethernet-phy@7 (VSC8514 PHY3, MDIO 7) ✓ — port 8 owns the QSGMII main lane as expected
  • phys = <0x73 0x05 0x202> on all ports — correctly referencing both the GMII_SEL phy (0x73) and the serdes PHY (phandle 0x202 = our overlay’s phy@3) ✓
  • reset-gpios = <0x1c 0x21 0x00> = GPIO0_33 (nRESET) ✓
  • coma-mode-gpios = <0x1c 0x10 0x00> = GPIO0_16 (COMA_MODE) ✓

What the live DTS reveals about the WIZ issue:

The serdes PHY (phandle 0x202) IS referenced in the phys property, so the CPSW driver should be calling phy_power_on() on it. The fact that WIZ_SERDES_CTRL bit30 (RESET_N) is still 0 while GMII_SEL is correctly programmed means phy_power_on() IS reaching the GMII_SEL phy but the WIZ global SERDES_CTRL bit30 is never being set to 1.

This points to the WIZ driver’s wiz_power_on() callback not being triggered — it’s only called when someone uses a WIZ-exported PHY (#phy-cells = 1), but the CPSW ports reference the Torrent PHYs directly. The Cadence Torrent driver’s reset_control_deassert() releases per-lane resets (WIZ reset #4 for lane 3) but the global WIZ_SERDES_CTRL RESET_N bit needs a separate write that isn’t happening.

This is likely the next thing to look at with TI/Toradex support — whether the WIZ wiz_power_on() needs to be explicitly triggered for the QSGMII use case, either via a missing DTS property or a driver patch.

Excellent analysis! Your findings reveal a critical WIZ SerDes controller initialization issue that’s preventing the QSGMII link from establishing properly (1).

Root Cause Analysis

Your investigation correctly identifies that while the device tree configuration is properly structured, the WIZ global SERDES_CTRL RESET_N bit (bit30) remains deasserted (0), preventing the SerDes from becoming operational (1).

Key Findings Confirmed

  1. CPSW Port Configuration: Correct - ports 5-8 properly reference both GMII_SEL and SerDes PHYs
  2. GPIO Control: Correct - VSC8514 reset and COMA_MODE GPIOs are properly configured
  3. SerDes PHY References: Correct - all ports reference the same SerDes PHY (phandle 0x202) as expected for QSGMII multiplexing
  4. GMII_SEL Programming: Working - port 8 correctly configured as QSGMII main port

WIZ Driver Power Management Issue

The core issue you’ve identified is that the WIZ driver’s wiz_power_on() callback is not being triggered in the QSGMII configuration. This is because:

  • CPSW ports reference Cadence Torrent PHYs directly (not WIZ-exported PHYs)
  • Cadence Torrent driver calls reset_control_deassert() for per-lane resets only
  • The global WIZ_SERDES_CTRL RESET_N bit requires separate initialization

Potential Solutions

Based on Toradex device tree overlay documentation (2), consider these approaches:

1. Explicit WIZ PHY Reference

Try adding an explicit WIZ PHY reference to your SerDes configuration:

serdes2_qsgmii_link: phy@3 {
    reg = <3>;
    status = "okay";
    #phy-cells = <0>;
    cdns,num-lanes = <1>;
    cdns,phy-type = <PHY_TYPE_QSGMII>;
    resets = <&serdes_wiz2 4>;
    /* Add explicit WIZ reference */
    wiz = <&serdes_wiz2>;
};

(2)

2. WIZ Power Domain Configuration

Ensure the WIZ controller is properly configured for QSGMII operation:

&serdes_wiz2 {
    status = "okay";
    /* Ensure proper power domain configuration */
    power-domains = <&k3_pds 297 TI_SCI_PD_EXCLUSIVE>;
};

(2)

3. Manual WIZ Initialization

As a debugging step, try manually asserting the WIZ SERDES_CTRL RESET_N bit:

# Check current WIZ register state
devmem2 0x5020404 w  # WIZ_SERDES_CTRL register
# Manually set bit30 (RESET_N)
devmem2 0x5020404 w 0x40000000

Next Steps

  1. Contact Toradex Support: This appears to be a platform-specific WIZ driver integration issue that may require a BSP-level fix (3)

  2. Verify WIZ Power State: Check if the WIZ controller is in the correct power state:

    cat /sys/kernel/debug/pm_genpd/pm_genpd_summary | grep wiz
    
  3. Test with Simplified Configuration: Try configuring only one QSGMII port initially to isolate the WIZ initialization issue

  4. Driver Debug: Enable WIZ and Cadence Torrent driver debugging:

    echo 8 > /proc/sys/kernel/printk
    echo 'file drivers/phy/ti/phy-j721e-wiz.c +p' > /sys/kernel/debug/dynamic_debug/control
    

Your analysis correctly identifies that this is likely a WIZ driver integration issue rather than a device tree configuration problem. The fact that GMII_SEL is working but the global SerDes reset remains asserted suggests the WIZ wiz_power_on() callback needs to be explicitly triggered for QSGMII use cases.

Best regards, Toradex AI Assistant.


Did you like my answer? :+1: :-1:

If I answered your question, please tick the “Solution” checkbox.

For follow-up questions to me, please use @ToradexAI in your post.