* [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver
@ 2026-10-20 11:11 Kyle Switch
2026-10-20 11:11 ` [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid Kyle Switch
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Kyle Switch @ 2026-10-20 11:11 UTC (permalink / raw)
To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms,
linux, netdev, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang
This patchset mainly adds support for the yt922x series to the dsa driver.
Currently, only the yt9224 in the yt922x series is supported, and the
corresponding tag handling for yt922x is added.
At the same time, it optimizes the max ports boundary check in the
existing code, and initializes the dsa switch based on the chipid.
changes in v16:
1) Add missing break in switch case to prevent fall-through in
yt922x_port_config()
changes in v15:
1) Revert the use of GENMASK_U16() to replace GENMASK(), so as to
facilitate adaptation to scenarios with more ports in the future.
2) Group functions with the same functionality together in mdio_bus.c.
3) Remove meaningless code comments.
4) Renaming parameters in yt922x_pcs_get_state().
5) Add neg_mode and advertising handle.
changes in v14:
1) Replace the goto-based skip_extmode_check path with an early
conditional in yt921x_chip_detect().
changes in v13:
1) Fix a compilation error in patch1 only.
The change is limited to the yt92xx_register_switch function:
- Replace yt921x_priv with dsa_to_yt921x_priv
- Replace to_device with yt921x_priv_to_device
changes in v12:
1) Rebase the code.
2) Delete origin patch4 and patch5 about MIB init and remove operation.
3) Optimize error message logging.
4) Add documents about yt922x tag.
changes in v11:
1) Initialize ctrl variable inside yt922x_port_up().
2) Update maitainer for new file.
changes in v10:
1) Rebase the code.
changes in v9:
patch 1: 1) Add a dev_err() when chipid mismatch.
patch 3: 1) Using GENMASK_U* to replace GENMASK
changes in v8:
patch 1: 1) Add a lock operation to acquire the chip ID during the probe()
stage.
2) Remove meaningless changes.
patch 3: Modify the changelog description.
patch 6: 1) Remove sds_init() in this patch.
2) Code refactoring, including yt922x_sds_phyaddr_get() and
yt921x_needs_extmode_check()
3) In yt921x_intif_ext_write() and yt921x_intif_ext_read(),
add a boundary check for phyaddr. The reason is that the
phyaddr of the top ext register is 9, so max_ports should
be used for the check to include it.
4) In the yt922x driver, added initialization of yt921x_port
for exporting priv.
changes in v7:
patch 1: Fix the series_lookup() redundancy issue.
Fix the dsa->priv initialization and the code style issue.
patch 6: Move psc definition to yt921x_port
Fix psc_get_state() and an_restart().
Use the existing yt921x driver interface for the duplicated
logic for chip_reset().
Move the yt922x_cpu_port_set() to chip_setup_dsa() to align
with yt921x function.
Kyle Switch (5):
net: dsa: motorcomm: initialize dsa_switch based on chipid
net: dsa: motorcomm: use max_ports in series for port bounds checking
net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers
net: dsa: tag_yt922x: add support for Motorcomm YT922x tags
net: dsa: motorcomm: Add support for Motorcomm YT922x
MAINTAINERS | 2 +
drivers/net/dsa/motorcomm/Kconfig | 7 +-
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 575 ++++++++++++++++++++++++++-
drivers/net/dsa/motorcomm/chip.h | 101 +++++
drivers/net/dsa/motorcomm/mdio_bus.c | 48 ++-
drivers/net/dsa/motorcomm/mdio_bus.h | 4 +
drivers/net/dsa/motorcomm/pcs-922x.c | 282 +++++++++++++
drivers/net/dsa/motorcomm/pcs.h | 1 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 6 +
net/dsa/Makefile | 1 +
net/dsa/tag_yt922x.c | 122 ++++++
13 files changed, 1135 insertions(+), 17 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/pcs-922x.c
create mode 100644 net/dsa/tag_yt922x.c
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch @ 2026-10-20 11:11 ` Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-20 11:11 ` [PATCH net-next v16 2/5] net: dsa: motorcomm: use max_ports in series for port bounds checking Kyle Switch ` (4 subsequent siblings) 5 siblings, 1 reply; 11+ messages in thread From: Kyle Switch @ 2026-10-20 11:11 UTC (permalink / raw) To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel Cc: ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Replace the hardcoded chip handling in yt92xx_register_switch() with a data-driven loopup based on the chip ID. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com> --- drivers/net/dsa/motorcomm/chip.c | 73 ++++++++++++++++++++++++++++---- drivers/net/dsa/motorcomm/chip.h | 19 +++++++++ 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c index e2b5c89f7e77..ccb84f889dd2 100644 --- a/drivers/net/dsa/motorcomm/chip.c +++ b/drivers/net/dsa/motorcomm/chip.c @@ -2580,6 +2580,67 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = { .setup = yt921x_dsa_setup, }; +static const struct yt92xx_series yt92xx_series_table[] = { + [YT92XX_MODE_YT921X] = { + .name = "YT921x", + .max_ports = YT921X_PORT_NUM, + .num_lag_ids = YT921X_LAG_NUM, + .ageing_time_min = 1 * 5000, + .ageing_time_max = U16_MAX * 5000, + .dscp_prio_mapping_is_global = true, + .assisted_learning_on_cpu_port = true, + .switch_ops = &yt921x_dsa_switch_ops, + .mac_ops = &yt921x_phylink_mac_ops + }, +}; + +static const struct yt92xx_series *yt92xx_series_lookup(u32 major) +{ + if (major == YT9215_MAJOR || major == YT9218_MAJOR) + return &yt92xx_series_table[YT92XX_MODE_YT921X]; + else + return NULL; +} + +static int yt92xx_register_switch(struct dsa_switch *ds) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + const struct yt92xx_series *series; + u32 chipid; + u32 major; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); + mutex_unlock(&priv->reg_lock); + if (res) { + dev_err(dev, "Unexpected chipid 0x%x\n", chipid); + return res; + } + + major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); + series = yt92xx_series_lookup(major); + if (!series) { + dev_err(dev, "Undefined series info for the corresponding major\n"); + return -ENODEV; + } + priv->series = series; + + ds->assisted_learning_on_cpu_port = + priv->series->assisted_learning_on_cpu_port; + ds->dscp_prio_mapping_is_global = + priv->series->dscp_prio_mapping_is_global; + ds->ageing_time_min = priv->series->ageing_time_min; + ds->ageing_time_max = priv->series->ageing_time_max; + ds->num_lag_ids = priv->series->num_lag_ids; + ds->num_ports = priv->series->max_ports; + ds->ops = priv->series->switch_ops; + ds->phylink_mac_ops = priv->series->mac_ops; + + return 0; +} + static void yt921x_mdio_shutdown(struct mdio_device *mdiodev) { struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); @@ -2622,6 +2683,7 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev) struct yt921x_reg_mdio *mdio; struct yt921x_priv *priv; struct dsa_switch *ds; + int res; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -2648,15 +2710,10 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev) ds = &priv->ds; ds->dev = dev; - ds->assisted_learning_on_cpu_port = true; - ds->dscp_prio_mapping_is_global = true; ds->priv = priv; - ds->ops = &yt921x_dsa_switch_ops; - ds->ageing_time_min = 1 * 5000; - ds->ageing_time_max = U16_MAX * 5000; - ds->phylink_mac_ops = &yt921x_phylink_mac_ops; - ds->num_lag_ids = YT921X_LAG_NUM; - ds->num_ports = YT921X_PORT_NUM; + res = yt92xx_register_switch(ds); + if (res) + return res; mdiodev_set_drvdata(mdiodev, priv); diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h index 7047ad98f431..8e0b00281c56 100644 --- a/drivers/net/dsa/motorcomm/chip.h +++ b/drivers/net/dsa/motorcomm/chip.h @@ -503,9 +503,28 @@ static inline u16 yt921x_info_ports_mask(const struct yt921x_info *info) return info->internal_mask | info->xmii_mask | info->serdes_mask; } +enum yt92xx_mode { + YT92XX_MODE_YT921X, + YT92XX_MODE_YT922X, + YT92XX_MODE_MAX, +}; + +struct yt92xx_series { + const char *name; + unsigned int max_ports; + unsigned int num_lag_ids; + unsigned int ageing_time_min; + unsigned int ageing_time_max; + u32 dscp_prio_mapping_is_global; + u32 assisted_learning_on_cpu_port; + const struct dsa_switch_ops *switch_ops; + const struct phylink_mac_ops *mac_ops; +}; + struct yt921x_priv { struct dsa_switch ds; + const struct yt92xx_series *series; const struct yt921x_info *info; unsigned int meter_slot_ns; unsigned int port_shape_slot_ns; -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid 2026-10-20 11:11 ` [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid Kyle Switch @ 2026-10-22 2:11 ` netdev-bot+sashiko 0 siblings, 0 replies; 11+ messages in thread From: netdev-bot+sashiko @ 2026-10-22 2:11 UTC (permalink / raw) To: kyle.switch Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you respin. Low priority / nit picks for your consideration: - [Low] When yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid) fails, yt92xx_register_switch() (drivers/net/dsa/motorcomm/chip.c) prints… -- net: dsa: motorcomm: initialize dsa_switch based on chipid This patch replaces the hardcoded dsa_switch setup in yt921x_mdio_probe() with a new helper, yt92xx_register_switch(). The helper reads the chip ID, looks up a per-series descriptor table and fills in the dsa_switch fields from that descriptor. > diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c > index d4fc20c52a58d..4add61b89310a 100644 > --- a/drivers/net/dsa/motorcomm/chip.c > +++ b/drivers/net/dsa/motorcomm/chip.c [ ... ] > +static int yt92xx_register_switch(struct dsa_switch *ds) > +{ > + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); > + struct device *dev = yt921x_priv_to_device(priv); > + const struct yt92xx_series *series; > + u32 chipid; > + u32 major; > + int res; > + > + mutex_lock(&priv->reg_lock); > + res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); > + mutex_unlock(&priv->reg_lock); > + if (res) { > + dev_err(dev, "Unexpected chipid 0x%x\n", chipid); ^^^^^^ [Severity: Low] Can this print an uninitialized value? chipid is a local u32 with no initializer. This dev_err() only runs when yt921x_reg_read() has failed. With the MDIO backend, the output pointer is written only on success. Every error path jumps over the store: drivers/net/dsa/motorcomm/chip.c:yt921x_reg_mdio_read() { ... res = __mdiobus_read(bus, addr, reg_data); if (res < 0) goto end; val = (val << 16) | (u16)res; *valp = val; res = 0; end: mutex_unlock(&bus->mdio_lock); return res; } So if an MDIO error happens during yt921x_mdio_probe(), the log line will show leftover stack contents. The wording also suggests a bad ID was read, when in fact the read failed. The same string in yt921x_chip_detect() is printed only after a successful read, when the major is not recognized. Could the read-failure path just return res, or print a message that does not use chipid? It looks like the final patch of this series, "net: dsa: motorcomm: Add support for Motorcomm YT922x", rewrites this branch as a plain "if (res) return res;". After that change, chipid is printed only on the unknown-major path. The intermediate patches in the series still contain this code. > + return res; > + } [ ... ] -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020111115.3072180-1-kyle.switch%40motor-comm.com ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net-next v16 2/5] net: dsa: motorcomm: use max_ports in series for port bounds checking 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch 2026-10-20 11:11 ` [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid Kyle Switch @ 2026-10-20 11:11 ` Kyle Switch 2026-10-20 11:11 ` [PATCH net-next v16 3/5] net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers Kyle Switch ` (3 subsequent siblings) 5 siblings, 0 replies; 11+ messages in thread From: Kyle Switch @ 2026-10-20 11:11 UTC (permalink / raw) To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel Cc: ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Replace the hardcoded YT921X_PORT_NUM macro with the per-series max_ports field in port validation. This removes family-specific constants from the common code path and simplifies adding new switch families with different port counts. No functional change for existing YT921X devices. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com> --- drivers/net/dsa/motorcomm/mdio_bus.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c index dd6c52f37bbf..83f0212551ff 100644 --- a/drivers/net/dsa/motorcomm/mdio_bus.c +++ b/drivers/net/dsa/motorcomm/mdio_bus.c @@ -113,7 +113,7 @@ static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) u16 val; int res; - if (port >= YT921X_PORT_NUM) + if (port >= priv->series->max_ports) return U16_MAX; mutex_lock(&priv->reg_lock); @@ -131,7 +131,7 @@ 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) + if (port >= priv->series->max_ports) return -ENODEV; mutex_lock(&priv->reg_lock); @@ -145,6 +145,7 @@ int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) { struct device *dev = yt921x_priv_to_device(priv); struct mii_bus *mbus; + u32 max_ports; int res; mbus = devm_mdiobus_alloc(dev); @@ -157,7 +158,8 @@ int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) 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); + max_ports = priv->series->max_ports; + mbus->phy_mask = (u32)~GENMASK(max_ports - 1, 0); res = devm_of_mdiobus_register(dev, mbus, mnp); if (res) -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net-next v16 3/5] net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch 2026-10-20 11:11 ` [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid Kyle Switch 2026-10-20 11:11 ` [PATCH net-next v16 2/5] net: dsa: motorcomm: use max_ports in series for port bounds checking Kyle Switch @ 2026-10-20 11:11 ` Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-20 11:11 ` [PATCH net-next v16 4/5] net: dsa: tag_yt922x: add support for Motorcomm YT922x tags Kyle Switch ` (2 subsequent siblings) 5 siblings, 1 reply; 11+ messages in thread From: Kyle Switch @ 2026-10-20 11:11 UTC (permalink / raw) To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel Cc: ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Some masks assigned to u16 and u32 fields are defined with GENMASK(),which is an unsigned long constant and wider than the field type on 64-bit builds. Convert the YT921X_MBUS_CTRL_* masks used by mdio_bus.c to fixed-width helpers. This is a type cleanup only, with no functional change. Other GENMASK() uses in the driver are intentionally left untouched. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com> --- drivers/net/dsa/motorcomm/chip.c | 2 +- drivers/net/dsa/motorcomm/mdio_bus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c index ccb84f889dd2..bbb598e19a8d 100644 --- a/drivers/net/dsa/motorcomm/chip.c +++ b/drivers/net/dsa/motorcomm/chip.c @@ -2285,7 +2285,7 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv) if (res) return res; - ctrl = GENMASK(10, 0); + ctrl = GENMASK_U32(10, 0); res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); if (res) return res; diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c index 83f0212551ff..3aa1111efaa4 100644 --- a/drivers/net/dsa/motorcomm/mdio_bus.c +++ b/drivers/net/dsa/motorcomm/mdio_bus.c @@ -159,7 +159,7 @@ int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) mbus->write = yt921x_mbus_int_write; mbus->parent = dev; max_ports = priv->series->max_ports; - mbus->phy_mask = (u32)~GENMASK(max_ports - 1, 0); + mbus->phy_mask = ~GENMASK_U32(max_ports - 1, 0); res = devm_of_mdiobus_register(dev, mbus, mnp); if (res) -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v16 3/5] net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers 2026-10-20 11:11 ` [PATCH net-next v16 3/5] net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers Kyle Switch @ 2026-10-22 2:11 ` netdev-bot+sashiko 0 siblings, 0 replies; 11+ messages in thread From: netdev-bot+sashiko @ 2026-10-22 2:11 UTC (permalink / raw) To: kyle.switch Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider. Critical: 0 · High: 0 · Medium: 1 · Low: 0 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Medium] The commit message for "net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers" does not match the diff, in three ways. -- net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers This commit changes two GENMASK() uses in the YT921x DSA driver to GENMASK_U32(): the FILTER_UNK value in yt921x_chip_setup_dsa(), and the mii_bus phy_mask in yt921x_mbus_int_init(). The commit message describes it as a type-only cleanup with no functional change. > Some masks assigned to u16 and u32 fields are defined with > GENMASK(),which is an unsigned long constant and wider than > the field type on 64-bit builds. Convert the YT921X_MBUS_CTRL_* > masks used by mdio_bus.c to fixed-width helpers. > > This is a type cleanup only, with no functional change. Other > GENMASK() uses in the driver are intentionally left untouched. [Severity: Medium] Does this description match the diff? The patch doesn't modify drivers/net/dsa/motorcomm/mdio_bus.h, and the YT921X_MBUS_CTRL_* masks there still use plain GENMASK(): #define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21) #define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16) #define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */ #define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */ They are still combined into u32 locals in yt921x_intif_read(), yt921x_intif_write(), yt921x_extif_read() and yt921x_extif_write(). For example: mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | YT921X_MBUS_CTRL_OP_M; The only change in mdio_bus.c is the phy_mask expression in yt921x_mbus_int_init(), and that is not a YT921X_MBUS_CTRL_* mask: mbus->phy_mask = ~GENMASK_U32(max_ports - 1, 0); Later patches in the series don't seem to convert these masks either. The message also says other GENMASK() uses are left untouched, but the patch changes the open-coded value in yt921x_chip_setup_dsa() in chip.c: ctrl = GENMASK_U32(10, 0); res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); The message doesn't mention this change. The named mask for the same field, YT921X_FILTER_PORTS_M in chip.h, is still GENMASK(10, 0). Both changed sites are u32, so the reference to "u16 and u32 fields" doesn't match the diff either. The message may describe a different version of this patch. Could it be rewritten to describe the two conversions that are actually made? The other option would be to convert the mdio_bus.h masks as described, and then drop or explain the chip.c hunk. -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020111115.3072180-1-kyle.switch%40motor-comm.com ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net-next v16 4/5] net: dsa: tag_yt922x: add support for Motorcomm YT922x tags 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch ` (2 preceding siblings ...) 2026-10-20 11:11 ` [PATCH net-next v16 3/5] net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers Kyle Switch @ 2026-10-20 11:11 ` Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-20 11:11 ` [PATCH net-next v16 5/5] net: dsa: motorcomm: Add support for Motorcomm YT922x Kyle Switch 2026-10-21 1:30 ` [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Jakub Kicinski 5 siblings, 1 reply; 11+ messages in thread From: Kyle Switch @ 2026-10-20 11:11 UTC (permalink / raw) To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel Cc: ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Add DSA tag support for Motorcomm YT922x switches. YT922x uses the same ETH_P_YT921X EtherType and the same 8-byte tag length as YT921x, but the tag layout differs, so a separate tag driver is needed instead of extending tag_yt921x.c. The new driver documents both the to-CPU and from-CPU tag layouts. Update the co-maintainer for the Motorcomm DSA driver. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com> --- MAINTAINERS | 2 + include/net/dsa.h | 2 + net/dsa/Kconfig | 6 +++ net/dsa/Makefile | 1 + net/dsa/tag_yt922x.c | 122 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 net/dsa/tag_yt922x.c diff --git a/MAINTAINERS b/MAINTAINERS index 23e9194483d9..f10a2e4edae7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18431,11 +18431,13 @@ F: drivers/net/phy/motorcomm.c MOTORCOMM YT921X ETHERNET SWITCH DRIVER M: David Yang <mmyangfl@gmail.com> +M: Kyle Switch <kyle.switch@motor-comm.com> L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml F: drivers/net/dsa/motorcomm/ F: net/dsa/tag_yt921x.c +F: net/dsa/tag_yt922x.c MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD M: Jiri Slaby <jirislaby@kernel.org> diff --git a/include/net/dsa.h b/include/net/dsa.h index 5d12191b6f6f..5a173fe2bb87 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -62,6 +62,7 @@ struct tc_action; #define DSA_TAG_PROTO_KSZ8463_VALUE 34 #define DSA_TAG_PROTO_MT7628_VALUE 35 #define DSA_TAG_PROTO_KS8995_VALUE 36 +#define DSA_TAG_PROTO_YT922X_VALUE 37 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -101,6 +102,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE, DSA_TAG_PROTO_MT7628 = DSA_TAG_PROTO_MT7628_VALUE, DSA_TAG_PROTO_KS8995 = DSA_TAG_PROTO_KS8995_VALUE, + DSA_TAG_PROTO_YT922X = DSA_TAG_PROTO_YT922X_VALUE, }; struct dsa_switch; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 4f44bf3ede23..06d8d6526054 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -233,4 +233,10 @@ config NET_DSA_TAG_YT921X Say Y or M if you want to enable support for tagging frames for Motorcomm YT921x switches. +config NET_DSA_TAG_YT922X + tristate "Tag driver for Motorcomm YT922x switches" + help + Say Y or M if you want to enable support for tagging frames for + Motorcomm YT922x switches. + endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 1f9cc30e9988..0ef6dfce3b92 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o obj-$(CONFIG_NET_DSA_TAG_YT921X) += tag_yt921x.o +obj-$(CONFIG_NET_DSA_TAG_YT922X) += tag_yt922x.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/net/dsa/tag_yt922x.c b/net/dsa/tag_yt922x.c new file mode 100644 index 000000000000..b6146a9af98b --- /dev/null +++ b/net/dsa/tag_yt922x.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Motorcomm YT922x Switch Extended CPU Port Tagging + * + * Copyright (c) 2026 Kyle switch <kyle.switch@motor-comm.com> + * + */ + +#include <linux/etherdevice.h> + +#include "tag.h" + +#define YT922X_TAG_LEN 8 + +/* + * To define the from cpu tag format 8 bytes. + * + * To-CPU (RX) tag format, 8 bytes: + * + * tag[0]: TPID (default: 0x9988) + * tag[1]: VID lookup + * tag[2]: bit 5:2 - RX source port + * + * From-CPU (TX) tag format, 8 bytes: + * + * tag[0]: TPID (default: 0x9988) + * tag[1]: bit 8:0 - Dst port mask + * tag[2]: bit 15 - Dst port 0 + * + */ +#define YT922X_TAG_NAME "yt922x" +#define YT922X_TAG_PORTMASK_0 BIT(15) +#define YT922X_TAG_PORTMASK_M GENMASK(8, 0) +#define YT922X_TAG_PORTS(x) FIELD_PREP(YT922X_TAG_PORTMASK_M, (x)) +#define YT922X_TAG_FORCE_DST BIT(9) +#define YT922X_TAG_PRIO_M GENMASK(12, 10) +#define YT922X_TAG_PRIO_EN BIT(13) +#define YT922X_TAG_PRIO(x) (FIELD_PREP(YT922X_TAG_PRIO_M, (x)) | YT922X_TAG_PRIO_EN) +#define YT922X_TAG_RX_PORT_M GENMASK(5, 2) + +static struct sk_buff * +yt922x_tag_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + unsigned long ports; + __be16 *tag; + u16 ctrl; + + skb_push(skb, YT922X_TAG_LEN); + dsa_alloc_etype_header(skb, YT922X_TAG_LEN); + tag = dsa_etype_header_pos_tx(skb); + + tag[0] = htons(ETH_P_YT921X); + ports = dsa_xmit_port_mask(skb, netdev); + /*To fill in the case where the port index is not 0 */ + ctrl = YT922X_TAG_PRIO(skb->priority) | YT922X_TAG_FORCE_DST | + YT922X_TAG_PORTS(ports >> 1); + tag[1] = htons(ctrl); + if (ports & BIT(0)) { + /* To fill in the case where the port index is 0 */ + ctrl = YT922X_TAG_PORTMASK_0; + tag[2] = htons(ctrl); + } else { + tag[2] = 0; + } + tag[3] = 0; + + return skb; +} + +static struct sk_buff * +yt922x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) +{ + unsigned int port; + __be16 *tag; + u16 rx; + + if (unlikely(!pskb_may_pull(skb, YT922X_TAG_LEN))) { + kfree_skb(skb); + return NULL; + } + + tag = dsa_etype_header_pos_rx(skb); + + if (unlikely(tag[0] != htons(ETH_P_YT921X))) { + dev_warn_ratelimited(&netdev->dev, + "Unexpected EtherType 0x%04x\n", + ntohs(tag[0])); + kfree_skb(skb); + return NULL; + } + + /* Locate which port this is coming from */ + rx = ntohs(tag[2]); + port = FIELD_GET(YT922X_TAG_RX_PORT_M, rx); + skb->dev = dsa_conduit_find_user(netdev, 0, port); + if (unlikely(!skb->dev)) { + dev_warn_ratelimited(&netdev->dev, + "Couldn't decode source port %u\n", port); + kfree_skb(skb); + return NULL; + } + + /* Remove tag and update checksum */ + skb_pull_rcsum(skb, YT922X_TAG_LEN); + dsa_strip_etype_header(skb, YT922X_TAG_LEN); + + return skb; +} + +static const struct dsa_device_ops yt922x_netdev_ops = { + .name = YT922X_TAG_NAME, + .proto = DSA_TAG_PROTO_YT922X, + .xmit = yt922x_tag_xmit, + .rcv = yt922x_tag_rcv, + .needed_headroom = YT922X_TAG_LEN, +}; + +MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT922x switches"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT922X, YT922X_TAG_NAME); + +module_dsa_tag_driver(yt922x_netdev_ops); -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v16 4/5] net: dsa: tag_yt922x: add support for Motorcomm YT922x tags 2026-10-20 11:11 ` [PATCH net-next v16 4/5] net: dsa: tag_yt922x: add support for Motorcomm YT922x tags Kyle Switch @ 2026-10-22 2:11 ` netdev-bot+sashiko 0 siblings, 0 replies; 11+ messages in thread From: netdev-bot+sashiko @ 2026-10-22 2:11 UTC (permalink / raw) To: kyle.switch Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you respin. Low priority / nit picks for your consideration: - [Low] The layout comment in net/dsa/tag_yt922x.c does not match what yt922x_tag_xmit() puts on the wire, even though the commit message… - [Low] Concern: yt922x_tag_rcv() never calls dsa_default_offload_fwd_mark(), unlike yt921x_tag_rcv(), so the… -- net: dsa: tag_yt922x: add support for Motorcomm YT922x tags This commit adds a DSA tag driver for Motorcomm YT922x switches. They use the same ETH_P_YT921X EtherType and 8-byte tag length as YT921x, but a different tag layout. The commit also adds a co-maintainer and the new file to the Motorcomm MAINTAINERS entry. > diff --git a/net/dsa/tag_yt922x.c b/net/dsa/tag_yt922x.c > new file mode 100644 > index 0000000000000..b6146a9af98b3 > --- /dev/null > +++ b/net/dsa/tag_yt922x.c > @@ -0,0 +1,122 @@ [ ... ] > +#define YT922X_TAG_LEN 8 > + > +/* > + * To define the from cpu tag format 8 bytes. > + * > + * To-CPU (RX) tag format, 8 bytes: > + * > + * tag[0]: TPID (default: 0x9988) > + * tag[1]: VID lookup > + * tag[2]: bit 5:2 - RX source port > + * > + * From-CPU (TX) tag format, 8 bytes: > + * > + * tag[0]: TPID (default: 0x9988) > + * tag[1]: bit 8:0 - Dst port mask > + * tag[2]: bit 15 - Dst port 0 > + * > + */ [Severity: Low] This isn't a bug, but does this From-CPU layout match what yt922x_tag_xmit() puts on the wire? The commit message says "The new driver documents both the to-CPU and from-CPU tag layouts". >From the comment, tag[1] bit 0 would be port 0. The code shifts the mask by one, though: ctrl = YT922X_TAG_PRIO(skb->priority) | YT922X_TAG_FORCE_DST | YT922X_TAG_PORTS(ports >> 1); So tag[1] bit n is port n+1, and port 0 goes in tag[2] bit 15. On every frame, tag[1] also carries YT922X_TAG_FORCE_DST (bit 9), YT922X_TAG_PRIO_M (bits 12:10) and YT922X_TAG_PRIO_EN (bit 13), and tag[3] is written as zero. None of these are in the comment. Could the comment describe the shifted mask and these extra fields? Also, the block starts with "To define the from cpu tag format 8 bytes." but then describes the To-CPU layout first. -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020111115.3072180-1-kyle.switch%40motor-comm.com ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net-next v16 5/5] net: dsa: motorcomm: Add support for Motorcomm YT922x 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch ` (3 preceding siblings ...) 2026-10-20 11:11 ` [PATCH net-next v16 4/5] net: dsa: tag_yt922x: add support for Motorcomm YT922x tags Kyle Switch @ 2026-10-20 11:11 ` Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-21 1:30 ` [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Jakub Kicinski 5 siblings, 1 reply; 11+ messages in thread From: Kyle Switch @ 2026-10-20 11:11 UTC (permalink / raw) To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel Cc: ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Add support for Motorcomm YT922X, which is series of ethernet switches developed by Motorcomm Electronic Technology, includes YT9224 and YT9228. This patch only adds support for the YT9224 variant. YT9228 is not supported yet. This patch adds basic support for a working DSA switch, includes .port_setup, .setup, .phylink_get_caps, .get_tag_protocol. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com> --- drivers/net/dsa/motorcomm/Kconfig | 7 +- drivers/net/dsa/motorcomm/Makefile | 1 + drivers/net/dsa/motorcomm/chip.c | 508 ++++++++++++++++++++++++++- drivers/net/dsa/motorcomm/chip.h | 82 +++++ drivers/net/dsa/motorcomm/mdio_bus.c | 40 +++ drivers/net/dsa/motorcomm/mdio_bus.h | 4 + drivers/net/dsa/motorcomm/pcs-922x.c | 282 +++++++++++++++ drivers/net/dsa/motorcomm/pcs.h | 1 + 8 files changed, 916 insertions(+), 9 deletions(-) create mode 100644 drivers/net/dsa/motorcomm/pcs-922x.c diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig index 79cdd79a1fd2..648c49d6c527 100644 --- a/drivers/net/dsa/motorcomm/Kconfig +++ b/drivers/net/dsa/motorcomm/Kconfig @@ -1,11 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only config NET_DSA_YT921X - tristate "Motorcomm YT9215 ethernet switch chip support" + tristate "Motorcomm YT9215 and YT9224 ethernet switch chip support" select NET_DSA_TAG_YT921X + select NET_DSA_TAG_YT922X select NET_IEEE8021Q_HELPERS if DCB help - This enables support for the Motorcomm YT9215 ethernet switch - chip. + This enables support for the Motorcomm YT9215 and YT9224 + ethernet switch chip. config NET_DSA_YT921X_LEDS bool "LED support for Motorcomm YT9215" diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile index 9eaa59f699e8..3ca151358d19 100644 --- a/drivers/net/dsa/motorcomm/Makefile +++ b/drivers/net/dsa/motorcomm/Makefile @@ -4,6 +4,7 @@ yt921x-objs := chip.o yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o yt921x-objs += mdio_bus.o yt921x-objs += mib.o +yt921x-objs += pcs-922x.o yt921x-objs += pcs-921x.o yt921x-objs += smi.o yt921x-objs += tc.o diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c index bbb598e19a8d..74b5e341213d 100644 --- a/drivers/net/dsa/motorcomm/chip.c +++ b/drivers/net/dsa/motorcomm/chip.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Driver for Motorcomm YT921x Switch + * Driver for Motorcomm YT921x and YT922x Switch * * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII, * be sure to do your own checks before porting to another chip. @@ -76,6 +76,12 @@ static const struct yt921x_info yt921x_infos[] = { BIT(8) | BIT(9), BIT(8) | BIT(9), }, + { + "YT9224", YT9224_MAJOR, 0, 0, + GENMASK(7, 4), + 0, + BIT(8) | BIT(0), + }, {} }; @@ -2150,6 +2156,11 @@ static void yt921x_dsa_teardown(struct dsa_switch *ds) #endif } +static bool yt921x_needs_extmode_check(u32 major) +{ + return (major == YT9224_MAJOR) ? false : true; +} + static int yt921x_chip_detect(struct yt921x_priv *priv) { struct device *dev = yt921x_priv_to_device(priv); @@ -2175,6 +2186,14 @@ static int yt921x_chip_detect(struct yt921x_priv *priv) return -ENODEV; } + if (!yt921x_needs_extmode_check(major)) { + dev_info(dev, + "Motorcomm %s ethernet switch, chipid: 0x%x\n", + info->name, chipid); + priv->info = info; + return 0; + } + res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode); if (res) return res; @@ -2580,6 +2599,470 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = { .setup = yt921x_dsa_setup, }; +static int yt922x_port_down(struct yt921x_priv *priv, int port) +{ + u32 mask; + int res; + + /* mac force down */ + mask = YT922X_PORT_LINK | YT922X_PORT_RX_MAC_EN | + YT922X_PORT_TX_MAC_EN | YT922X_PORT_LINK_AN; + res = yt921x_reg_clear_bits(priv, YT922X_PORTn_CTRL(port), mask); + if (res) + return res; + /* Need force op to make soft configuration effective */ + mask = YT922X_PORT_FORCE_OP; + res = yt921x_reg_set_bits(priv, YT922X_PORTn_CTRL(port), mask); + if (res) + return res; + + /* disable en_phy */ + res = yt921x_reg_clear_bits(priv, YT922X_EN_PHY_VALUE, BIT(port)); + if (res) + return res; + res = yt921x_reg_set_bits(priv, YT922X_EN_PHY_OVERWRITE, BIT(port)); + if (res) + return res; + + return 0; +} + +static void +yt922x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + int port = dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt922x_port_down(priv, port); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down", + port, res); +} + +static int +yt922x_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) +{ + struct yt921x_port *pp = &priv->ports[port]; + int ps = ethtool_speed_to_yt921x(speed); + u32 ctrl = 0; + u32 mask; + int res; + + if (ps == YT921X_SPEED_INVALID) + return -EINVAL; + + mask = YT922X_PORT_SPEED_M | YT922X_PORT_TX_MAC_EN | + YT922X_PORT_RX_MAC_EN | YT922X_PORT_TX_PAUSE | + YT922X_PORT_RX_PAUSE | YT922X_PORT_DUPLEX_FULL | + YT922X_PORT_LINK_AN | YT922X_PORT_CFG_RX_EN | + YT922X_PORT_CFG_TX_EN | YT922X_PORT_FC_AN | + YT922X_PORT_LINK; + + ctrl |= ps; + + if (duplex == DUPLEX_FULL) + ctrl |= YT922X_PORT_DUPLEX_FULL; + if (tx_pause) + ctrl |= YT922X_PORT_TX_PAUSE; + if (rx_pause) + ctrl |= YT922X_PORT_RX_PAUSE; + ctrl |= YT922X_PORT_RX_MAC_EN | YT922X_PORT_TX_MAC_EN | + YT922X_PORT_CFG_TX_EN | YT922X_PORT_CFG_RX_EN; + + /* + * Self-negotiation: the LINK/AN bits in ctrl are set based to inband. + * Forced mode: set LINK, keep the AN bit at 0 and clear it via the mask + */ + if (pp->inband) + ctrl |= (YT922X_PORT_FC_AN | YT922X_PORT_LINK_AN); + else + ctrl |= YT922X_PORT_LINK; + + res = yt921x_reg_update_bits(priv, YT922X_PORTn_CTRL(port), mask, ctrl); + if (res) + return res; + + /* force op */ + mask = YT922X_PORT_FORCE_OP; + res = yt921x_reg_set_bits(priv, YT922X_PORTn_CTRL(port), mask); + if (res) + return res; + + /* enable en_phy */ + res = yt921x_reg_set_bits(priv, YT922X_EN_PHY_VALUE, BIT(port)); + if (res) + return res; + res = yt921x_reg_set_bits(priv, YT922X_EN_PHY_OVERWRITE, BIT(port)); + if (res) + return res; + + return 0; +} + +static void +yt922x_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + int port = dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt922x_port_up(priv, port, mode, interface, speed, duplex, + tx_pause, rx_pause); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up", + port, res); +} + +static int +yt922x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, + phy_interface_t interface) +{ + struct device *dev = yt921x_priv_to_device(priv); + const struct yt921x_info *info = priv->info; + struct yt921x_port *pp = &priv->ports[port]; + + if (BIT(port) & info->internal_mask) { + if (interface != PHY_INTERFACE_MODE_INTERNAL) + goto err; + return 0; + } + + switch (interface) { + /* SERDES */ + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_USXGMII: + if (!(BIT(port) & info->serdes_mask)) + goto err; + pp->serdes = true; + break; + default: + goto err; + } + + return 0; + +err: + dev_err(dev, "Wrong mode %d on port %d\n", interface, port); + return -EINVAL; +} + +static void +yt922x_phylink_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + int port = dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt922x_port_config(priv, port, mode, state->interface); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config", + port, res); +} + +static struct phylink_pcs * +yt922x_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 = dsa_to_yt921x_priv(dp->ds); + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_USXGMII: + return &priv->ports[dp->index].pcs; + + default: + return NULL; + } +} + +static const struct phylink_mac_ops yt922x_phylink_mac_ops = { + .mac_select_pcs = yt922x_phylink_mac_select_pcs, + .mac_link_down = yt922x_phylink_mac_link_down, + .mac_link_up = yt922x_phylink_mac_link_up, + .mac_config = yt922x_phylink_mac_config, +}; + +static enum dsa_tag_protocol +yt922x_dsa_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol m) +{ + return DSA_TAG_PROTO_YT922X; +} + +static void +yt922x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + const struct yt921x_info *info = priv->info; + + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000; + + if (info->internal_mask & BIT(port)) { + /* port 4 to port 7, internal utp */ + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + config->mac_capabilities |= MAC_2500FD; + } + if (info->serdes_mask & BIT(port)) { + /* serdes */ + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + config->mac_capabilities |= MAC_2500FD; + __set_bit(PHY_INTERFACE_MODE_USXGMII, + config->supported_interfaces); + config->mac_capabilities |= MAC_5000FD; + config->mac_capabilities |= MAC_10000FD; + } +} + +static int yt922x_port_setup(struct yt921x_priv *priv, int port) +{ + struct dsa_switch *ds = &priv->ds; + u32 mask; + u32 ctrl; + int res; + + /* enable user port isolation and disable fdb learning */ + ctrl = ~priv->cpu_ports_mask; + res = yt921x_reg_write(priv, YT922X_PORTn_ISOLATION(port), ctrl); + if (res) + return res; + + mask = YT922X_PORT_LEARN_DIS; + res = yt921x_reg_set_bits(priv, YT922X_PORTn_LEARN(port), mask); + if (res) + return res; + + if (dsa_is_cpu_port(ds, port)) { + ctrl = ~(u32)0; + res = yt921x_reg_write(priv, YT922X_PORTn_ISOLATION(port), + ctrl); + if (res) + return res; + } + + return 0; +} + +static int yt922x_dsa_port_setup(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt922x_port_setup(priv, port); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt922x_cpu_tag_mode_set_8b(struct yt921x_priv *priv) +{ + u32 val; + u32 val1; + int res; + + /* cpu tag mode set to 8b */ + res = yt921x_reg_read(priv, YT922X_CPU_TAG_RX_CTRL, &val); + if (res) + return res; + res = yt921x_reg_read(priv, YT922X_CPU_TAG_TX_CTRL, &val1); + if (res) + return res; + val &= ~YT922X_CPU_TAG_RX_MODE; + val1 &= ~YT922X_CPU_TAG_TX_MODE; + val1 &= ~YT922X_CPU_TAG_TX_TYPE; + res = yt921x_reg_write(priv, YT922X_CPU_TAG_RX_CTRL, val); + if (res) + return res; + res = yt921x_reg_write(priv, YT922X_CPU_TAG_TX_CTRL, val1); + if (res) + return res; + + return 0; +} + +static int yt922x_cpu_port_set(struct yt921x_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + u32 ctrl; + int res; + + /* cpu tag mode */ + res = yt922x_cpu_tag_mode_set_8b(priv); + if (res) + return res; + + /* Enable DSA */ + priv->cpu_ports_mask = dsa_cpu_ports(ds); + ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN | + YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask)); + res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl); + if (res) + return res; + + /* Setup software switch */ + ctrl = YT922X_CPU_COPY_TO_EXT_CPU; + res = yt921x_reg_write(priv, YT922X_CPU_COPY, ctrl); + if (res) + return res; + + return res; +} + +static int yt922x_chip_setup_dsa(struct yt921x_priv *priv) +{ + unsigned long cpu_ports_mask; + u32 ctrl; + int port; + int res; + + /* cpu port set */ + res = yt922x_cpu_port_set(priv); + if (res) + return res; + + ctrl = GENMASK_U32(8, 0); + res = yt921x_reg_write(priv, YT922X_FILTER_UNK_UCAST, ctrl); + if (res) + return res; + + ctrl = 0; + for (int i = 0; i < priv->series->max_ports; i++) + ctrl |= YT922X_ACT_UNK_ACTn_TRAP(i); + cpu_ports_mask = priv->cpu_ports_mask; + for_each_set_bit(port, &cpu_ports_mask, priv->series->max_ports) { + ctrl &= ~YT922X_ACT_UNK_ACTn_M(port); + ctrl |= YT922X_ACT_UNK_ACTn_DROP(port); + } + res = yt921x_reg_write(priv, YT922X_ACT_UNK_UCAST, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT922X_ACT_UNK_MCAST, ctrl); + if (res) + return res; + + return 0; +} + +static int yt922x_chip_setup(struct yt921x_priv *priv) +{ + u32 ctrl; + int res; + + ctrl = YT922X_FUNC_MIB | YT922X_FUNC_ACL; + res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl); + if (res) + return res; + + res = yt922x_chip_setup_dsa(priv); + if (res) + return res; + + return 0; +} + +static void yt922x_pcs_setup(struct dsa_switch *ds) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + const struct yt921x_info *info = priv->info; + unsigned long mask; + int port; + + mask = info->serdes_mask; + for_each_set_bit(port, &mask, priv->series->max_ports) { + struct yt921x_port *pp = &priv->ports[port]; + + pp->pcs.ops = &yt922x_phylink_pcs_ops; + pp->pcs.poll = true; + + __set_bit(PHY_INTERFACE_MODE_SGMII, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, + pp->pcs.supported_interfaces); + } +} + +static int yt922x_dsa_setup(struct dsa_switch *ds) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + struct device_node *np = dev->of_node; + struct device_node *child; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_chip_reset(priv); + mutex_unlock(&priv->reg_lock); + if (res) + return res; + + /* Register the internal mdio bus. */ + child = of_get_child_by_name(np, "mdio"); + if (child) { + res = yt921x_mbus_int_init(priv, child); + of_node_put(child); + if (res) + return res; + } + + mutex_lock(&priv->reg_lock); + res = yt922x_chip_setup(priv); + mutex_unlock(&priv->reg_lock); + if (res) + return res; + + /* switch sds pcs setup */ + yt922x_pcs_setup(ds); + + return 0; +} + +static const struct dsa_switch_ops yt922x_dsa_switch_ops = { + /* port */ + .get_tag_protocol = yt922x_dsa_get_tag_protocol, + .phylink_get_caps = yt922x_dsa_phylink_get_caps, + .port_setup = yt922x_dsa_port_setup, + /* chip */ + .setup = yt922x_dsa_setup, +}; + static const struct yt92xx_series yt92xx_series_table[] = { [YT92XX_MODE_YT921X] = { .name = "YT921x", @@ -2592,12 +3075,25 @@ static const struct yt92xx_series yt92xx_series_table[] = { .switch_ops = &yt921x_dsa_switch_ops, .mac_ops = &yt921x_phylink_mac_ops }, + [YT92XX_MODE_YT922X] = { + .name = "YT922x", + .max_ports = YT922X_PORT_NUM, + .num_lag_ids = YT922X_LAG_NUM, + .ageing_time_min = 1 * 6000, + .ageing_time_max = U16_MAX * 6000, + .dscp_prio_mapping_is_global = true, + .assisted_learning_on_cpu_port = true, + .switch_ops = &yt922x_dsa_switch_ops, + .mac_ops = &yt922x_phylink_mac_ops, + }, }; static const struct yt92xx_series *yt92xx_series_lookup(u32 major) { if (major == YT9215_MAJOR || major == YT9218_MAJOR) return &yt92xx_series_table[YT92XX_MODE_YT921X]; + else if (major == YT9224_MAJOR) + return &yt92xx_series_table[YT92XX_MODE_YT922X]; else return NULL; } @@ -2614,15 +3110,13 @@ static int yt92xx_register_switch(struct dsa_switch *ds) mutex_lock(&priv->reg_lock); res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); mutex_unlock(&priv->reg_lock); - if (res) { - dev_err(dev, "Unexpected chipid 0x%x\n", chipid); + if (res) return res; - } major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); series = yt92xx_series_lookup(major); if (!series) { - dev_err(dev, "Undefined series info for the corresponding major\n"); + dev_err(dev, "Chip ID: 0x%x, and Unknown major.\n", chipid); return -ENODEV; } priv->series = series; @@ -2722,6 +3216,7 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev) static const struct of_device_id yt921x_of_match[] = { { .compatible = "motorcomm,yt9215" }, + { .compatible = "motorcomm,yt9224" }, {} }; MODULE_DEVICE_TABLE(of, yt921x_of_match); @@ -2739,5 +3234,6 @@ static struct mdio_driver yt921x_mdio_driver = { mdio_module_driver(yt921x_mdio_driver); MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>"); -MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch"); +MODULE_AUTHOR("Kyle Switch <kyle.switch@motor-comm.com>"); +MODULE_DESCRIPTION("Driver for Motorcomm YT921x and YT922x Switch"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h index 8e0b00281c56..cc35dec2e52f 100644 --- a/drivers/net/dsa/motorcomm/chip.h +++ b/drivers/net/dsa/motorcomm/chip.h @@ -119,6 +119,7 @@ enum yt921x_speed { YT921X_SPEED_1000, YT921X_SPEED_10000, YT921X_SPEED_2500, + YT921X_SPEED_5000, YT921X_SPEED_INVALID }; @@ -431,6 +432,7 @@ enum yt921x_fdb_entry_status { #define YT9215_MAJOR 0x9002 #define YT9218_MAJOR 0x9001 +#define YT9224_MAJOR 0x9004 /* required for a hard reset */ #define YT921X_RST_DELAY_US 10000 @@ -450,6 +452,84 @@ enum yt921x_fdb_entry_status { #define YT921X_NAME "yt921x" +/* yt922x register lists */ +#define YT92XX_PAGE_SELECT 0x1e +#define YT92XX_PAGE 0x1f +#define YT922X_EN_PHY_OVERWRITE (0x80040) +#define YT922X_EN_PHY_VALUE (0x8003c) +/* CTRL: force op to make soft configuration effective */ +#define YT922X_PORTn_CTRL(port) (0x80080 + 4 * (port)) +#define YT922X_PORT_FORCE_OP BIT(14) +#define YT922X_PORT_CFG_TX_EN BIT(13) +#define YT922X_PORT_CFG_RX_EN BIT(12) +#define YT922X_PORT_FC_AN BIT(11) +#define YT922X_PORT_LINK_AN BIT(10) /* CTRL: auto negotiation */ +#define YT922X_PORT_LINK BIT(9) /* CTRL: link status */ +#define YT922X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */ +#define YT922X_PORT_DUPLEX_FULL BIT(7) +#define YT922X_PORT_RX_PAUSE BIT(6) +#define YT922X_PORT_TX_PAUSE BIT(5) +#define YT922X_PORT_RX_MAC_EN BIT(4) +#define YT922X_PORT_TX_MAC_EN BIT(3) +#define YT922X_PORT_SPEED_M GENMASK(2, 0) +#define YT922X_PORT_SDSn 0x400 +#define YT922X_SERDES_MODE_M GENMASK(6, 4) +#define YT922X_SERDES_MODE(x) FIELD_PREP(YT922X_SERDES_MODE_M, (x)) +#define YT922X_SERDES_MODE_SGMII YT922X_SERDES_MODE(0) +#define YT922X_SERDES_MODE_REVSGMII YT922X_SERDES_MODE(1) +#define YT922X_SERDES_MODE_1000BASEX YT922X_SERDES_MODE(2) +#define YT922X_SERDES_MODE_100BASEX YT922X_SERDES_MODE(3) +#define YT922X_SERDES_MODE_2500BASEX YT922X_SERDES_MODE(4) +#define YT922X_SERDES_MODE_USXGMII YT922X_SERDES_MODE(6) +#define YT922X_PORT_NUM 9 +#define YT922X_PCS_LINK_CTRL 0x11 +#define YT922X_PCS_LINK_STATUS BIT(10) +#define YT922X_PCS_AN_COMPLETE BIT(11) +#define YT922X_PCS_WRITE_PROTECT 0x4be + +#define YT922X_LAG_NUM 4 + +#define YT922X_PORTn_ISOLATION(port) (0x180d80 + 0x4 * (port)) + +#define YT922X_PORTn_LEARN(port) (0x180300 + 4 * (port)) +#define YT922X_PORT_LEARN_DIS BIT(18) + +#define YT922X_FUNC_ACL BIT(5) +#define YT922X_FUNC_MIB BIT(4) + +#define YT922X_FILTER_UNK_UCAST 0x180ec8 +#define YT922X_ACT_UNK_UCAST 0x180ed8 +#define YT922X_ACT_UNK_MCAST 0x180ee0 +#define YT922X_ACT_UNK_MCAST_BYPASS_DROP_PIM BIT(22) +#define YT922X_ACT_UNK_MCAST_BYPASS_DROP_MLD BIT(21) +#define YT922X_ACT_UNK_MCAST_BYPASS_DROP_IGMP BIT(20) +#define YT922X_ACT_UNK_ACTn_M(port) GENMASK(2 * (port) + 1, 2 * (port)) +#define YT922X_ACT_UNK_ACTn(port, x) ((x) << (2 * (port))) +#define YT922X_ACT_UNK_ACTn_FORWARD(port) YT922X_ACT_UNK_ACTn(port, 0) /* flood */ +#define YT922X_ACT_UNK_ACTn_DROP(port) YT922X_ACT_UNK_ACTn(port, 1) /* discard */ +#define YT922X_ACT_UNK_ACTn_TRAP(port) YT922X_ACT_UNK_ACTn(port, 3) /* steer to CPU */ + +#define YT922X_CPU_COPY 0x181100 +#define YT922X_CPU_COPY_TO_INT_CPU BIT(1) +#define YT922X_CPU_COPY_TO_EXT_CPU BIT(0) +#define YT922X_CPU_TAG_RX_CTRL 0x80504 +#define YT922X_CPU_TAG_RX_MODE BIT(0) +#define YT922X_CPU_TAG_TX_CTRL 0x100710 +#define YT922X_CPU_TAG_TX_TYPE BIT(0) +#define YT922X_CPU_TAG_TX_MODE BIT(1) +#define YT922X_CPU_TAG_TX_CTAG_OP BIT(2) +#define YT922X_CPU_TAG_TX_STAG_OP BIT(3) +/* Define top ext addr for yt922x */ +#define YT922X_COMMON_EXT_PHYADDR 9 + +enum yt922x_phy_reg_type { + YT922X_PHY_REG_TYPE_COMMON_EXT, + YT922X_PHY_REG_TYPE_SDS_COMMON_EXT, + YT922X_PHY_REG_TYPE_MII, + YT922X_PHY_REG_TYPE_EXT, + YT922X_PHY_REG_TYPE_MAX +}; + struct yt921x_port { unsigned char index; @@ -569,6 +649,8 @@ static inline int ethtool_speed_to_yt921x(int speed) return YT921X_SPEED_2500; case SPEED_10000: return YT921X_SPEED_10000; + case SPEED_5000: + return YT921X_SPEED_5000; default: return YT921X_SPEED_INVALID; } diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c index 3aa1111efaa4..d545f338bd9d 100644 --- a/drivers/net/dsa/motorcomm/mdio_bus.c +++ b/drivers/net/dsa/motorcomm/mdio_bus.c @@ -107,6 +107,45 @@ yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg, return 1; } +/* intif extend register read/write api */ +int +yt921x_intif_ext_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + int res; + + if (port > priv->series->max_ports) + return -ENODEV; + + res = yt921x_intif_write(priv, port, YT92XX_PAGE_SELECT, reg); + if (res) + return res; + + res = yt921x_intif_write(priv, port, YT92XX_PAGE, val); + if (res) + return res; + + return 0; +} + +int +yt921x_intif_ext_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + int res; + + if (port > priv->series->max_ports) + return -ENODEV; + + res = yt921x_intif_write(priv, port, YT92XX_PAGE_SELECT, reg); + if (res) + return res; + + res = yt921x_intif_read(priv, port, YT92XX_PAGE, valp); + if (res) + return res; + + return 0; +} + static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) { struct yt921x_priv *priv = mbus->priv; @@ -299,3 +338,4 @@ int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) return 0; } + diff --git a/drivers/net/dsa/motorcomm/mdio_bus.h b/drivers/net/dsa/motorcomm/mdio_bus.h index e79b725d435b..238a35891cb1 100644 --- a/drivers/net/dsa/motorcomm/mdio_bus.h +++ b/drivers/net/dsa/motorcomm/mdio_bus.h @@ -35,6 +35,10 @@ 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_ext_write(struct yt921x_priv *priv, int port, int reg, + u16 val); +int yt921x_intif_ext_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); diff --git a/drivers/net/dsa/motorcomm/pcs-922x.c b/drivers/net/dsa/motorcomm/pcs-922x.c new file mode 100644 index 000000000000..5ad7873e0df7 --- /dev/null +++ b/drivers/net/dsa/motorcomm/pcs-922x.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 Kyle Switch + */ + +#include "chip.h" +#include "mdio_bus.h" +#include "pcs.h" +#include "smi.h" + +#define to_device(priv) ((priv)->ds.dev) + +static int yt922x_sds_phyaddr_get(int port, + enum yt922x_phy_reg_type reg_type) +{ + if (reg_type == YT922X_PHY_REG_TYPE_COMMON_EXT) + return YT922X_COMMON_EXT_PHYADDR; + + return port; +} + +static void yt922x_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); + int port = pp->index; + u16 link_ctrl; + int res; + u16 bmsr; + int addr; + u16 lp; + + addr = yt922x_sds_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII); + if (addr < 0) { + state->link = false; + return; + } + + mutex_lock(&priv->reg_lock); + switch (state->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + res = yt921x_intif_read(priv, addr, MII_BMSR, &bmsr); + if (res) + goto err; + res = yt921x_intif_read(priv, addr, MII_LPA, &lp); + if (res) + goto err; + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lp); + break; + case PHY_INTERFACE_MODE_100BASEX: + res = yt921x_intif_read(priv, addr, MII_BMSR, &bmsr); + if (res) + goto err; + state->speed = SPEED_100; + state->duplex = DUPLEX_FULL; + state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; + state->link = !!(bmsr & BMSR_LSTATUS); + break; + case PHY_INTERFACE_MODE_USXGMII: + res = yt921x_intif_read(priv, addr, YT922X_PCS_LINK_CTRL, + &link_ctrl); + if (res) + goto err; + state->link = FIELD_GET(YT922X_PCS_LINK_STATUS, link_ctrl); + state->an_complete = FIELD_GET(YT922X_PCS_AN_COMPLETE, + link_ctrl); + res = yt921x_intif_read(priv, addr, MII_LPA, &lp); + if (res) + goto err; + if (state->link) + phylink_decode_usxgmii_word(state, lp); + break; + default: + state->link = false; + break; + } + mutex_unlock(&priv->reg_lock); + return; + +err: + mutex_unlock(&priv->reg_lock); + state->link = false; +} + +static void yt922x_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 bmcr; + int addr; + int res; + + addr = yt922x_sds_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII); + if (addr < 0) { + dev_err(dev, "Invalid SDS PHY addr for port %d\n", port); + return; + } + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_read(priv, addr, MII_BMCR, &bmcr); + if (res) + goto err; + bmcr |= BMCR_ANRESTART; + bmcr |= BMCR_RESET; + res = yt921x_intif_write(priv, addr, MII_BMCR, bmcr); + if (res) + goto err; + mutex_unlock(&priv->reg_lock); + return; + +err: + mutex_unlock(&priv->reg_lock); + dev_err(dev, "Failed to an restart PCS port %d: %i\n", port, res); +} + +static int yt922x_pcs_config(struct phylink_pcs *pcs, 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 = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + bool changed = false; + int res, port; + u16 bmcr_old; + u16 bmcr_new; + u16 data; + u16 ctrl; + int addr; + int adv; + + port = pp->index; + addr = yt922x_sds_phyaddr_get + (port, YT922X_PHY_REG_TYPE_SDS_COMMON_EXT); + if (addr < 0) + return addr; + + mutex_lock(&priv->reg_lock); + + /* write protect */ + res = yt921x_intif_ext_write(priv, addr, YT922X_PCS_WRITE_PROTECT, 0xd); + if (res) + goto err; + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + ctrl = YT922X_SERDES_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_100BASEX: + ctrl = YT922X_SERDES_MODE_100BASEX; + break; + case PHY_INTERFACE_MODE_1000BASEX: + ctrl = YT922X_SERDES_MODE_1000BASEX; + break; + case PHY_INTERFACE_MODE_2500BASEX: + ctrl = YT922X_SERDES_MODE_2500BASEX; + break; + case PHY_INTERFACE_MODE_USXGMII: + ctrl = YT922X_SERDES_MODE_USXGMII; + break; + default: + res = -EINVAL; + goto err; + } + res = yt921x_intif_ext_read(priv, addr, YT922X_PORT_SDSn, &data); + if (res) + goto err; + data &= ~YT922X_SERDES_MODE_M; + data |= ctrl; + res = yt921x_intif_ext_write(priv, addr, YT922X_PORT_SDSn, data); + if (res) + goto err; + + if (inband) { + 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) + goto err; + + changed = !!res; + } + } + + res = yt921x_intif_read(priv, port, MII_BMCR, &bmcr_old); + if (res) + goto err; + + bmcr_new = bmcr_old; + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + bmcr_new |= BMCR_ANENABLE; + else + bmcr_new &= ~BMCR_ANENABLE; + + changed |= (bmcr_new != bmcr_old); + + res = yt921x_intif_write(priv, port, MII_BMCR, bmcr_new); + if (res) + goto err; + pp->inband = inband; + mutex_unlock(&priv->reg_lock); + + return changed; + +err: + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt922x_pcs_enable(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 = yt921x_priv_to_device(priv); + int port = pp->index; + int addr; + u16 bmcr; + int res; + + addr = yt922x_sds_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII); + if (addr < 0) + return addr; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, addr, MII_BMCR, &bmcr); + if (res) + goto end; + + bmcr &= ~BMCR_PDOWN; + bmcr |= BMCR_ANRESTART; + res = yt921x_intif_write(priv, addr, MII_BMCR, bmcr); + +end: + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to enable PCS port %d: %i\n", port, res); + + return res; +} + +static void yt922x_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 = yt921x_priv_to_device(priv); + int port = pp->index; + int addr; + int res; + + addr = yt922x_sds_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII); + if (addr < 0) { + dev_err(dev, "Invalid SDS PHY addr for port %d\n", port); + return; + } + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_modify(priv, addr, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to power down PCS port %d: %i\n", port, + res); +} + +const struct phylink_pcs_ops yt922x_phylink_pcs_ops = { + .pcs_get_state = yt922x_pcs_get_state, + .pcs_config = yt922x_pcs_config, + .pcs_an_restart = yt922x_pcs_an_restart, + .pcs_enable = yt922x_pcs_enable, + .pcs_disable = yt922x_pcs_disable, +}; diff --git a/drivers/net/dsa/motorcomm/pcs.h b/drivers/net/dsa/motorcomm/pcs.h index 42426558086a..c85caa7bf212 100644 --- a/drivers/net/dsa/motorcomm/pcs.h +++ b/drivers/net/dsa/motorcomm/pcs.h @@ -9,5 +9,6 @@ #include <linux/phylink.h> extern const struct phylink_pcs_ops yt921x_phylink_pcs_ops; +extern const struct phylink_pcs_ops yt922x_phylink_pcs_ops; #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v16 5/5] net: dsa: motorcomm: Add support for Motorcomm YT922x 2026-10-20 11:11 ` [PATCH net-next v16 5/5] net: dsa: motorcomm: Add support for Motorcomm YT922x Kyle Switch @ 2026-10-22 2:11 ` netdev-bot+sashiko 0 siblings, 0 replies; 11+ messages in thread From: netdev-bot+sashiko @ 2026-10-22 2:11 UTC (permalink / raw) To: kyle.switch Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms, linux, netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider. Critical: 0 · High: 0 · Medium: 4 · Low: 2 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Medium] yt921x_of_match in drivers/net/dsa/motorcomm/chip.c now accepts the new compatible "motorcomm,yt9224", but the DT binding was… - [Medium] yt922x_port_up() (drivers/net/dsa/motorcomm/chip.c) sets YT922X_PORT_FC_AN | YT922X_PORT_LINK_AN whenever pp->inband is true. - [Medium] On YT922x SerDes ports (0 and 8), nothing tells the SerDes side the resolved speed, duplex or link when in-band AN is not enabled. - [Medium] yt922x_pcs_an_restart() (drivers/net/dsa/motorcomm/pcs-922x.c) sets BMCR_RESET in addition to BMCR_ANRESTART. - [Low] The YT922x code disagrees with itself on PHY_INTERFACE_MODE_100BASEX. - [Low] The YT92XX_MODE_YT922X entry in yt92xx_series_table (drivers/net/dsa/motorcomm/chip.c) declares capabilities that… -- net: dsa: motorcomm: Add support for Motorcomm YT922x This adds initial support for the Motorcomm YT9224 switch to the yt921x DSA driver. It adds a separate dsa_switch_ops and phylink_mac_ops table for the YT922x series and a new PCS implementation in pcs-922x.c. Only port_setup, setup, phylink_get_caps and get_tag_protocol are provided. > diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c > index be82fbb99a936..b5c76a5ce55a2 100644 > --- a/drivers/net/dsa/motorcomm/chip.c > +++ b/drivers/net/dsa/motorcomm/chip.c [ ... ] > @@ -2574,6 +2593,470 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = { [ ... ] > +static int > +yt922x_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) > +{ [ ... ] > + /* > + * Self-negotiation: the LINK/AN bits in ctrl are set based to inband. > + * Forced mode: set LINK, keep the AN bit at 0 and clear it via the mask > + */ > + if (pp->inband) > + ctrl |= (YT922X_PORT_FC_AN | YT922X_PORT_LINK_AN); > + else > + ctrl |= YT922X_PORT_LINK; [Severity: Medium] Should YT922X_PORT_FC_AN really be set every time pp->inband is true? pp->inband is set in yt922x_pcs_config(): bool inband = neg_mode & PHYLINK_PCS_NEG_INBAND; In include/linux/phylink.h, PHYLINK_PCS_NEG_INBAND_DISABLED has the same value as PHYLINK_PCS_NEG_INBAND. So this test is also true when in-band AN is disabled and BMCR_ANENABLE is being cleared. yt922x_pcs_config() also never reads permit_pause_to_mac. As a result, FC_AN gets set in these cases: - the INBAND_DISABLED case - SGMII, whose in-band word carries no pause bits - when phylink passes permit_pause_to_mac = false, for example after ethtool -A ... autoneg off In those cases, would the MAC ignore the tx_pause/rx_pause values that phylink passed to mac_link_up()? The YT921x path in pcs-921x.c gates the equivalent bit like this: yt921x_serdes_config() { ... if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && permit_pause_to_mac && interface != PHY_INTERFACE_MODE_SGMII) ctrl |= YT921X_PORT_CTRL_PAUSE_AN; ... } Could YT922x use the same condition? > + > + res = yt921x_reg_update_bits(priv, YT922X_PORTn_CTRL(port), mask, ctrl); > + if (res) > + return res; [Severity: Medium] On the SerDes ports (0 and 8), does anything program the SerDes side with the resolved speed, duplex and link when in-band AN is not used? Examples are SGMII with an out-of-band PHY, or a fixed-link. As far as I can see, nothing does: - yt922x_port_up() writes only YT922X_PORTn_CTRL. - yt922x_pcs_config() writes only the SerDes mode field and BMCR_ANENABLE. - yt922x_phylink_pcs_ops has no .pcs_link_up. - yt922x_port_config() sets pp->serdes = true, but no YT922x code reads it. The YT921x path does this step in yt921x_port_up(): if (pp->serdes) { mask = YT921X_SERDES_SPEED_M; ctrl = YT921X_SERDES_SPEED(ps); ... If the YT922x PCS works the same way, could a 10/100 link through an SGMII PHY with AN disabled leave the PCS at its default rate while the MAC is forced to 10/100? The link would come up but pass no traffic. The YT922x register map isn't public, so the MAC-side write may be enough on its own. Could this be confirmed? [ ... ] > +static struct phylink_pcs * > +yt922x_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 = dsa_to_yt921x_priv(dp->ds); > + > + switch (interface) { > + case PHY_INTERFACE_MODE_SGMII: > + case PHY_INTERFACE_MODE_1000BASEX: > + case PHY_INTERFACE_MODE_2500BASEX: > + case PHY_INTERFACE_MODE_USXGMII: > + return &priv->ports[dp->index].pcs; > + > + default: > + return NULL; > + } > +} [Severity: Low] Is PHY_INTERFACE_MODE_100BASEX meant to be supported on YT922x? Some of the code handles it: - yt922x_port_config() accepts it. - yt922x_pcs_config() programs YT922X_SERDES_MODE_100BASEX. - yt922x_pcs_get_state() has a dedicated 100BASEX branch. Other places leave it out: - yt922x_dsa_phylink_get_caps() does not set it in config->supported_interfaces. - yt922x_pcs_setup() does not set it in pcs.supported_interfaces. - yt922x_phylink_mac_select_pcs() returns NULL for it. phylink will reject 100base-x, so the 100BASEX handling looks like dead code. Should 100BASEX be added to the caps and the PCS selection, or dropped? Also, yt922x_phylink_pcs_ops has no .pcs_inband_caps. yt921x_phylink_pcs_ops has yt921x_phylink_pcs_inband_caps(), which limits 100BASEX to LINK_INBAND_DISABLE. Should YT922x provide one as well, so phylink knows the in-band limits of the PCS? [ ... ] > @@ -2586,12 +3069,25 @@ static const struct yt92xx_series yt92xx_series_table[] = { > .switch_ops = &yt921x_dsa_switch_ops, > .mac_ops = &yt921x_phylink_mac_ops > }, > + [YT92XX_MODE_YT922X] = { > + .name = "YT922x", > + .max_ports = YT922X_PORT_NUM, > + .num_lag_ids = YT922X_LAG_NUM, > + .ageing_time_min = 1 * 6000, > + .ageing_time_max = U16_MAX * 6000, > + .dscp_prio_mapping_is_global = true, > + .assisted_learning_on_cpu_port = true, > + .switch_ops = &yt922x_dsa_switch_ops, > + .mac_ops = &yt922x_phylink_mac_ops, > + }, > }; [Severity: Low] Do these capabilities match what yt922x_dsa_switch_ops implements? yt92xx_register_switch() copies these fields into struct dsa_switch: - num_lag_ids - ageing_time_min/max - dscp_prio_mapping_is_global - assisted_learning_on_cpu_port yt922x_dsa_switch_ops only has get_tag_protocol, phylink_get_caps, port_setup and setup. There is no port_lag_join, set_ageing_time, port_fdb_add/del or DSCP op. With assisted_learning_on_cpu_port set, the DSA core will try to program host FDB entries. dsa_user_fdb_event() then returns -EOPNOTSUPP because port_fdb_add/del are missing. Nothing breaks at runtime today. Should these fields stay unset until the matching ops are added? They look copied from the YT921x entry. [ ... ] > @@ -2716,6 +3210,7 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev) > > static const struct of_device_id yt921x_of_match[] = { > { .compatible = "motorcomm,yt9215" }, > + { .compatible = "motorcomm,yt9224" }, > {} > }; [Severity: Medium] Is a binding update for this compatible missing from the series? Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml still has: const: motorcomm,yt9215 Its description only covers the YT9213/4/5/8 port layouts. YT9224 has a different layout: internal PHYs on ports 4-7, SerDes on ports 0 and 8, and no xMII. Any DTS using motorcomm,yt9224 would fail dtbs_check. checkpatch will also warn about an undocumented compatible. [ ... ] > diff --git a/drivers/net/dsa/motorcomm/pcs-922x.c b/drivers/net/dsa/motorcomm/pcs-922x.c > new file mode 100644 > index 0000000000000..5ad7873e0df7e > --- /dev/null > +++ b/drivers/net/dsa/motorcomm/pcs-922x.c [ ... ] > +static void yt922x_pcs_an_restart(struct phylink_pcs *pcs) > +{ [ ... ] > + mutex_lock(&priv->reg_lock); > + res = yt921x_intif_read(priv, addr, MII_BMCR, &bmcr); > + if (res) > + goto err; > + bmcr |= BMCR_ANRESTART; > + bmcr |= BMCR_RESET; > + res = yt921x_intif_write(priv, addr, MII_BMCR, bmcr); [Severity: Medium] Is BMCR_RESET intended here? This PCS is driven as a Clause 22 device. get_state uses MII_BMSR/MII_LPA and phylink_mii_c22_pcs_decode_state(). On such a PCS, BMCR bit 15 resets the control registers to their defaults. Could that wipe out the MII_ADVERTISE value and the BMCR_ANENABLE state that yt922x_pcs_config() just wrote? The code also does not wait for the self-clearing reset to complete. This path runs right after configuration. yt922x_pcs_config() reports a change even when only ANENABLE toggles: changed |= (bmcr_new != bmcr_old); So for 1000BASE-X/2500BASE-X with in-band AN, the first major config returns > 0 and phylink then calls pcs_an_restart. Running ethtool -r on the port takes the same path through phylink_ethtool_nway_reset(). The YT921x version, yt921x_phylink_pcs_an_restart(), only sets BMCR_ANRESTART. yt921x_serdes_config() also clears changed when ANENABLE toggles: /* 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; Should YT922x follow the same approach? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020111115.3072180-1-kyle.switch%40motor-comm.com ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch ` (4 preceding siblings ...) 2026-10-20 11:11 ` [PATCH net-next v16 5/5] net: dsa: motorcomm: Add support for Motorcomm YT922x Kyle Switch @ 2026-10-21 1:30 ` Jakub Kicinski 5 siblings, 0 replies; 11+ messages in thread From: Jakub Kicinski @ 2026-10-21 1:30 UTC (permalink / raw) To: Kyle Switch Cc: andrew, olteanv, davem, edumazet, pabeni, mmyangfl, horms, linux, netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, wei.zhang, sijia.huang On Tue, 20 Oct 2026 19:11:10 +0800 Kyle Switch wrote: > This patchset mainly adds support for the yt922x series to the dsa driver. > Currently, only the yt9224 in the yt922x series is supported, and the > corresponding tag handling for yt922x is added. ## Form letter - net-next-closed The merge window for Linux v7.4 has begun, and the net-next tree is now closed for new drivers, features, code refactoring and optimizations. We are currently accepting bug fixes only. Please repost when net-next reopens. There is some uncertainty about how long this merge window will last, so please check https://netdev.bots.linux.dev/net-next.html before reposting. net-next _may_ reopen on Nov 2nd. See: https://www.kernel.org/doc/html/next/process/maintainer-netdev.html#development-cycle -- pw-bot: defer pv-bot: closed ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-22 2:11 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-20 11:11 [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Kyle Switch 2026-10-20 11:11 ` [PATCH net-next v16 1/5] net: dsa: motorcomm: initialize dsa_switch based on chipid Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-20 11:11 ` [PATCH net-next v16 2/5] net: dsa: motorcomm: use max_ports in series for port bounds checking Kyle Switch 2026-10-20 11:11 ` [PATCH net-next v16 3/5] net: dsa: motorcomm: use fixed-width GENMASK_U*() helpers Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-20 11:11 ` [PATCH net-next v16 4/5] net: dsa: tag_yt922x: add support for Motorcomm YT922x tags Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-20 11:11 ` [PATCH net-next v16 5/5] net: dsa: motorcomm: Add support for Motorcomm YT922x Kyle Switch 2026-10-22 2:11 ` netdev-bot+sashiko 2026-10-21 1:30 ` [PATCH net-next v16 0/5] net: dsa: motorcomm: add support yt922x driver Jakub Kicinski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®