* [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
@ 2026-09-09 19:05 ` David Yang
2026-09-10 20:59 ` Andrew Lunn
2026-09-09 19:05 ` [PATCH net-next 2/7] net: dsa: motorcomm: Check port type with runtime info David Yang
` (5 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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>
---
drivers/net/dsa/motorcomm/chip.c | 39 ++++++++++++--------------------
drivers/net/dsa/motorcomm/chip.h | 18 +++++++++++++++
2 files changed, 32 insertions(+), 25 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index d663af010f43..64fdc5109cc6 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -96,18 +96,6 @@ 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;
-};
-
#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)
@@ -117,37 +105,44 @@ static const struct yt921x_info yt921x_infos[] = {
{
"YT9215SC", YT9215_MAJOR, 1, 0,
YT921X_PORT_MASK_INT0_n(5),
+ YT921X_PORT_MASK_EXT1,
YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
},
{
"YT9215S", YT9215_MAJOR, 2, 0,
YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ YT921X_PORT_MASK_EXT1,
+ YT921X_PORT_MASK_EXT0,
},
{
"YT9215RB", YT9215_MAJOR, 3, 0,
YT921X_PORT_MASK_INT0_n(5),
YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ 0,
},
{
"YT9214NB", YT9215_MAJOR, 3, 2,
YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ YT921X_PORT_MASK_EXT1,
+ YT921X_PORT_MASK_EXT0,
},
{
"YT9213NB", YT9215_MAJOR, 3, 3,
YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
YT921X_PORT_MASK_EXT1,
+ YT921X_PORT_MASK_EXT1,
},
{
"YT9218N", YT9218_MAJOR, 0, 0,
YT921X_PORT_MASK_INT0_n(8),
0,
+ 0,
},
{
"YT9218MB", YT9218_MAJOR, 1, 0,
YT921X_PORT_MASK_INT0_n(8),
YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
},
{}
};
@@ -4026,15 +4021,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
@@ -4047,9 +4037,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..bc2641ded101 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -960,6 +960,24 @@ 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;
+};
+
+static inline u16 yt921x_info_ports_mask(const struct yt921x_info *info)
+{
+ return info->internal_mask | info->xmii_mask | info->serdes_mask;
+}
+
struct yt921x_priv {
struct dsa_switch ds;
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks
2026-09-09 19:05 ` [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks David Yang
@ 2026-09-10 20:59 ` Andrew Lunn
2026-09-10 21:37 ` David Yang
0 siblings, 1 reply; 12+ messages in thread
From: Andrew Lunn @ 2026-09-10 20:59 UTC (permalink / raw)
To: David Yang
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Russell King, linux-kernel
> #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)
I'm i right in says YT921X_PORT_MASK_EXT0 refers to port 8, because of
BIT(8)? And YT921X_PORT_MASK_EXT1 BIT(9) means port 9?
Andrew
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks
2026-09-10 20:59 ` Andrew Lunn
@ 2026-09-10 21:37 ` David Yang
2026-09-11 11:46 ` Andrew Lunn
0 siblings, 1 reply; 12+ messages in thread
From: David Yang @ 2026-09-10 21:37 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Russell King, linux-kernel
On Fri, Sep 11, 2026 at 4:59 AM Andrew Lunn <andrew@lunn.ch> wrote:
>
> > #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)
>
> I'm i right in says YT921X_PORT_MASK_EXT0 refers to port 8, because of
> BIT(8)? And YT921X_PORT_MASK_EXT1 BIT(9) means port 9?
>
> Andrew
Yes.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks
2026-09-10 21:37 ` David Yang
@ 2026-09-11 11:46 ` Andrew Lunn
2026-09-11 12:32 ` David Yang
0 siblings, 1 reply; 12+ messages in thread
From: Andrew Lunn @ 2026-09-11 11:46 UTC (permalink / raw)
To: David Yang
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Russell King, linux-kernel
On Fri, Sep 11, 2026 at 05:37:45AM +0800, David Yang wrote:
> On Fri, Sep 11, 2026 at 4:59 AM Andrew Lunn <andrew@lunn.ch> wrote:
> >
> > > #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)
> >
> > I'm i right in says YT921X_PORT_MASK_EXT0 refers to port 8, because of
> > BIT(8)? And YT921X_PORT_MASK_EXT1 BIT(9) means port 9?
> >
> > Andrew
>
> Yes.
Can we give these better names then, YT921X_PORT_MASK_EXT8, or
YT921X_PORT_8_MASK_EXT.
Andrew
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks
2026-09-11 11:46 ` Andrew Lunn
@ 2026-09-11 12:32 ` David Yang
0 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-11 12:32 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Russell King, linux-kernel
On Fri, Sep 11, 2026 at 7:46 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> On Fri, Sep 11, 2026 at 05:37:45AM +0800, David Yang wrote:
> > On Fri, Sep 11, 2026 at 4:59 AM Andrew Lunn <andrew@lunn.ch> wrote:
> > >
> > > > #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)
> > >
> > > I'm i right in says YT921X_PORT_MASK_EXT0 refers to port 8, because of
> > > BIT(8)? And YT921X_PORT_MASK_EXT1 BIT(9) means port 9?
> > >
> > > Andrew
> >
> > Yes.
>
> Can we give these better names then, YT921X_PORT_MASK_EXT8, or
> YT921X_PORT_8_MASK_EXT.
>
> Andrew
These indexes are used in the vendor reference.
Or maybe just BIT(8)? Since they are not used anywhere else and kind
of leftovers.
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next 2/7] net: dsa: motorcomm: Check port type with runtime info
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
2026-09-09 19:05 ` [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks David Yang
@ 2026-09-09 19:05 ` David Yang
2026-09-09 19:05 ` [PATCH net-next 3/7] net: dsa: motorcomm: Fix port control/status register bit field names David Yang
` (4 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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. Drop them and identify the port type using chip info
and runtime configuration.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 36 ++++++++++++++++++++++----------
drivers/net/dsa/motorcomm/chip.h | 11 +++++-----
2 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 64fdc5109cc6..946949a3aba8 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -3743,6 +3743,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;
@@ -3751,12 +3752,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)
@@ -3771,6 +3773,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;
@@ -3805,7 +3809,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:
@@ -3841,7 +3848,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)
@@ -3881,17 +3888,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;
}
@@ -3901,6 +3907,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)
@@ -3933,13 +3942,18 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
if (res)
return res;
+ pp->serdes = true;
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 bc2641ded101..6f0123d219e0 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] 12+ messages in thread* [PATCH net-next 3/7] net: dsa: motorcomm: Fix port control/status register bit field names
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
2026-09-09 19:05 ` [PATCH net-next 1/7] net: dsa: motorcomm: Split xMII and SERDES port masks David Yang
2026-09-09 19:05 ` [PATCH net-next 2/7] net: dsa: motorcomm: Check port type with runtime info David Yang
@ 2026-09-09 19:05 ` David Yang
2026-09-09 19:05 ` [PATCH net-next 4/7] net: dsa: motorcomm: Introduce yt921x_speed David Yang
` (3 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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 behavior
changed.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 3 ++-
drivers/net/dsa/motorcomm/chip.h | 7 ++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 946949a3aba8..53d0a85e9310 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -3747,7 +3747,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 6f0123d219e0..e87daf8c650b 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -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] 12+ messages in thread* [PATCH net-next 4/7] net: dsa: motorcomm: Introduce yt921x_speed
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
` (2 preceding siblings ...)
2026-09-09 19:05 ` [PATCH net-next 3/7] net: dsa: motorcomm: Fix port control/status register bit field names David Yang
@ 2026-09-09 19:05 ` David Yang
2026-09-09 19:05 ` [PATCH net-next 5/7] net: dsa: motorcomm: Hoist port_to_priv helper into chip.h David Yang
` (2 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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 | 61 +++++++++++++++++++++++--------
2 files changed, 52 insertions(+), 71 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 53d0a85e9310..0635359787db 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -3776,29 +3776,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_NUM)
return -EINVAL;
- }
+
+ ctrl = YT921X_PORT_SPEED(ps);
if (duplex == DUPLEX_FULL)
ctrl |= YT921X_PORT_DUPLEX_FULL;
if (tx_pause)
@@ -3815,25 +3801,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;
@@ -3855,25 +3823,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 e87daf8c650b..dd121ef2cbf5 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_NUM
+};
+
#define YT921X_SENSOR 0x8036c
#define YT921X_SENSOR_TEMP BIT(18)
#define YT921X_TEMP 0x80374
@@ -1009,4 +1004,40 @@ 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_NUM;
+ }
+}
+
+static inline int yt921x_speed_to_ethtool(int speed)
+{
+ switch (speed) {
+ case YT921X_SPEED_10:
+ return SPEED_10;
+ case YT921X_SPEED_100:
+ return SPEED_100;
+ case YT921X_SPEED_1000:
+ return SPEED_1000;
+ case YT921X_SPEED_2500:
+ return SPEED_2500;
+ case YT921X_SPEED_10000:
+ return SPEED_10000;
+ default:
+ return SPEED_UNKNOWN;
+ }
+}
+
#endif
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH net-next 5/7] net: dsa: motorcomm: Hoist port_to_priv helper into chip.h
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
` (3 preceding siblings ...)
2026-09-09 19:05 ` [PATCH net-next 4/7] net: dsa: motorcomm: Introduce yt921x_speed David Yang
@ 2026-09-09 19:05 ` David Yang
2026-09-09 19:05 ` [PATCH net-next 6/7] net: dsa: motorcomm: Split MDIO bus module David Yang
2026-09-09 19:05 ` [PATCH net-next 7/7] net: dsa: motorcomm: Add SerDes PCS David Yang
6 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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>
---
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 0635359787db..beeb08f2b000 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -603,9 +603,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 dd121ef2cbf5..b9a9e9d22d4b 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -1004,6 +1004,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 87a6b9cf6068..dd5b3684250d 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] 12+ messages in thread* [PATCH net-next 6/7] net: dsa: motorcomm: Split MDIO bus module
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
` (4 preceding siblings ...)
2026-09-09 19:05 ` [PATCH net-next 5/7] net: dsa: motorcomm: Hoist port_to_priv helper into chip.h David Yang
@ 2026-09-09 19:05 ` David Yang
2026-09-09 19:05 ` [PATCH net-next 7/7] net: dsa: motorcomm: Add SerDes PCS David Yang
6 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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>
---
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 | 37 ++++
5 files changed, 317 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 beeb08f2b000..85a31389050b 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 {
@@ -271,274 +272,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 b9a9e9d22d4b..21132a147d6b 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..261a71c637f5
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/mdio_bus.h
@@ -0,0 +1,37 @@
+/* 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/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] 12+ messages in thread* [PATCH net-next 7/7] net: dsa: motorcomm: Add SerDes PCS
2026-09-09 19:05 [PATCH net-next 0/7] net: dsa: motorcomm: Add SerDes PCS David Yang
` (5 preceding siblings ...)
2026-09-09 19:05 ` [PATCH net-next 6/7] net: dsa: motorcomm: Split MDIO bus module David Yang
@ 2026-09-09 19:05 ` David Yang
6 siblings, 0 replies; 12+ messages in thread
From: David Yang @ 2026-09-09 19:05 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>
---
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 78 ++++++----
drivers/net/dsa/motorcomm/chip.h | 8 +-
drivers/net/dsa/motorcomm/mdio_bus.c | 28 +++-
drivers/net/dsa/motorcomm/mdio_bus.h | 14 ++
drivers/net/dsa/motorcomm/pcs-921x.c | 209 +++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/pcs.h | 13 ++
7 files changed, 323 insertions(+), 28 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 85a31389050b..4780bb190ede 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 {
@@ -3516,6 +3517,10 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
if (ps == YT921X_SPEED_NUM)
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;
@@ -3524,7 +3529,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;
@@ -3544,7 +3551,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)
@@ -3575,7 +3583,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,
if (res)
return res;
- 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;
-
pp->serdes = true;
break;
/* add XMII support here */
@@ -3639,6 +3624,24 @@ 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);
+
+ 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[dp->index].pcs;
+ default:
+ return NULL;
+ }
+}
+
static void
yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
@@ -4226,6 +4229,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;
}
@@ -4235,6 +4243,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);
@@ -4268,6 +4278,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);
@@ -4285,6 +4312,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 21132a147d6b..42905e35928b 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)
@@ -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..5a47ed7bff99 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;
@@ -86,6 +85,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 261a71c637f5..d859226fede8 100644
--- a/drivers/net/dsa/motorcomm/mdio_bus.h
+++ b/drivers/net/dsa/motorcomm/mdio_bus.h
@@ -31,6 +31,20 @@
struct yt921x_priv;
+int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp);
+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..eef6e79c099b
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/pcs-921x.c
@@ -0,0 +1,209 @@
+// 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 inband = neg_mode & PHYLINK_PCS_NEG_INBAND;
+ struct yt921x_port *pp = &priv->ports[port];
+ bool changed = false;
+ u16 bmcr;
+ u32 mask;
+ u32 ctrl;
+ 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)
+ 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;
+ }
+
+ bmcr = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? BMCR_ANENABLE : 0;
+ res = yt921x_intif_modify_changed(priv, port, MII_BMCR,
+ BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
+ if (res < 0)
+ return res;
+
+ /* 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 (res)
+ changed = false;
+
+ pp->inband = inband;
+ return changed;
+}
+
+static unsigned int
+yt921x_phylink_pcs_inband_caps(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ 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 out;
+
+ res = yt921x_intif_read(priv, port, MII_LPA, &lpa);
+
+out:
+ mutex_unlock(&priv->reg_lock);
+
+ if (res) {
+ dev_err(dev, "Failed to %s PCS port %d: %i\n", "get state of",
+ port, res);
+ state->link = false;
+ 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;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_modify(priv, port, MII_BMCR, BMCR_ANRESTART,
+ BMCR_ANRESTART);
+ 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;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_modify(priv, port, MII_BMCR,
+ BMCR_PDOWN | BMCR_ANRESTART, BMCR_ANRESTART);
+ 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);
+ 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] 12+ messages in thread