[PATCH 13/20] net: dsa: xilinx: wire up phylink for the switch ports
Nagadheeraj Rottela <[email protected]>
| Newsgroups | org.kernel.vger.linux-devicetree,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> Signed-off-by: Srinivas Neeli <[email protected]> Signed-off-by: Nagadheeraj Rottela <[email protected]> --- 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