[PATCH 13/20] net: dsa: xilinx: wire up phylink for the switch ports

Nagadheeraj Rottela nagadheeraj.rottela at amd.com
Fri Aug 7 03:44:24 PDT 2026


Wire up the MAC-side phylink ops for all three switch ports.

For the two external user ports, phylink_get_caps fills
supported_interfaces with RGMII variants, SGMII, and GMII, and sets
mac_capabilities to MAC_100FD and MAC_1000FD. The speed-config field
only encodes those two speeds, so 10 Mbps and half-duplex are not
reachable in hardware. The CPU port has no external PHY. Its link is
internal and fixed at MAC_1000FD, so phylink_get_caps sets
PHY_INTERFACE_MODE_INTERNAL and returns early for it.

mac_link_up programs the resolved speed into the speed-config field
and sets TX_EN and RX_EN to allow frames to flow. mac_link_down clears
them. mac_config is intentionally empty. The interface mode is fixed
at IP synthesis time and there is no runtime register to program.

Co-developed-by: Srinivas Neeli <srinivas.neeli at amd.com>
Signed-off-by: Srinivas Neeli <srinivas.neeli at amd.com>
Signed-off-by: Nagadheeraj Rottela <nagadheeraj.rottela at amd.com>
---
 drivers/net/dsa/xilinx/xilinx_tsn.c | 102 ++++++++++++++++++++++++++++
 drivers/net/dsa/xilinx/xilinx_tsn.h |  12 ++++
 2 files changed, 114 insertions(+)

diff --git a/drivers/net/dsa/xilinx/xilinx_tsn.c b/drivers/net/dsa/xilinx/xilinx_tsn.c
index 542b74514ed3..86aba2e8b6e9 100644
--- a/drivers/net/dsa/xilinx/xilinx_tsn.c
+++ b/drivers/net/dsa/xilinx/xilinx_tsn.c
@@ -14,6 +14,7 @@
 #include <linux/of.h>
 #include <linux/of_mdio.h>
 #include <linux/phy.h>
+#include <linux/phylink.h>
 #include <linux/platform_device.h>
 #include <net/dsa.h>
 
@@ -324,6 +325,105 @@ static void xlnx_tsn_port_stp_state_set(struct dsa_switch *ds, int port,
 	xlnx_tsn_set_port_state(sw, port, hw_state);
 }
 
+static void xlnx_tsn_phylink_get_caps(struct dsa_switch *ds, int port,
+				      struct phylink_config *config)
+{
+	if (port == XLNX_TSN_CPU_PORT) {
+		config->mac_capabilities = MAC_1000FD;
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+		return;
+	}
+
+	/* The MAC's speed-config field only encodes 100 / 1000.
+	 * Half-duplex and 10 Mbps are not supported.
+	 */
+	config->mac_capabilities = MAC_100FD | MAC_1000FD;
+	phy_interface_set_rgmii(config->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces);
+}
+
+static void xlnx_tsn_mac_config(struct phylink_config *config,
+				unsigned int mode,
+				const struct phylink_link_state *state)
+{
+	/* Interface mode (RGMII / SGMII / GMII) is fixed at IP synthesis
+	 * time. There is no runtime register to program it here.
+	 */
+}
+
+static void xlnx_tsn_mac_link_down(struct phylink_config *config,
+				   unsigned int mode,
+				   phy_interface_t interface)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct xlnx_tsn *sw = dp->ds->priv;
+	struct xlnx_tsn_mac *m;
+	u32 rcw1, tc;
+
+	m = &sw->mac[dp->index];
+
+	/* The CPU port has no switch-side MAC registers. Nothing to
+	 * tear down here (see xlnx_tsn_mac_link_up).
+	 */
+	if (dp->index == XLNX_TSN_CPU_PORT)
+		return;
+
+	tc = mac_ior(m, TSN_TC_OFFSET) & ~TSN_TC_TX_EN;
+	mac_iow(m, TSN_TC_OFFSET, tc);
+
+	rcw1 = mac_ior(m, TSN_RCW1_OFFSET) & ~TSN_RCW1_RX_EN;
+	mac_iow(m, TSN_RCW1_OFFSET, rcw1);
+}
+
+static void xlnx_tsn_mac_link_up(struct phylink_config *config,
+				 struct phy_device *phy, unsigned int mode,
+				 phy_interface_t interface, int speed,
+				 int duplex, bool tx_pause, bool rx_pause)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct xlnx_tsn *sw = dp->ds->priv;
+	u32 speed_cfg, rcw1, tc;
+	struct xlnx_tsn_mac *m;
+
+	m = &sw->mac[dp->index];
+
+	/* The CPU port is the internal endpoint MAC. It has no switch-side
+	 * MAC registers and is managed by the endpoint driver, not here.
+	 */
+	if (dp->index == XLNX_TSN_CPU_PORT)
+		return;
+
+	speed_cfg = mac_ior(m, TSN_SPEED_CFG_OFFSET) & ~TSN_SPEED_CFG_MASK;
+	switch (speed) {
+	case SPEED_1000:
+		speed_cfg |= TSN_SPEED_CFG_1000;
+		break;
+	case SPEED_100:
+		speed_cfg |= TSN_SPEED_CFG_100;
+		break;
+	default:
+		dev_warn(sw->dev,
+			 "port %d: unsupported link speed %d Mbps\n",
+			 dp->index, speed);
+		return;
+	}
+	mac_iow(m, TSN_SPEED_CFG_OFFSET, speed_cfg);
+
+	rcw1 = mac_ior(m, TSN_RCW1_OFFSET) | TSN_RCW1_RX_EN;
+	mac_iow(m, TSN_RCW1_OFFSET, rcw1);
+
+	tc = mac_ior(m, TSN_TC_OFFSET) | TSN_TC_TX_EN;
+	mac_iow(m, TSN_TC_OFFSET, tc);
+}
+
+static const struct phylink_mac_ops xlnx_tsn_phylink_mac_ops = {
+	.mac_config	= xlnx_tsn_mac_config,
+	.mac_link_up	= xlnx_tsn_mac_link_up,
+	.mac_link_down	= xlnx_tsn_mac_link_down,
+};
+
 static int xlnx_tsn_setup(struct dsa_switch *ds)
 {
 	struct xlnx_tsn *sw = ds->priv;
@@ -372,6 +472,7 @@ static const struct dsa_switch_ops xlnx_tsn_switch_ops = {
 	.setup			= xlnx_tsn_setup,
 	.teardown		= xlnx_tsn_teardown,
 	.port_stp_state_set	= xlnx_tsn_port_stp_state_set,
+	.phylink_get_caps	= xlnx_tsn_phylink_get_caps,
 };
 
 static int xlnx_tsn_map_reg(struct platform_device *pdev, const char *name,
@@ -421,6 +522,7 @@ static int xlnx_tsn_probe(struct platform_device *pdev)
 	ds->dev = dev;
 	ds->num_ports = XLNX_TSN_NUM_PORTS;
 	ds->ops = &xlnx_tsn_switch_ops;
+	ds->phylink_mac_ops = &xlnx_tsn_phylink_mac_ops;
 	ds->priv = sw;
 
 	platform_set_drvdata(pdev, sw);
diff --git a/drivers/net/dsa/xilinx/xilinx_tsn.h b/drivers/net/dsa/xilinx/xilinx_tsn.h
index 025645a83992..6286caff7a1c 100644
--- a/drivers/net/dsa/xilinx/xilinx_tsn.h
+++ b/drivers/net/dsa/xilinx/xilinx_tsn.h
@@ -66,6 +66,18 @@ enum tsn_port_state {
 
 #define TSN_MDIO_MRD_MASK		GENMASK(15, 0)
 
+/* Per-MAC receive / transmit / speed configuration registers. */
+#define TSN_RCW1_OFFSET			0x00000404
+#define TSN_RCW1_RX_EN			BIT(28)
+
+#define TSN_TC_OFFSET			0x00000408
+#define TSN_TC_TX_EN			BIT(28)
+
+#define TSN_SPEED_CFG_OFFSET		0x00000410
+#define TSN_SPEED_CFG_MASK		GENMASK(31, 30)
+#define TSN_SPEED_CFG_100		BIT(30)
+#define TSN_SPEED_CFG_1000		BIT(31)
+
 struct mii_bus;
 struct xlnx_tsn;
 
-- 
2.34.1




More information about the linux-arm-kernel mailing list