realtek: dsa: support RTL931x MAC force mode

RTL931x controls forced link, speed, duplex, and flow control with
separate enable bits. Describe the register layout and provide a MAC
link-state source selector so the common RTL93xx callbacks can manage
its force-mode transitions.

This supports fixed links, CPU ports, and dynamic SFP PHYs while
leaving statically described PHY links and direct PCS links
hardware-controlled. Dynamic SGMII and out-of-band PHYs remain forced,
while dynamic USXGMII PHYs return to PCS-controlled state after the
force pulse.

Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Link: https://github.com/openwrt/openwrt/pull/25118
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
This commit is contained in:
Jonas Jelonek committed 2026-10-12 20:06:56 +02:00
1 parent 951eb211c7
commit 9cf511cfb4
3 files changed
+49 -12

No files matched your search

@@ -329,8 +329,7 @@ static void rtldsa_93xx_phylink_mac_config(struct phylink_config *config,
return;
cfg = &priv->r->mac_force_mode;
force_dynamic_phy = priv->family_id == RTL9300_FAMILY_ID &&
!priv->ports[port].phy &&
force_dynamic_phy = !priv->ports[port].phy &&
(mode == MLO_AN_PHY || state->interface == PHY_INTERFACE_MODE_SGMII);
/* Select PCS before dropping force mode to avoid a transient link down. */
@@ -340,8 +339,7 @@ static void rtldsa_93xx_phylink_mac_config(struct phylink_config *config,
RTLDSA_MAC_LINK_STATE_SOURCE_PCS,
state->interface);
if (priv->family_id == RTL9300_FAMILY_ID &&
(mode == MLO_AN_FIXED || force_dynamic_phy)) {
if (mode == MLO_AN_FIXED || force_dynamic_phy) {
/* Keep links that require force mode forced down. */
regmap_write(priv->map, priv->r->mac_force_mode_ctrl(port),
cfg->force_en_mask | cfg->fc_force_en_mask);
@@ -385,8 +383,7 @@ static void rtldsa_93xx_phylink_mac_link_down(struct phylink_config *config,
int port = dp->index;
cfg = &priv->r->mac_force_mode;
force_dynamic_phy = priv->family_id == RTL9300_FAMILY_ID &&
!priv->ports[port].phy &&
force_dynamic_phy = !priv->ports[port].phy &&
(mode == MLO_AN_PHY || interface == PHY_INTERFACE_MODE_SGMII);
/* Stop TX/RX to port */
@@ -398,8 +395,7 @@ static void rtldsa_93xx_phylink_mac_link_down(struct phylink_config *config,
RTLDSA_MAC_LINK_STATE_SOURCE_PCS,
interface);
if (priv->family_id == RTL9300_FAMILY_ID &&
(dsa_port_is_cpu(dp) || mode == MLO_AN_FIXED || force_dynamic_phy)) {
if (dsa_port_is_cpu(dp) || mode == MLO_AN_FIXED || force_dynamic_phy) {
/* Preserve force mode while forcing the link down. */
regmap_update_bits(priv->map, priv->r->mac_force_mode_ctrl(port),
cfg->link_up_mask, 0);
@@ -473,9 +469,6 @@ static void rtldsa_93xx_phylink_mac_link_up(struct phylink_config *config,
bool dynamic_phy, force_dynamic_phy, keep_forced;
u32 mcr, spdsel;
if (priv->family_id != RTL9300_FAMILY_ID)
goto restart;
/*
* The RTL93xx polling machinery does more than issue MDIO
* transactions. It collects resolved link state and distributes it
@@ -34,7 +34,15 @@
#define RTL930X_MEDIA_SEL BIT(16)
#define RTL931X_FORCE_LINK_EN BIT(0)
#define RTL931X_FORCE_DUPLEX_EN BIT(2)
#define RTL931X_FORCE_SPEED_EN BIT(3)
#define RTL931X_MAC_FORCE_FC_EN BIT(4)
#define RTL931X_FORCE_LINK BIT(9)
#define RTL931X_DUPLEX_MODE BIT(11)
#define RTL931X_SPEED_SHIFT (12)
#define RTL931X_SPEED_MASK (15 << RTL931X_SPEED_SHIFT)
#define RTL931X_TX_PAUSE_EN BIT(16)
#define RTL931X_RX_PAUSE_EN BIT(17)
enum rtldsa_mac_link_state_source {
RTLDSA_MAC_LINK_STATE_SOURCE_PCS,
@@ -2,6 +2,7 @@
#include <asm/mach-rtl-otto/mach-rtl-otto.h>
#include <linux/etherdevice.h>
#include <linux/regmap.h>
#include "lag.h"
#include "l2.h"
@@ -82,6 +83,11 @@
#define RTL931X_LED_CLK_SEL_200NS 2
#define RTL931X_LED_CLK_SEL_100NS 3
#define RTL931X_SMI_PHY_ABLTY_GET_SEL(port) (0x0cac + ((port) / 16) * 4)
#define RTL931X_SMI_PHY_ABLTY_MDIO 0x0
#define RTL931X_SMI_PHY_ABLTY_SDS 0x2
#define RTL931X_SMI_PHY_ABLTY_MASK(port) (GENMASK(1, 0) << (((port) % 16) * 2))
static inline int rtl931x_mac_force_mode_ctrl(int p)
{
return RTL931X_MAC_FORCE_MODE_CTRL + (p << 2);
@@ -382,6 +388,28 @@ static void rtldsa_931x_led_init(struct rtl838x_switch_priv *priv)
dev_dbg(dev, "%08x: %08x\n", 0xbb000600 + i * 4, sw_r32(0x0600 + i * 4));
}
static int
rtldsa_931x_mac_link_state_source_set(struct rtl838x_switch_priv *priv,
int port,
enum rtldsa_mac_link_state_source source,
phy_interface_t interface)
{
u32 mask, src_val;
if (port >= RTL931X_CPU_PORT)
return 0;
mask = RTL931X_SMI_PHY_ABLTY_MASK(port);
if (source == RTLDSA_MAC_LINK_STATE_SOURCE_PCS)
src_val = RTL931X_SMI_PHY_ABLTY_SDS;
else
src_val = RTL931X_SMI_PHY_ABLTY_MDIO;
return regmap_update_bits(priv->map,
RTL931X_SMI_PHY_ABLTY_GET_SEL(port),
mask, field_prep(mask, src_val));
}
const struct rtldsa_config rtldsa_931x_cfg = {
.switch_ops = &rtldsa_93xx_switch_ops,
.phylink_mac_ops = &rtldsa_93xx_phylink_mac_ops,
@@ -432,10 +460,18 @@ const struct rtldsa_config rtldsa_931x_cfg = {
.stp_get = rtldsa_931x_stp_get,
.stp_set = rtl931x_stp_set,
.mac_force_mode = {
.force_en_mask = RTL931X_FORCE_LINK_EN,
.force_en_mask = RTL931X_FORCE_LINK_EN |
RTL931X_FORCE_DUPLEX_EN |
RTL931X_FORCE_SPEED_EN,
.fc_force_en_mask = RTL931X_MAC_FORCE_FC_EN,
.link_up_mask = RTL931X_FORCE_LINK,
.duplex_mask = RTL931X_DUPLEX_MODE,
.speed_mask = RTL931X_SPEED_MASK,
.tx_pause_mask = RTL931X_TX_PAUSE_EN,
.rx_pause_mask = RTL931X_RX_PAUSE_EN,
},
.mac_force_mode_ctrl = rtl931x_mac_force_mode_ctrl,
.mac_link_state_source_set = rtldsa_931x_mac_link_state_source_set,
.mac_link_sts = RTL931X_MAC_LINK_STS,
.mac_port_ctrl = rtl931x_mac_port_ctrl,
.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 |