* [PATCH net-next v4 1/8] net: dsa: motorcomm: Remove YT921X_PORT_MASK_* macros
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 2/8] net: dsa: motorcomm: Split xMII and SERDES port masks David Yang
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Remove YT921X_PORT_MASK_* macros since they are only thin wrappers
around BIT() and GENMASK() and are not used in other places (plain BIT()
and GENMASK() are used there). No functional change.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/chip.c | 31 +++++++++++++------------------
1 file changed, 13 insertions(+), 18 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 8b0ac5fc8c8b..825e0bfcff86 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -108,46 +108,41 @@ struct yt921x_info {
u16 external_mask;
};
-#define YT921X_PORT_MASK_INTn(port) BIT(port)
-#define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0)
-#define YT921X_PORT_MASK_EXT0 BIT(8)
-#define YT921X_PORT_MASK_EXT1 BIT(9)
-
static const struct yt921x_info yt921x_infos[] = {
{
"YT9215SC", YT9215_MAJOR, 1, 0,
- YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ GENMASK(4, 0),
+ BIT(8) | BIT(9),
},
{
"YT9215S", YT9215_MAJOR, 2, 0,
- YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ GENMASK(4, 0),
+ BIT(8) | BIT(9),
},
{
"YT9215RB", YT9215_MAJOR, 3, 0,
- YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ GENMASK(4, 0),
+ BIT(8) | BIT(9),
},
{
"YT9214NB", YT9215_MAJOR, 3, 2,
- YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ BIT(1) | BIT(3),
+ BIT(8) | BIT(9),
},
{
"YT9213NB", YT9215_MAJOR, 3, 3,
- YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
- YT921X_PORT_MASK_EXT1,
+ BIT(1) | BIT(3),
+ BIT(9),
},
{
"YT9218N", YT9218_MAJOR, 0, 0,
- YT921X_PORT_MASK_INT0_n(8),
+ GENMASK(7, 0),
0,
},
{
"YT9218MB", YT9218_MAJOR, 1, 0,
- YT921X_PORT_MASK_INT0_n(8),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ GENMASK(7, 0),
+ BIT(8) | BIT(9),
},
{}
};
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 2/8] net: dsa: motorcomm: Split xMII and SERDES port masks
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
2026-09-23 2:19 ` [PATCH net-next v4 1/8] net: dsa: motorcomm: Remove YT921X_PORT_MASK_* macros David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 3/8] net: dsa: motorcomm: Identify port type at runtime David Yang
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Replace external_mask with xmii_mask and serdes_mask, and advertise the
interface modes it actually supports.
Also move struct yt921x_info into chip.h for future use.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/chip.c | 39 ++++++++++++--------------------
drivers/net/dsa/motorcomm/chip.h | 13 +++++++++++
2 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 825e0bfcff86..54881856b796 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -96,53 +96,48 @@ static const struct yt921x_mib_desc yt921x_mib_descs[] = {
MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
};
-struct yt921x_info {
- const char *name;
- u16 major;
- /* Unknown, seems to be plain enumeration */
- u8 mode;
- u8 extmode;
- /* Ports with integral GbE PHYs, not including MCU Port 10 */
- u16 internal_mask;
- /* TODO: see comments in yt921x_dsa_phylink_get_caps() */
- u16 external_mask;
-};
-
static const struct yt921x_info yt921x_infos[] = {
{
"YT9215SC", YT9215_MAJOR, 1, 0,
GENMASK(4, 0),
+ BIT(9),
BIT(8) | BIT(9),
},
{
"YT9215S", YT9215_MAJOR, 2, 0,
GENMASK(4, 0),
- BIT(8) | BIT(9),
+ BIT(9),
+ BIT(8),
},
{
"YT9215RB", YT9215_MAJOR, 3, 0,
GENMASK(4, 0),
BIT(8) | BIT(9),
+ 0,
},
{
"YT9214NB", YT9215_MAJOR, 3, 2,
BIT(1) | BIT(3),
- BIT(8) | BIT(9),
+ BIT(9),
+ BIT(8),
},
{
"YT9213NB", YT9215_MAJOR, 3, 3,
BIT(1) | BIT(3),
BIT(9),
+ BIT(9),
},
{
"YT9218N", YT9218_MAJOR, 0, 0,
GENMASK(7, 0),
0,
+ 0,
},
{
"YT9218MB", YT9218_MAJOR, 1, 0,
GENMASK(7, 0),
BIT(8) | BIT(9),
+ BIT(8) | BIT(9),
},
{}
};
@@ -4021,15 +4016,10 @@ yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
*/
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
- } else if (info->external_mask & BIT(port)) {
- /* TODO: external ports may support SERDES only, XMII only, or
- * SERDES + XMII depending on the chip. However, we can't get
- * the accurate config table due to lack of document, thus
- * we simply declare SERDES + XMII and rely on the correctness
- * of devicetree for now.
- */
+ return;
+ }
- /* SERDES */
+ if (BIT(port) & info->serdes_mask) {
__set_bit(PHY_INTERFACE_MODE_SGMII,
config->supported_interfaces);
/* REVSGMII (SGMII in PHY role) should go here, once
@@ -4042,9 +4032,8 @@ yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
config->supported_interfaces);
config->mac_capabilities |= MAC_2500FD;
-
- /* XMII */
-
+ }
+ if (BIT(port) & info->xmii_mask) {
/* Not tested. To add support for XMII:
* - Add proper interface modes below
* - Handle them in yt921x_port_config()
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 83cd454955dd..83e76cefbccd 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -960,6 +960,19 @@ struct yt921x_reg_ops {
int (*write)(void *context, u32 reg, u32 val);
};
+struct yt921x_info {
+ const char *name;
+ u16 major;
+ /* Unknown, seems to be plain enumeration */
+ u8 mode;
+ u8 extmode;
+ /* Ports with integral GbE PHYs, not including MCU Port 10 */
+ u16 internal_mask;
+ /* Note: xmii_mask and serdes_mask may overlap */
+ u16 xmii_mask;
+ u16 serdes_mask;
+};
+
struct yt921x_priv {
struct dsa_switch ds;
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 3/8] net: dsa: motorcomm: Identify port type at runtime
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
2026-09-23 2:19 ` [PATCH net-next v4 1/8] net: dsa: motorcomm: Remove YT921X_PORT_MASK_* macros David Yang
2026-09-23 2:19 ` [PATCH net-next v4 2/8] net: dsa: motorcomm: Split xMII and SERDES port masks David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 4/8] net: dsa: motorcomm: Fix register bit field names David Yang
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
The yt921x_port_is_internal/external() macros hardcoded the port layout:
they treat ports 8 and 9 as external on every chip and count the dummy
ports as internal.
Identify the actual port capabilities using chip info and track
configuration of external ports (although only SerDes is supported now).
This is a hardening and cleanup step ahead of the SerDes PCS support, no
behavior changes intended.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 40 +++++++++++++++++++++++---------
drivers/net/dsa/motorcomm/chip.h | 11 +++++----
2 files changed, 35 insertions(+), 16 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 54881856b796..ea4b29882b5c 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -3738,6 +3738,7 @@ yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
static int yt921x_port_down(struct yt921x_priv *priv, int port)
{
+ const struct yt921x_info *info = priv->info;
u32 mask;
int res;
@@ -3746,12 +3747,13 @@ static int yt921x_port_down(struct yt921x_priv *priv, int port)
if (res)
return res;
- if (yt921x_port_is_external(port)) {
+ if (BIT(port) & info->serdes_mask) {
mask = YT921X_SERDES_LINK;
res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
if (res)
return res;
-
+ }
+ if (BIT(port) & info->xmii_mask) {
mask = YT921X_XMII_LINK;
res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
if (res)
@@ -3766,6 +3768,8 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
phy_interface_t interface, int speed, int duplex,
bool tx_pause, bool rx_pause)
{
+ const struct yt921x_info *info = priv->info;
+ struct yt921x_port *pp = &priv->ports[port];
u32 mask;
u32 ctrl;
int res;
@@ -3800,7 +3804,10 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (res)
return res;
- if (yt921x_port_is_external(port)) {
+ if (!(BIT(port) & (info->serdes_mask | info->xmii_mask)))
+ return 0;
+
+ if (pp->serdes) {
mask = YT921X_SERDES_SPEED_M;
switch (speed) {
case SPEED_10:
@@ -3836,7 +3843,7 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
mask, ctrl);
if (res)
return res;
-
+ } else {
mask = YT921X_XMII_LINK;
res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask);
if (res)
@@ -3876,17 +3883,16 @@ static int
yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
phy_interface_t interface)
{
+ const struct yt921x_info *info = priv->info;
+ struct yt921x_port *pp = &priv->ports[port];
struct device *dev = to_device(priv);
u32 mask;
u32 ctrl;
int res;
- if (!yt921x_port_is_external(port)) {
- if (interface != PHY_INTERFACE_MODE_INTERNAL) {
- dev_err(dev, "Wrong mode %d on port %d\n",
- interface, port);
- return -EINVAL;
- }
+ if (BIT(port) & info->internal_mask) {
+ if (interface != PHY_INTERFACE_MODE_INTERNAL)
+ goto err;
return 0;
}
@@ -3896,6 +3902,9 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
case PHY_INTERFACE_MODE_100BASEX:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
+ if (!(BIT(port) & info->serdes_mask))
+ goto err;
+
mask = YT921X_SERDES_CTRL_PORTn(port);
res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask);
if (res)
@@ -3906,6 +3915,11 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
if (res)
return res;
+ /* The order is quite arbitrary - we can't return to a safe
+ * state on IO errors.
+ */
+ pp->serdes = true;
+
mask = YT921X_SERDES_MODE_M;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
@@ -3931,10 +3945,14 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
break;
/* add XMII support here */
default:
- return -EINVAL;
+ goto err;
}
return 0;
+
+err:
+ dev_err(dev, "Wrong mode %d on port %d\n", interface, port);
+ return -EINVAL;
}
static void
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 83e76cefbccd..fd552f7dced4 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -858,9 +858,6 @@ enum yt921x_fdb_entry_status {
#define YT921X_NAME "yt921x"
-#define yt921x_port_is_internal(port) ((port) < 8)
-#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)
-
struct yt921x_mib {
u64 rx_broadcast;
u64 rx_pause;
@@ -936,8 +933,12 @@ struct yt921x_acl_blk {
struct yt921x_port {
unsigned char index;
- bool hairpin;
- bool isolated;
+ /* SerDes in use */
+ bool serdes:1;
+ /* BR_HAIRPIN_MODE */
+ bool hairpin:1;
+ /* BR_ISOLATED */
+ bool isolated:1;
struct delayed_work mib_read;
struct yt921x_mib mib;
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 4/8] net: dsa: motorcomm: Fix register bit field names
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
` (2 preceding siblings ...)
2026-09-23 2:19 ` [PATCH net-next v4 3/8] net: dsa: motorcomm: Identify port type at runtime David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 5/8] net: dsa: motorcomm: Introduce yt921x_speed David Yang
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Review and fix the incorrectness in the bit field names. No functional
change.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/chip.c | 3 ++-
drivers/net/dsa/motorcomm/chip.h | 9 +++++----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index ea4b29882b5c..f22c37e0b5bc 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -3742,7 +3742,8 @@ static int yt921x_port_down(struct yt921x_priv *priv, int port)
u32 mask;
int res;
- mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
+ mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_RX_MAC_EN |
+ YT921X_PORT_TX_MAC_EN;
res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask);
if (res)
return res;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index fd552f7dced4..3fb0c6a69dd8 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -42,7 +42,7 @@
#define YT921X_PVID_SEL 0x80014
#define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port)
#define YT921X_SERDES_CTRL 0x80028
-#define YT921X_SERDES_CTRL_PORTn_TEST(port) BIT((port) - 3)
+#define YT921X_SERDES_CTRL_TEST BIT(6)
#define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8)
#define YT921X_IO_LEVEL 0x80030
#define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4)
@@ -94,11 +94,12 @@
#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2)
#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3)
#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4)
-#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port))
+#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) /* Bit 7-0 shared with PORT_STATUS */
#define YT921X_PORT_CTRL_PAUSE_AN BIT(10)
+#define YT921X_PORT_CTRL_LINK_AN BIT(9)
+#define YT921X_PORT_CTRL_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */
#define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port))
-#define YT921X_PORT_LINK BIT(9) /* CTRL: auto negotiation */
-#define YT921X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */
+#define YT921X_PORT_LINK BIT(8)
#define YT921X_PORT_DUPLEX_FULL BIT(7)
#define YT921X_PORT_RX_PAUSE BIT(6)
#define YT921X_PORT_TX_PAUSE BIT(5)
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 5/8] net: dsa: motorcomm: Introduce yt921x_speed
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
` (3 preceding siblings ...)
2026-09-23 2:19 ` [PATCH net-next v4 4/8] net: dsa: motorcomm: Fix register bit field names David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 6/8] net: dsa: motorcomm: Hoist port_to_priv helper into chip.h David Yang
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
FIELD_PREP values cannot be used as case labels. Introduce enum for
future use.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 62 ++++----------------------------
drivers/net/dsa/motorcomm/chip.h | 43 ++++++++++++++--------
2 files changed, 34 insertions(+), 71 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index f22c37e0b5bc..06e2c60b415f 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -3771,29 +3771,15 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
{
const struct yt921x_info *info = priv->info;
struct yt921x_port *pp = &priv->ports[port];
+ int ps = ethtool_speed_to_yt921x(speed);
u32 mask;
u32 ctrl;
int res;
- switch (speed) {
- case SPEED_10:
- ctrl = YT921X_PORT_SPEED_10;
- break;
- case SPEED_100:
- ctrl = YT921X_PORT_SPEED_100;
- break;
- case SPEED_1000:
- ctrl = YT921X_PORT_SPEED_1000;
- break;
- case SPEED_2500:
- ctrl = YT921X_PORT_SPEED_2500;
- break;
- case SPEED_10000:
- ctrl = YT921X_PORT_SPEED_10000;
- break;
- default:
+ if (ps == YT921X_SPEED_INVALID)
return -EINVAL;
- }
+
+ ctrl = YT921X_PORT_SPEED(ps);
if (duplex == DUPLEX_FULL)
ctrl |= YT921X_PORT_DUPLEX_FULL;
if (tx_pause)
@@ -3810,25 +3796,7 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (pp->serdes) {
mask = YT921X_SERDES_SPEED_M;
- switch (speed) {
- case SPEED_10:
- ctrl = YT921X_SERDES_SPEED_10;
- break;
- case SPEED_100:
- ctrl = YT921X_SERDES_SPEED_100;
- break;
- case SPEED_1000:
- ctrl = YT921X_SERDES_SPEED_1000;
- break;
- case SPEED_2500:
- ctrl = YT921X_SERDES_SPEED_2500;
- break;
- case SPEED_10000:
- ctrl = YT921X_SERDES_SPEED_10000;
- break;
- default:
- return -EINVAL;
- }
+ ctrl = YT921X_SERDES_SPEED(ps);
mask |= YT921X_SERDES_DUPLEX_FULL;
if (duplex == DUPLEX_FULL)
ctrl |= YT921X_SERDES_DUPLEX_FULL;
@@ -3850,25 +3818,7 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (res)
return res;
- switch (speed) {
- case SPEED_10:
- ctrl = YT921X_MDIO_POLLING_SPEED_10;
- break;
- case SPEED_100:
- ctrl = YT921X_MDIO_POLLING_SPEED_100;
- break;
- case SPEED_1000:
- ctrl = YT921X_MDIO_POLLING_SPEED_1000;
- break;
- case SPEED_2500:
- ctrl = YT921X_MDIO_POLLING_SPEED_2500;
- break;
- case SPEED_10000:
- ctrl = YT921X_MDIO_POLLING_SPEED_10000;
- break;
- default:
- return -EINVAL;
- }
+ ctrl = YT921X_MDIO_POLLING_SPEED(ps);
if (duplex == DUPLEX_FULL)
ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL;
ctrl |= YT921X_MDIO_POLLING_LINK;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 3fb0c6a69dd8..30d131eb9844 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -89,11 +89,6 @@
#define YT921X_SERDES_DUPLEX_FULL BIT(3)
#define YT921X_SERDES_SPEED_M GENMASK(2, 0)
#define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x))
-#define YT921X_SERDES_SPEED_10 YT921X_SERDES_SPEED(0)
-#define YT921X_SERDES_SPEED_100 YT921X_SERDES_SPEED(1)
-#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2)
-#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3)
-#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4)
#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) /* Bit 7-0 shared with PORT_STATUS */
#define YT921X_PORT_CTRL_PAUSE_AN BIT(10)
#define YT921X_PORT_CTRL_LINK_AN BIT(9)
@@ -107,11 +102,6 @@
#define YT921X_PORT_TX_MAC_EN BIT(3)
#define YT921X_PORT_SPEED_M GENMASK(2, 0)
#define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x))
-#define YT921X_PORT_SPEED_10 YT921X_PORT_SPEED(0)
-#define YT921X_PORT_SPEED_100 YT921X_PORT_SPEED(1)
-#define YT921X_PORT_SPEED_1000 YT921X_PORT_SPEED(2)
-#define YT921X_PORT_SPEED_10000 YT921X_PORT_SPEED(3)
-#define YT921X_PORT_SPEED_2500 YT921X_PORT_SPEED(4)
#define YT921X_PON_STRAP_FUNC 0x80320
#define YT921X_PON_STRAP_VAL 0x80324
#define YT921X_PON_STRAP_CAP 0x80328
@@ -122,11 +112,16 @@
#define YT921X_MDIO_POLLING_LINK BIT(3)
#define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0)
#define YT921X_MDIO_POLLING_SPEED(x) FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x))
-#define YT921X_MDIO_POLLING_SPEED_10 YT921X_MDIO_POLLING_SPEED(0)
-#define YT921X_MDIO_POLLING_SPEED_100 YT921X_MDIO_POLLING_SPEED(1)
-#define YT921X_MDIO_POLLING_SPEED_1000 YT921X_MDIO_POLLING_SPEED(2)
-#define YT921X_MDIO_POLLING_SPEED_10000 YT921X_MDIO_POLLING_SPEED(3)
-#define YT921X_MDIO_POLLING_SPEED_2500 YT921X_MDIO_POLLING_SPEED(4)
+
+enum yt921x_speed {
+ YT921X_SPEED_10,
+ YT921X_SPEED_100,
+ YT921X_SPEED_1000,
+ YT921X_SPEED_10000,
+ YT921X_SPEED_2500,
+ YT921X_SPEED_INVALID
+};
+
#define YT921X_SENSOR 0x8036c
#define YT921X_SENSOR_TEMP BIT(18)
#define YT921X_TEMP 0x80374
@@ -1004,4 +999,22 @@ struct yt921x_priv {
struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM];
};
+static inline int ethtool_speed_to_yt921x(int speed)
+{
+ switch (speed) {
+ case SPEED_10:
+ return YT921X_SPEED_10;
+ case SPEED_100:
+ return YT921X_SPEED_100;
+ case SPEED_1000:
+ return YT921X_SPEED_1000;
+ case SPEED_2500:
+ return YT921X_SPEED_2500;
+ case SPEED_10000:
+ return YT921X_SPEED_10000;
+ default:
+ return YT921X_SPEED_INVALID;
+ }
+}
+
#endif
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 6/8] net: dsa: motorcomm: Hoist port_to_priv helper into chip.h
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
` (4 preceding siblings ...)
2026-09-23 2:19 ` [PATCH net-next v4 5/8] net: dsa: motorcomm: Introduce yt921x_speed David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 7/8] net: dsa: motorcomm: Split MDIO bus module David Yang
2026-09-23 2:19 ` [PATCH net-next v4 8/8] net: dsa: motorcomm: Add SerDes PCS David Yang
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
yt921x_poll_mib() open-codes the same container_of_const() expression
that leds.c defines as to_yt921x_priv(). Move the macro to chip.h as
yt921x_port_to_priv() so both files use one definition.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/chip.c | 3 +--
drivers/net/dsa/motorcomm/chip.h | 3 +++
drivers/net/dsa/motorcomm/leds.c | 14 ++++++--------
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 06e2c60b415f..ee433f1e1b58 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -598,9 +598,8 @@ static void yt921x_poll_mib(struct work_struct *work)
{
struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
mib_read.work);
- struct yt921x_priv *priv = (void *)(pp - pp->index) -
- offsetof(struct yt921x_priv, ports);
unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int port = pp->index;
int res;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 30d131eb9844..6a9fb27ddfb7 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -999,6 +999,9 @@ struct yt921x_priv {
struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM];
};
+#define yt921x_port_to_priv(pp) \
+ container_of_const((pp), struct yt921x_priv, ports[(pp)->index])
+
static inline int ethtool_speed_to_yt921x(int speed)
{
switch (speed) {
diff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c
index 0e8761f37995..6613fe3b0471 100644
--- a/drivers/net/dsa/motorcomm/leds.c
+++ b/drivers/net/dsa/motorcomm/leds.c
@@ -12,8 +12,6 @@
#define to_yt921x_led(led_cdev) \
container_of_const((led_cdev), struct yt921x_led, cdev)
#define to_yt921x_port(led) ((led)->port)
-#define to_yt921x_priv(pp) \
- container_of_const((pp), struct yt921x_priv, ports[(pp)->index])
#define to_device(priv) ((priv)->ds.dev)
static u32 yt921x_led_regaddr(struct yt921x_priv *priv, int port, int group)
@@ -321,7 +319,7 @@ yt921x_cled_brightness_set_blocking(struct led_classdev *led_cdev,
{
struct yt921x_led *led = to_yt921x_led(led_cdev);
struct yt921x_port *pp = to_yt921x_port(led);
- struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
@@ -337,7 +335,7 @@ yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
{
struct yt921x_led *led = to_yt921x_led(led_cdev);
struct yt921x_port *pp = to_yt921x_port(led);
- struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
@@ -353,7 +351,7 @@ yt921x_cled_hw_control_get_device(struct led_classdev *led_cdev)
{
struct yt921x_led *led = to_yt921x_led(led_cdev);
struct yt921x_port *pp = to_yt921x_port(led);
- struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
struct dsa_port *dp;
dp = dsa_to_port(&priv->ds, pp->index);
@@ -374,7 +372,7 @@ yt921x_cled_hw_control_is_supported(struct led_classdev *led_cdev,
{
struct yt921x_led *led = to_yt921x_led(led_cdev);
struct yt921x_port *pp = to_yt921x_port(led);
- struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
if (yt921x_led_trigger_is_supported(priv, pp->index, led->group, flags))
return 0;
@@ -386,7 +384,7 @@ yt921x_cled_hw_control_get(struct led_classdev *led_cdev, unsigned long *flagsp)
{
struct yt921x_led *led = to_yt921x_led(led_cdev);
struct yt921x_port *pp = to_yt921x_port(led);
- struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
@@ -401,7 +399,7 @@ yt921x_cled_hw_control_set(struct led_classdev *led_cdev, unsigned long flags)
{
struct yt921x_led *led = to_yt921x_led(led_cdev);
struct yt921x_port *pp = to_yt921x_port(led);
- struct yt921x_priv *priv = to_yt921x_priv(pp);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
int res;
mutex_lock(&priv->reg_lock);
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 7/8] net: dsa: motorcomm: Split MDIO bus module
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
` (5 preceding siblings ...)
2026-09-23 2:19 ` [PATCH net-next v4 6/8] net: dsa: motorcomm: Hoist port_to_priv helper into chip.h David Yang
@ 2026-09-23 2:19 ` David Yang
2026-09-23 2:19 ` [PATCH net-next v4 8/8] net: dsa: motorcomm: Add SerDes PCS David Yang
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Split it in order to be used in other modules.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 269 +-------------------------
drivers/net/dsa/motorcomm/chip.h | 21 --
drivers/net/dsa/motorcomm/mdio_bus.c | 278 +++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/mdio_bus.h | 39 ++++
5 files changed, 319 insertions(+), 289 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/mdio_bus.c
create mode 100644 drivers/net/dsa/motorcomm/mdio_bus.h
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index aeb12cb91f93..f7cce7eabbb8 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -2,4 +2,5 @@
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
+yt921x-objs += mdio_bus.o
yt921x-objs += smi.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index ee433f1e1b58..6e1219ef05d7 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -27,6 +27,7 @@
#include "chip.h"
#include "leds.h"
+#include "mdio_bus.h"
#include "smi.h"
struct yt921x_mib_desc {
@@ -266,274 +267,6 @@ static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
/* TODO: SPI/I2C */
-static int yt921x_intif_wait(struct yt921x_priv *priv)
-{
- u32 val = 0;
-
- return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
- &val);
-}
-
-static int
-yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
-{
- struct device *dev = to_device(priv);
- u32 mask;
- u32 ctrl;
- u32 val;
- int res;
-
- res = yt921x_intif_wait(priv);
- if (res)
- return res;
-
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_READ;
- res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
-
- res = yt921x_intif_wait(priv);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
- if (res)
- return res;
-
- if ((u16)val != val)
- dev_info(dev,
- "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
- __func__, port, reg, val);
- *valp = (u16)val;
- return 0;
-}
-
-static int
-yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
-{
- u32 mask;
- u32 ctrl;
- int res;
-
- res = yt921x_intif_wait(priv);
- if (res)
- return res;
-
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_WRITE;
- res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
-
- return yt921x_intif_wait(priv);
-}
-
-static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
-{
- struct yt921x_priv *priv = mbus->priv;
- u16 val;
- int res;
-
- if (port >= YT921X_PORT_NUM)
- return U16_MAX;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_intif_read(priv, port, reg, &val);
- mutex_unlock(&priv->reg_lock);
-
- if (res)
- return res;
- return val;
-}
-
-static int
-yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
-{
- struct yt921x_priv *priv = mbus->priv;
- int res;
-
- if (port >= YT921X_PORT_NUM)
- return -ENODEV;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_intif_write(priv, port, reg, data);
- mutex_unlock(&priv->reg_lock);
-
- return res;
-}
-
-static int
-yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
-{
- struct device *dev = to_device(priv);
- struct mii_bus *mbus;
- int res;
-
- mbus = devm_mdiobus_alloc(dev);
- if (!mbus)
- return -ENOMEM;
-
- mbus->name = "YT921x internal MDIO bus";
- snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
- mbus->priv = priv;
- mbus->read = yt921x_mbus_int_read;
- mbus->write = yt921x_mbus_int_write;
- mbus->parent = dev;
- mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
-
- res = devm_of_mdiobus_register(dev, mbus, mnp);
- if (res)
- return res;
-
- priv->mbus_int = mbus;
-
- return 0;
-}
-
-static int yt921x_extif_wait(struct yt921x_priv *priv)
-{
- u32 val = 0;
-
- return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
- &val);
-}
-
-static int
-yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
-{
- struct device *dev = to_device(priv);
- u32 mask;
- u32 ctrl;
- u32 val;
- int res;
-
- res = yt921x_extif_wait(priv);
- if (res)
- return res;
-
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
- res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
-
- res = yt921x_extif_wait(priv);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
- if (res)
- return res;
-
- if ((u16)val != val)
- dev_info(dev,
- "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
- __func__, port, reg, val);
- *valp = (u16)val;
- return 0;
-}
-
-static int
-yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
-{
- u32 mask;
- u32 ctrl;
- int res;
-
- res = yt921x_extif_wait(priv);
- if (res)
- return res;
-
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
- res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
-
- return yt921x_extif_wait(priv);
-}
-
-static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
-{
- struct yt921x_priv *priv = mbus->priv;
- u16 val;
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_extif_read(priv, port, reg, &val);
- mutex_unlock(&priv->reg_lock);
-
- if (res)
- return res;
- return val;
-}
-
-static int
-yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
-{
- struct yt921x_priv *priv = mbus->priv;
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_extif_write(priv, port, reg, data);
- mutex_unlock(&priv->reg_lock);
-
- return res;
-}
-
-static int
-yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
-{
- struct device *dev = to_device(priv);
- struct mii_bus *mbus;
- int res;
-
- mbus = devm_mdiobus_alloc(dev);
- if (!mbus)
- return -ENOMEM;
-
- mbus->name = "YT921x external MDIO bus";
- snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
- mbus->priv = priv;
- /* TODO: c45? */
- mbus->read = yt921x_mbus_ext_read;
- mbus->write = yt921x_mbus_ext_write;
- mbus->parent = dev;
-
- res = devm_of_mdiobus_register(dev, mbus, mnp);
- if (res)
- return res;
-
- priv->mbus_ext = mbus;
-
- return 0;
-}
-
/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
static int yt921x_read_mib(struct yt921x_priv *priv, int port)
{
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 6a9fb27ddfb7..f68b77f815b1 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -243,27 +243,6 @@ enum yt921x_speed {
#define YT9218_SYS_CLK_167M 0
#define YT921X_SYS_CLK_143M 1
-#define YT921X_EXT_MBUS_OP 0x6a000
-#define YT921X_INT_MBUS_OP 0xf0000
-#define YT921X_MBUS_OP_START BIT(0)
-#define YT921X_EXT_MBUS_CTRL 0x6a004
-#define YT921X_INT_MBUS_CTRL 0xf0004
-#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21)
-#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x))
-#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16)
-#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x))
-#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */
-#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x))
-#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4)
-#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */
-#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x))
-#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1)
-#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2)
-#define YT921X_EXT_MBUS_DOUT 0x6a008
-#define YT921X_INT_MBUS_DOUT 0xf0008
-#define YT921X_EXT_MBUS_DIN 0x6a00c
-#define YT921X_INT_MBUS_DIN 0xf000c
-
#define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port))
#define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4)
#define YT921X_PORT_EGR_TPID_CTAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x))
diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c
new file mode 100644
index 000000000000..ba70343f3f33
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mdio_bus.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include <linux/of_mdio.h>
+
+#include "chip.h"
+#include "mdio_bus.h"
+#include "smi.h"
+
+#define to_device(priv) ((priv)->ds.dev)
+
+static int yt921x_intif_wait(struct yt921x_priv *priv)
+{
+ u32 val = 0;
+
+ return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
+ &val);
+}
+
+static int
+yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
+{
+ struct device *dev = to_device(priv);
+ u32 mask;
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ res = yt921x_intif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_READ;
+ res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ res = yt921x_intif_wait(priv);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
+ if (res)
+ return res;
+
+ if ((u16)val != val)
+ dev_info(dev,
+ "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
+ __func__, port, reg, val);
+ *valp = (u16)val;
+ return 0;
+}
+
+static int
+yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
+{
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ res = yt921x_intif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_WRITE;
+ res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ return yt921x_intif_wait(priv);
+}
+
+static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ u16 val;
+ int res;
+
+ if (port >= YT921X_PORT_NUM)
+ return U16_MAX;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_read(priv, port, reg, &val);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ return res;
+ return val;
+}
+
+static int
+yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ int res;
+
+ if (port >= YT921X_PORT_NUM)
+ return -ENODEV;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_write(priv, port, reg, data);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
+{
+ struct device *dev = to_device(priv);
+ struct mii_bus *mbus;
+ int res;
+
+ mbus = devm_mdiobus_alloc(dev);
+ if (!mbus)
+ return -ENOMEM;
+
+ mbus->name = "YT921x internal MDIO bus";
+ snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+ mbus->priv = priv;
+ mbus->read = yt921x_mbus_int_read;
+ mbus->write = yt921x_mbus_int_write;
+ mbus->parent = dev;
+ mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
+
+ res = devm_of_mdiobus_register(dev, mbus, mnp);
+ if (res)
+ return res;
+
+ priv->mbus_int = mbus;
+
+ return 0;
+}
+
+static int yt921x_extif_wait(struct yt921x_priv *priv)
+{
+ u32 val = 0;
+
+ return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
+ &val);
+}
+
+static int
+yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
+{
+ struct device *dev = to_device(priv);
+ u32 mask;
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ res = yt921x_extif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
+ res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ res = yt921x_extif_wait(priv);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
+ if (res)
+ return res;
+
+ if ((u16)val != val)
+ dev_info(dev,
+ "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
+ __func__, port, reg, val);
+ *valp = (u16)val;
+ return 0;
+}
+
+static int
+yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
+{
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ res = yt921x_extif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
+ res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ return yt921x_extif_wait(priv);
+}
+
+static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ u16 val;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_extif_read(priv, port, reg, &val);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ return res;
+ return val;
+}
+
+static int
+yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_extif_write(priv, port, reg, data);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
+{
+ struct device *dev = to_device(priv);
+ struct mii_bus *mbus;
+ int res;
+
+ mbus = devm_mdiobus_alloc(dev);
+ if (!mbus)
+ return -ENOMEM;
+
+ mbus->name = "YT921x external MDIO bus";
+ snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
+ mbus->priv = priv;
+ /* TODO: c45? */
+ mbus->read = yt921x_mbus_ext_read;
+ mbus->write = yt921x_mbus_ext_write;
+ mbus->parent = dev;
+
+ res = devm_of_mdiobus_register(dev, mbus, mnp);
+ if (res)
+ return res;
+
+ priv->mbus_ext = mbus;
+
+ return 0;
+}
diff --git a/drivers/net/dsa/motorcomm/mdio_bus.h b/drivers/net/dsa/motorcomm/mdio_bus.h
new file mode 100644
index 000000000000..ae5792b8da24
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mdio_bus.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_MDIO_BUS_H
+#define _YT_MDIO_BUS_H
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/of.h>
+
+#define YT921X_EXT_MBUS_OP 0x6a000
+#define YT921X_INT_MBUS_OP 0xf0000
+#define YT921X_MBUS_OP_START BIT(0)
+#define YT921X_EXT_MBUS_CTRL 0x6a004
+#define YT921X_INT_MBUS_CTRL 0xf0004
+#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21)
+#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x))
+#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16)
+#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x))
+#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */
+#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x))
+#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4)
+#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */
+#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x))
+#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1)
+#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2)
+#define YT921X_EXT_MBUS_DOUT 0x6a008
+#define YT921X_INT_MBUS_DOUT 0xf0008
+#define YT921X_EXT_MBUS_DIN 0x6a00c
+#define YT921X_INT_MBUS_DIN 0xf000c
+
+struct yt921x_priv;
+
+int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp);
+int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp);
+
+#endif
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH net-next v4 8/8] net: dsa: motorcomm: Add SerDes PCS
2026-09-23 2:19 [PATCH net-next v4 0/8] net: dsa: motorcomm: Add SerDes PCS David Yang
` (6 preceding siblings ...)
2026-09-23 2:19 ` [PATCH net-next v4 7/8] net: dsa: motorcomm: Split MDIO bus module David Yang
@ 2026-09-23 2:19 ` David Yang
7 siblings, 0 replies; 9+ messages in thread
From: David Yang @ 2026-09-23 2:19 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
linux-kernel
Support in-band negotiation of the SerDes ports.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 83 ++++++---
drivers/net/dsa/motorcomm/chip.h | 6 +
drivers/net/dsa/motorcomm/mdio_bus.c | 31 +++-
drivers/net/dsa/motorcomm/mdio_bus.h | 15 ++
drivers/net/dsa/motorcomm/pcs-921x.c | 247 +++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/pcs.h | 13 ++
7 files changed, 367 insertions(+), 29 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/pcs-921x.c
create mode 100644 drivers/net/dsa/motorcomm/pcs.h
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index f7cce7eabbb8..1d2c1b3064c4 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
yt921x-objs := chip.o
yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o
yt921x-objs += mdio_bus.o
+yt921x-objs += pcs-921x.o
yt921x-objs += smi.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 6e1219ef05d7..3ff2de383157 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -28,6 +28,7 @@
#include "chip.h"
#include "leds.h"
#include "mdio_bus.h"
+#include "pcs.h"
#include "smi.h"
struct yt921x_mib_desc {
@@ -3511,6 +3512,10 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (ps == YT921X_SPEED_INVALID)
return -EINVAL;
+ mask = YT921X_PORT_SPEED_M | YT921X_PORT_TX_MAC_EN |
+ YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_PAUSE |
+ YT921X_PORT_RX_PAUSE | YT921X_PORT_DUPLEX_FULL |
+ YT921X_PORT_CTRL_LINK_AN;
ctrl = YT921X_PORT_SPEED(ps);
if (duplex == DUPLEX_FULL)
ctrl |= YT921X_PORT_DUPLEX_FULL;
@@ -3519,7 +3524,9 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (rx_pause)
ctrl |= YT921X_PORT_RX_PAUSE;
ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
- res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl);
+ if (pp->serdes && pp->inband)
+ ctrl |= YT921X_PORT_CTRL_LINK_AN;
+ res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl);
if (res)
return res;
@@ -3539,7 +3546,8 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (rx_pause)
ctrl |= YT921X_SERDES_RX_PAUSE;
mask |= YT921X_SERDES_LINK;
- ctrl |= YT921X_SERDES_LINK;
+ if (!pp->inband)
+ ctrl |= YT921X_SERDES_LINK;
res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
mask, ctrl);
if (res)
@@ -3570,7 +3578,6 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
struct yt921x_port *pp = &priv->ports[port];
struct device *dev = to_device(priv);
u32 mask;
- u32 ctrl;
int res;
if (BIT(port) & info->internal_mask) {
@@ -3603,28 +3610,6 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
*/
pp->serdes = true;
- mask = YT921X_SERDES_MODE_M;
- switch (interface) {
- case PHY_INTERFACE_MODE_SGMII:
- ctrl = YT921X_SERDES_MODE_SGMII;
- break;
- case PHY_INTERFACE_MODE_100BASEX:
- ctrl = YT921X_SERDES_MODE_100BASEX;
- break;
- case PHY_INTERFACE_MODE_1000BASEX:
- ctrl = YT921X_SERDES_MODE_1000BASEX;
- break;
- case PHY_INTERFACE_MODE_2500BASEX:
- ctrl = YT921X_SERDES_MODE_2500BASEX;
- break;
- default:
- return -EINVAL;
- }
- res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
- mask, ctrl);
- if (res)
- return res;
-
break;
/* add XMII support here */
default:
@@ -3638,6 +3623,29 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
return -EINVAL;
}
+static struct phylink_pcs *
+yt921x_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ const struct yt921x_info *info = priv->info;
+ int port = dp->index;
+
+ if (!(BIT(port) & info->serdes_mask))
+ return NULL;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_100BASEX:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ return &priv->ports[port].pcs;
+ default:
+ return NULL;
+ }
+}
+
static void
yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
@@ -4225,6 +4233,11 @@ static int yt921x_chip_setup(struct yt921x_priv *priv)
if (res)
return res;
+ res = yt921x_reg_clear_bits(priv, YT921X_SERDES_CTRL,
+ YT921X_SERDES_CTRL_TEST);
+ if (res)
+ return res;
+
return 0;
}
@@ -4234,6 +4247,8 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
struct device *dev = to_device(priv);
struct device_node *np = dev->of_node;
struct device_node *child;
+ unsigned long mask;
+ int port;
int res;
mutex_lock(&priv->reg_lock);
@@ -4267,6 +4282,23 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
return -ENODEV;
}
+ mask = priv->info->serdes_mask;
+ for_each_set_bit(port, &mask, YT921X_PORT_NUM) {
+ struct yt921x_port *pp = &priv->ports[port];
+
+ pp->pcs.ops = &yt921x_phylink_pcs_ops;
+ pp->pcs.poll = true;
+
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ pp->pcs.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_100BASEX,
+ pp->pcs.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ pp->pcs.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ pp->pcs.supported_interfaces);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_chip_setup(priv);
mutex_unlock(&priv->reg_lock);
@@ -4284,6 +4316,7 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
}
static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
+ .mac_select_pcs = yt921x_phylink_mac_select_pcs,
.mac_link_down = yt921x_phylink_mac_link_down,
.mac_link_up = yt921x_phylink_mac_link_up,
.mac_config = yt921x_phylink_mac_config,
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index f68b77f815b1..28e4f26f9cc4 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -910,6 +910,8 @@ struct yt921x_port {
/* SerDes in use */
bool serdes:1;
+ /* Link from in-band status (PHYLINK_PCS_NEG_INBAND) */
+ bool inband:1;
/* BR_HAIRPIN_MODE */
bool hairpin:1;
/* BR_ISOLATED */
@@ -929,8 +931,12 @@ struct yt921x_port {
struct yt921x_led *leds[YT921X_LED_GROUP_NUM];
#endif
+
+ struct phylink_pcs pcs;
};
+#define pcs_to_yt921x_port(_pcs) container_of((_pcs), struct yt921x_port, pcs)
+
struct yt921x_reg_ops {
int (*read)(void *context, u32 reg, u32 *valp);
int (*write)(void *context, u32 reg, u32 val);
diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c
index ba70343f3f33..1a3f3cc68275 100644
--- a/drivers/net/dsa/motorcomm/mdio_bus.c
+++ b/drivers/net/dsa/motorcomm/mdio_bus.c
@@ -19,8 +19,7 @@ static int yt921x_intif_wait(struct yt921x_priv *priv)
&val);
}
-static int
-yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
+int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
{
struct device *dev = to_device(priv);
u32 mask;
@@ -58,8 +57,7 @@ yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
return 0;
}
-static int
-yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
+int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
{
u32 mask;
u32 ctrl;
@@ -86,6 +84,31 @@ yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
return yt921x_intif_wait(priv);
}
+int
+yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg,
+ u16 mask, u16 val)
+{
+ int res;
+ u16 v;
+ u16 u;
+
+ res = yt921x_intif_read(priv, port, reg, &v);
+ if (res)
+ return res;
+
+ u = v;
+ u &= ~mask;
+ u |= val;
+ if (u == v)
+ return 0;
+
+ res = yt921x_intif_write(priv, port, reg, u);
+ if (res)
+ return res;
+
+ return 1;
+}
+
static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
{
struct yt921x_priv *priv = mbus->priv;
diff --git a/drivers/net/dsa/motorcomm/mdio_bus.h b/drivers/net/dsa/motorcomm/mdio_bus.h
index ae5792b8da24..e79b725d435b 100644
--- a/drivers/net/dsa/motorcomm/mdio_bus.h
+++ b/drivers/net/dsa/motorcomm/mdio_bus.h
@@ -33,6 +33,21 @@
struct yt921x_priv;
+int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp);
+int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val);
+int yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg,
+ u16 mask, u16 val);
+
+static inline int
+yt921x_intif_modify(struct yt921x_priv *priv, int port, int reg, u16 mask,
+ u16 val)
+{
+ int res;
+
+ res = yt921x_intif_modify_changed(priv, port, reg, mask, val);
+ return res >= 0 ? 0 : res;
+}
+
int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp);
int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp);
diff --git a/drivers/net/dsa/motorcomm/pcs-921x.c b/drivers/net/dsa/motorcomm/pcs-921x.c
new file mode 100644
index 000000000000..47467d803e13
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/pcs-921x.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include "chip.h"
+#include "mdio_bus.h"
+#include "pcs.h"
+#include "smi.h"
+
+#define to_device(priv) ((priv)->ds.dev)
+
+static int
+yt921x_serdes_config(struct yt921x_priv *priv, int port, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising, bool permit_pause_to_mac)
+{
+ bool inband = neg_mode & PHYLINK_PCS_NEG_INBAND;
+ struct yt921x_port *pp = &priv->ports[port];
+ bool changed = false;
+ u16 bmcr;
+ u32 mask;
+ u32 ctrl;
+ u16 val;
+ int adv;
+ int res;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ ctrl = YT921X_SERDES_MODE_SGMII;
+ break;
+ case PHY_INTERFACE_MODE_100BASEX:
+ ctrl = YT921X_SERDES_MODE_100BASEX;
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ ctrl = YT921X_SERDES_MODE_1000BASEX;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ ctrl = YT921X_SERDES_MODE_2500BASEX;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
+ YT921X_SERDES_MODE_M, ctrl);
+ if (res)
+ return res;
+
+ mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_CTRL_PAUSE_AN;
+ ctrl = 0;
+ if (inband)
+ ctrl |= YT921X_PORT_CTRL_LINK_AN;
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && permit_pause_to_mac &&
+ interface != PHY_INTERFACE_MODE_SGMII)
+ ctrl |= YT921X_PORT_CTRL_PAUSE_AN;
+ res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl);
+ if (res)
+ return res;
+
+ if (inband) {
+ res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port),
+ YT921X_SERDES_LINK);
+ if (res)
+ return res;
+ }
+
+ adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising);
+ if (adv >= 0) {
+ res = yt921x_intif_modify_changed(priv, port, MII_ADVERTISE,
+ U16_MAX, adv);
+ if (res < 0)
+ return res;
+
+ changed = !!res;
+ }
+
+ res = yt921x_intif_read(priv, port, MII_BMCR, &val);
+ if (res)
+ return res;
+
+ bmcr = val;
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ bmcr |= BMCR_ANENABLE;
+ else
+ bmcr &= ~BMCR_ANENABLE;
+
+ /* If the ANENABLE bit was changed, the PHY will restart negotiation,
+ * so we don't need to flag a change to trigger its own restart.
+ */
+ if (bmcr != val)
+ changed = false;
+
+ bmcr &= ~BMCR_ISOLATE;
+ res = yt921x_intif_write(priv, port, MII_BMCR, bmcr);
+ if (res)
+ return res;
+
+ pp->inband = inband;
+ return changed;
+}
+
+static unsigned int
+yt921x_phylink_pcs_inband_caps(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ if (interface == PHY_INTERFACE_MODE_100BASEX)
+ return LINK_INBAND_DISABLE;
+
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+}
+
+static void
+yt921x_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
+ struct device *dev = to_device(priv);
+ int port = pp->index;
+ u16 bmsr;
+ u16 lpa;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+
+ res = yt921x_intif_read(priv, port, MII_BMSR, &bmsr);
+ if (res)
+ goto end;
+
+ res = yt921x_intif_read(priv, port, MII_LPA, &lpa);
+
+end:
+ mutex_unlock(&priv->reg_lock);
+
+ if (res) {
+ dev_err_ratelimited(dev, "Failed to %s PCS port %d: %i\n",
+ "get state of", port, res);
+ state->link = false;
+ return;
+ }
+
+ if (state->interface == PHY_INTERFACE_MODE_100BASEX) {
+ state->speed = SPEED_100;
+ state->duplex = DUPLEX_FULL;
+ state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
+ state->link = !!(bmsr & BMSR_LSTATUS);
+ return;
+ }
+
+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
+}
+
+static void yt921x_phylink_pcs_an_restart(struct phylink_pcs *pcs)
+{
+ struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
+ struct device *dev = to_device(priv);
+ int port = pp->index;
+ u16 val;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+
+ res = yt921x_intif_read(priv, port, MII_BMCR, &val);
+ if (res)
+ goto end;
+
+ val |= BMCR_ANRESTART;
+ res = yt921x_intif_write(priv, port, MII_BMCR, val);
+
+end:
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s PCS port %d: %i\n", "restart",
+ port, res);
+}
+
+static void yt921x_phylink_pcs_disable(struct phylink_pcs *pcs)
+{
+ struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
+ struct device *dev = to_device(priv);
+ int port = pp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_modify(priv, port, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s PCS port %d: %i\n", "power down",
+ port, res);
+}
+
+static int yt921x_phylink_pcs_enable(struct phylink_pcs *pcs)
+{
+ struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
+ int port = pp->index;
+ u16 val;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+
+ res = yt921x_intif_read(priv, port, MII_BMCR, &val);
+ if (res)
+ goto end;
+
+ val &= ~BMCR_PDOWN;
+ val |= BMCR_ANRESTART;
+ res = yt921x_intif_write(priv, port, MII_BMCR, val);
+
+end:
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
+ struct yt921x_priv *priv = yt921x_port_to_priv(pp);
+ int port = pp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_serdes_config(priv, port, neg_mode, interface,
+ advertising, permit_pause_to_mac);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+const struct phylink_pcs_ops yt921x_phylink_pcs_ops = {
+ .pcs_inband_caps = yt921x_phylink_pcs_inband_caps,
+ .pcs_get_state = yt921x_phylink_pcs_get_state,
+ .pcs_an_restart = yt921x_phylink_pcs_an_restart,
+ .pcs_disable = yt921x_phylink_pcs_disable,
+ .pcs_enable = yt921x_phylink_pcs_enable,
+ .pcs_config = yt921x_phylink_pcs_config,
+};
diff --git a/drivers/net/dsa/motorcomm/pcs.h b/drivers/net/dsa/motorcomm/pcs.h
new file mode 100644
index 000000000000..42426558086a
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/pcs.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_PCS_H
+#define _YT_PCS_H
+
+#include <linux/phylink.h>
+
+extern const struct phylink_pcs_ops yt921x_phylink_pcs_ops;
+
+#endif
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread