* [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
@ 2026-08-14 1:50 Kyle Switch
2026-08-16 21:17 ` David Yang
0 siblings, 1 reply; 6+ messages in thread
From: Kyle Switch @ 2026-08-14 1:50 UTC (permalink / raw)
To: andrew, olteanv, davem, edumazet, kuba, pabeni, mmyangfl, horms,
linux, netdev, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang
Motorcomm YT922x is a series of ethernet developed by Motorcomm
Electronic Technology, including YT9224 and YT9228.
Add support for the yt922x-series DSA driver in the existing files.
Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
changes in v1:
1. Add basic functional interfaces for the YT922X DSA driver.
2. Although the DSA driver supports both YT922X and YT921X simultaneously,
the original file names are still maintained for now;
the file naming may be optimized in the future.
3. Currently, the dsa_switch_ops structure is employed as the operational interface.
In future phases, as functionality expands, the architecture will be refactored
to introduce yt922x_dsa_ops, where chip-specific operations will be distinguished
between YT922X and YT921X, following a design pattern commonly adopted by other
existing multi-series DSA drivers.
4. A new thread(patch series) is created. Although a previous version was submitted,
the changes are substantial, so the old version is not relevant for reference.
drivers/net/dsa/Kconfig | 8 +-
drivers/net/dsa/Makefile | 2 +-
drivers/net/dsa/yt921x.c | 901 +++++++++++++++++++++++++++++++++++++--
drivers/net/dsa/yt921x.h | 108 +++++
include/net/dsa.h | 2 +
net/dsa/Kconfig | 6 +-
net/dsa/Makefile | 2 +-
net/dsa/tag_yt921x.c | 115 ++++-
8 files changed, 1101 insertions(+), 43 deletions(-)
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4ab567c5bbaf..69c824fd95bf 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -159,11 +159,11 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM
and VSC7398 SparX integrated ethernet switches, connected over
a CPU-attached address bus and work in memory-mapped I/O mode.
-config NET_DSA_YT921X
- tristate "Motorcomm YT9215 ethernet switch chip support"
- select NET_DSA_TAG_YT921X
+config NET_DSA_YT92XX
+ tristate "Motorcomm YT9215 and YT9224 ethernet switch chip support"
+ select NET_DSA_TAG_YT92XX
select NET_IEEE8021Q_HELPERS if DCB
help
- This enables support for the Motorcomm YT9215 ethernet switch
+ This enables support for the Motorcomm YT9215 and YT9224 ethernet switch
chip.
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index d2975badffc0..18c2ceef09a5 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
-obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
+obj-$(CONFIG_NET_DSA_YT92XX) += yt921x.o
obj-y += b53/
obj-y += hirschmann/
obj-y += lantiq/
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
index 159b16606f6c..742555f87819 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/yt921x.c
@@ -148,10 +148,15 @@ static const struct yt921x_info yt921x_infos[] = {
YT921X_PORT_MASK_INT0_n(8),
YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
},
+ {
+ "YT9224", YT9224_MAJOR, 0, 0,
+ 0x1f1,
+ 0x0,
+ },
{}
};
-#define YT921X_NAME "yt921x"
+#define YT92XX_NAME "yt92xx"
#define YT921X_VID_UNWARE 4095
@@ -560,10 +565,12 @@ yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
{
struct yt921x_priv *priv = mbus->priv;
+ int max_ports;
u16 val;
int res;
- if (port >= YT921X_PORT_NUM)
+ max_ports = priv->chip_info->ports;
+ if (port >= max_ports)
return U16_MAX;
mutex_lock(&priv->reg_lock);
@@ -579,9 +586,11 @@ static int
yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
{
struct yt921x_priv *priv = mbus->priv;
+ int max_ports;
int res;
- if (port >= YT921X_PORT_NUM)
+ max_ports = priv->chip_info->ports;
+ if (port >= max_ports)
return -ENODEV;
mutex_lock(&priv->reg_lock);
@@ -596,19 +605,21 @@ yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
{
struct device *dev = to_device(priv);
struct mii_bus *mbus;
+ int max_ports;
int res;
mbus = devm_mdiobus_alloc(dev);
if (!mbus)
return -ENOMEM;
+ max_ports = priv->chip_info->ports;
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);
+ mbus->phy_mask = (u32)~GENMASK(max_ports - 1, 0);
res = devm_of_mdiobus_register(dev, mbus, mnp);
if (res)
@@ -4748,6 +4759,13 @@ static int yt921x_dsa_setup(struct dsa_switch *ds)
struct device_node *child;
int res;
+ for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
+ struct yt921x_port *pp = &priv->ports[i];
+
+ pp->index = i;
+ INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
+ }
+
mutex_lock(&priv->reg_lock);
res = yt921x_chip_reset(priv);
mutex_unlock(&priv->reg_lock);
@@ -4869,7 +4887,821 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
.setup = yt921x_dsa_setup,
};
-static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
+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 = 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)
+{
+ 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:
+ return -EINVAL;
+ }
+ 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_LINK |
+ YT922X_PORT_CFG_RX_EN;
+ ctrl &= ~(YT922X_PORT_FC_AN | YT922X_PORT_LINK_AN);
+ res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), 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 = 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_intif_ext_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
+{
+ int res;
+
+ if (port >= YT922X_PORT_NUM)
+ 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_intif_ext_write(struct yt921x_priv *priv, int port, int reg, u16 val)
+{
+ int res;
+
+ if (port >= YT921X_PORT_NUM)
+ 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;
+}
+
+static int yt922x_internal_phyaddr_get(int port,
+ enum yt922x_phy_reg_type reg_type,
+ enum yt922x_phy_reg_space reg_space)
+{
+ int res;
+
+ if (port >= 4 && port <= 7) {
+ switch (reg_type) {
+ case YT922X_PHY_REG_TYPE_COMMON_EXT:
+ res = 9;
+ break;
+ case YT922X_PHY_REG_TYPE_PHY_COMMON_EXT:
+ if (port <= 5 && port >= 4)
+ res = 4;
+ else if (port <= 7 && port >= 6)
+ res = 6;
+ break;
+ case YT922X_PHY_REG_TYPE_SDS_COMMON_EXT:
+ res = -EINVAL;
+ break;
+ default:
+ if (reg_space != YT922X_PHY_REG_SPACE_PHY)
+ res = -EINVAL;
+ break;
+ }
+ } else {
+ if (port == 8) {
+ switch (reg_type) {
+ case YT922X_PHY_REG_TYPE_COMMON_EXT:
+ res = 9;
+ break;
+ case YT922X_PHY_REG_TYPE_SDS_COMMON_EXT:
+ res = 8;
+ break;
+ case YT922X_PHY_REG_TYPE_MII:
+ case YT922X_PHY_REG_TYPE_EXT:
+ if (reg_space == YT922X_PHY_REG_SPACE_SGMII ||
+ reg_space == YT922X_PHY_REG_SPACE_USXGMII)
+ res = 8;
+ else
+ res = -EINVAL;
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
+ return res;
+ }
+ switch (reg_type) {
+ case YT922X_PHY_REG_TYPE_COMMON_EXT:
+ res = 9;
+ break;
+ case YT922X_PHY_REG_TYPE_SDS_COMMON_EXT:
+ res = 0;
+ break;
+ case YT922X_PHY_REG_TYPE_MII:
+ case YT922X_PHY_REG_TYPE_EXT:
+ if (reg_space == YT922X_PHY_REG_SPACE_SGMII ||
+ reg_space == YT922X_PHY_REG_SPACE_USXGMII)
+ res = 0;
+ else
+ res = -EINVAL;
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static int yt922x_port_sds_init(struct yt921x_priv *priv, int port,
+ phy_interface_t interface)
+{
+ int addr;
+ u16 data;
+ int res;
+
+ addr = yt922x_internal_phyaddr_get(port,
+ YT922X_PHY_REG_TYPE_SDS_COMMON_EXT,
+ YT922X_PHY_REG_SPACE_SGMII);
+ if (addr < 0)
+ return -EINVAL;
+ /* write protect */
+ res = yt921x_intif_ext_write(priv, addr, 0x4be, 0xd);
+ if (res)
+ return res;
+ /* CDR */
+ if (interface == PHY_INTERFACE_MODE_100BASEX) {
+ res = yt921x_intif_ext_write(priv, addr, 0x406, 0x0);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x416, 0x3458);
+ if (res)
+ return res;
+ } else {
+ res = yt921x_intif_ext_write(priv, addr, 0x406, 0x800);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x416, 0x4558);
+ if (res)
+ return res;
+ }
+ /* PLL */
+ if (interface == PHY_INTERFACE_MODE_USXGMII) {
+ res = yt921x_intif_ext_write(priv, addr, 0x43a, 0x1006);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x43f, 0x3029);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x42a, 0xf070);
+ if (res)
+ return res;
+ } else {
+ res = yt921x_intif_ext_write(priv, addr, 0x43d, 0x207d);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x43c, 0x207d);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x43f, 0x3032);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x43a, 0x6);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x42a, 0xf070);
+ if (res)
+ return res;
+ }
+ /* VCO */
+ res = yt921x_intif_ext_write(priv, addr, 0x439, 0xC0);
+ if (res)
+ return res;
+ /* Vdac */
+ res = yt921x_intif_ext_write(priv, addr, 0x492, 0x7f7f);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x491, 0x7f);
+ if (res)
+ return res;
+ /* Eye */
+ res = yt921x_intif_ext_write(priv, addr, 0x454, 0xf14);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x497, 0xa44);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x4cd, 0x0);
+ if (res)
+ return res;
+
+ res = yt921x_intif_ext_write(priv, addr, 0x4af, 0x45e3);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x48a, 0xfff);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x408, 0x7c00);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x4d6, 0x7f);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x44f, 0xff08);
+ if (res)
+ return res;
+ /* FFE */
+ res = yt921x_intif_ext_write(priv, addr, 0x48e, 0x7d00);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0xd, 0x60f);
+ if (res)
+ return res;
+ /* CTLE */
+ res = yt921x_intif_ext_write(priv, addr, 0x4b0, 0x804);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x4b1, 0x7774);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x4af, 0x45e7);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x3, 0x5603);
+ if (res)
+ return res;
+
+ msleep(20);
+ res = yt921x_intif_ext_write(priv, addr, 0x492, 0x7fff);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x492, 0x7f7f);
+ if (res)
+ return res;
+ /* CTLE */
+ res = yt921x_intif_ext_write(priv, addr, 0x2000, 0x40);
+ if (res)
+ return res;
+ res = yt921x_intif_ext_write(priv, addr, 0x2000, 0x0);
+ if (res)
+ return res;
+
+ if (interface == PHY_INTERFACE_MODE_SGMII) {
+ res = yt921x_intif_ext_write(priv, addr, 0x1042, 0x48c);
+ if (res)
+ return res;
+ }
+
+ /* soft reset */
+ addr = yt922x_internal_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII,
+ YT922X_PHY_REG_SPACE_SGMII);
+ if (addr < 0)
+ return res;
+ res = yt921x_intif_read(priv, addr, 0x2000, &data);
+ if (res)
+ return res;
+ data &= ~(1 << 15);
+ res = yt921x_intif_write(priv, addr, 0x2000, data);
+ if (res)
+ return res;
+ addr = yt922x_internal_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII,
+ YT922X_PHY_REG_SPACE_USXGMII);
+ if (addr < 0)
+ return res;
+ res = yt921x_intif_read(priv, addr, 0x0, &data);
+ if (res)
+ return res;
+ data |= 1 << 15;
+ res = yt921x_intif_write(priv, addr, 0x0, data);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt922x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
+ phy_interface_t interface)
+{
+ int addr;
+ u16 data;
+ int res;
+
+ addr = yt922x_internal_phyaddr_get(port,
+ YT922X_PHY_REG_TYPE_SDS_COMMON_EXT,
+ YT922X_PHY_REG_SPACE_SGMII);
+ if (addr < 0)
+ return -EINVAL;
+ 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:
+ res = yt922x_port_sds_init(priv, port, interface);
+ if (res)
+ return res;
+
+ addr = yt922x_internal_phyaddr_get
+ (port, YT922X_PHY_REG_TYPE_SDS_COMMON_EXT,
+ YT922X_PHY_REG_SPACE_SGMII);
+ if (addr < 0)
+ return -EINVAL;
+
+ res = yt922x_intif_ext_read(priv, addr, YT922X_PORT_SDS_MODE,
+ &data);
+ if (res)
+ return res;
+
+ data &= (~0x7 << 4);
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ data |= FIELD_PREP(YT922X_PORT_SDS_MODE_M,
+ YT92XX_SERDES_MODE_SGMII);
+ break;
+ case PHY_INTERFACE_MODE_100BASEX:
+ data |= FIELD_PREP(YT922X_PORT_SDS_MODE_M,
+ YT92XX_SERDES_MODE_100BASEX);
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ data |= FIELD_PREP(YT922X_PORT_SDS_MODE_M,
+ YT92XX_SERDES_MODE_1000BASEX);
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ data |= FIELD_PREP(YT922X_PORT_SDS_MODE_M,
+ YT92XX_SERDES_MODE_2500BASEX);
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ data |= FIELD_PREP(YT922X_PORT_SDS_MODE_M,
+ YT92XX_SERDES_MODE_USXGMII);
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = yt921x_intif_ext_write(priv, addr, YT922X_PORT_SDS_MODE,
+ data);
+ if (res)
+ return res;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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 = 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 const struct phylink_mac_ops yt922x_phylink_mac_ops = {
+ .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 = 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)) {
+ if (port >= 4 && port <= 7) {
+ /* port 4 to port 7, internal utp */
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ config->supported_interfaces);
+ config->mac_capabilities |= MAC_2500FD;
+ } else {
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_100BASEX,
+ 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_2500FD;
+ config->mac_capabilities |= MAC_5000FD;
+ config->mac_capabilities |= MAC_10000FD;
+ }
+ } else {
+ /* external port will added later */
+ }
+}
+
+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 = 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_chip_detect(struct yt921x_priv *priv)
+{
+ struct device *dev = to_device(priv);
+ const struct yt921x_info *info;
+ u32 chipid;
+ u32 major;
+ int res;
+
+ res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
+ if (res)
+ return res;
+ major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
+ for (info = yt921x_infos; info->name; info++)
+ if (info->major == major)
+ break;
+ if (!info->name) {
+ dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
+ return -ENODEV;
+ }
+ priv->info = info;
+
+ return 0;
+}
+
+static int yt922x_chip_reset(struct yt921x_priv *priv)
+{
+ u32 val;
+ int res;
+
+ res = yt922x_chip_detect(priv);
+ if (res)
+ return res;
+
+ /* Reset */
+ res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);
+ if (res)
+ return res;
+
+ fsleep(YT921X_RST_DELAY_US);
+
+ val = 0;
+ res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int yt922x_chip_setup_dsa(struct yt921x_priv *priv)
+{
+ unsigned long cpu_ports_mask;
+ u32 ctrl;
+ int port;
+ int res;
+
+ ctrl = GENMASK(9, 0);
+ res = yt921x_reg_write(priv, YT922X_FILTER_UNK_UCAST, ctrl);
+ if (res)
+ return res;
+
+ ctrl = 0;
+ for (int i = 0; i < priv->chip_info->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->chip_info->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 int yt922x_cpu_tag_mode_set(struct yt921x_priv *priv)
+{
+ struct device *dev = to_device(priv);
+ u16 eth_p_tag;
+ u32 val;
+ u32 val1;
+ int res;
+
+ /* cpu tag mode set */
+ 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;
+
+ /* tpid check */
+ res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
+ if (res)
+ return res;
+ eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
+ if (eth_p_tag != ETH_P_YT921X) {
+ dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
+ ETH_P_YT921X);
+ return -EINVAL;
+ }
+
+ 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(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_dsa_setup(struct dsa_switch *ds)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct device *dev = to_device(priv);
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt922x_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;
+ }
+
+ /* cpu port set */
+ mutex_lock(&priv->reg_lock);
+ res = yt922x_cpu_port_set(priv);
+ mutex_unlock(&priv->reg_lock);
+ if (res)
+ return res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt922x_chip_setup(priv);
+ mutex_unlock(&priv->reg_lock);
+ if (res)
+ return res;
+
+ 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_chip_info yt92xx_chip_info_table[] = {
+ [YT9215] = {
+ .mode = YT9215,
+ .name = "motorcomm yt9215",
+ .ports = YT921X_PORT_NUM,
+ .lag_id = YT921X_LAG_NUM,
+ .ageing_time_min = 1 * 5000,
+ .ageing_time_max = U16_MAX * 5000,
+ .switch_ops = &yt921x_dsa_switch_ops,
+ .mac_ops = &yt921x_phylink_mac_ops,
+ },
+ [YT9224] = {
+ .mode = YT9224,
+ .name = "motorcomm yt9224",
+ .ports = YT922X_PORT_NUM,
+ .lag_id = YT922X_LAG_NUM,
+ .ageing_time_min = 1 * 6000,
+ .ageing_time_max = U16_MAX * 6000,
+ .switch_ops = &yt922x_dsa_switch_ops,
+ .mac_ops = &yt922x_phylink_mac_ops,
+ },
+};
+
+static void yt92xx_mdio_shutdown(struct mdio_device *mdiodev)
{
struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
@@ -4879,7 +5711,7 @@ static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
dsa_switch_shutdown(&priv->ds);
}
-static void yt921x_mdio_remove(struct mdio_device *mdiodev)
+static void yt92xx_mdio_remove(struct mdio_device *mdiodev)
{
struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
@@ -4911,13 +5743,18 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev)
mutex_destroy(&priv->reg_lock);
}
-static int yt921x_mdio_probe(struct mdio_device *mdiodev)
+static int yt92xx_mdio_probe(struct mdio_device *mdiodev)
{
+ const struct yt92xx_chip_info *compat_info = NULL;
struct device *dev = &mdiodev->dev;
struct yt921x_reg_mdio *mdio;
struct yt921x_priv *priv;
struct dsa_switch *ds;
+ compat_info = of_device_get_match_data(dev);
+ if (!compat_info)
+ return -EINVAL;
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -4932,51 +5769,53 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
mutex_init(&priv->reg_lock);
+ priv->chip_info = compat_info;
priv->reg_ops = &yt921x_reg_ops_mdio;
priv->reg_ctx = mdio;
- for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
- struct yt921x_port *pp = &priv->ports[i];
-
- pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
- }
-
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;
+ ds->ops = compat_info->switch_ops;
+ ds->ageing_time_min = compat_info->ageing_time_min;
+ ds->ageing_time_max = compat_info->ageing_time_max;
+ ds->phylink_mac_ops = compat_info->mac_ops;
+ ds->num_lag_ids = compat_info->lag_id;
+ ds->num_ports = compat_info->ports;
mdiodev_set_drvdata(mdiodev, priv);
return dsa_register_switch(ds);
}
-static const struct of_device_id yt921x_of_match[] = {
- { .compatible = "motorcomm,yt9215" },
+static const struct of_device_id yt92xx_of_match[] = {
+ {
+ .compatible = "motorcomm,yt9215",
+ .data = &yt92xx_chip_info_table[YT9215],
+ },
+ {
+ .compatible = "motorcomm,yt9224",
+ .data = &yt92xx_chip_info_table[YT9224],
+ },
{}
};
-MODULE_DEVICE_TABLE(of, yt921x_of_match);
+MODULE_DEVICE_TABLE(of, yt92xx_of_match);
-static struct mdio_driver yt921x_mdio_driver = {
- .probe = yt921x_mdio_probe,
- .remove = yt921x_mdio_remove,
- .shutdown = yt921x_mdio_shutdown,
+static struct mdio_driver yt92xx_mdio_driver = {
+ .probe = yt92xx_mdio_probe,
+ .remove = yt92xx_mdio_remove,
+ .shutdown = yt92xx_mdio_shutdown,
.mdiodrv.driver = {
- .name = YT921X_NAME,
- .of_match_table = yt921x_of_match,
+ .name = YT92XX_NAME,
+ .of_match_table = yt92xx_of_match,
},
};
-mdio_module_driver(yt921x_mdio_driver);
+mdio_module_driver(yt92xx_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/yt921x.h b/drivers/net/dsa/yt921x.h
index 5f3b99e189c4..0308e6b91618 100644
--- a/drivers/net/dsa/yt921x.h
+++ b/drivers/net/dsa/yt921x.h
@@ -837,6 +837,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
@@ -853,6 +854,96 @@ enum yt921x_fdb_entry_status {
/* 8 internal + 2 external + 1 mcu */
#define YT921X_PORT_NUM 11
+/* yt922x register lists */
+#define YT92XX_PAGE_SELECT 0x1e
+#define YT92XX_PAGE 0x1f
+#define YT922X_PORTn_CTRL(port) (0x80080 + 4 * (port))
+#define YT922X_PORT_CTRL_PAUSE_AN BIT(10)
+#define YT922X_PORTn_STATUS(port) (0x80200 + 4 * (port))
+#define YT922X_EN_PHY_OVERWRITE (0x80040)
+#define YT922X_EN_PHY_VALUE (0x8003c)
+/* CTRL: force op to make soft configuration effective */
+#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_SDS_MODE 0x400
+#define YT922X_PORT_SDS_MODE_M GENMASK(6, 4)
+#define YT92XX_SERDES_MODE_SGMII 0
+#define YT92XX_SERDES_MODE_REVSGMII 1
+#define YT92XX_SERDES_MODE_1000BASEX 2
+#define YT92XX_SERDES_MODE_100BASEX 3
+#define YT92XX_SERDES_MODE_2500BASEX 4
+#define YT92XX_SERDES_MODE_USXGMII 6
+#define YT922X_PORT_NUM 9
+/* LAG */
+#define YT922X_LAG_NUM 4
+/* ISO */
+#define YT922X_PORTn_ISOLATION(port) (0x4 * (port) + 0x180d80)
+/* FDB */
+#define YT922X_PORTn_LEARN(port) (0x180300 + 4 * (port))
+#define YT922X_PORT_LEARN_DIS BIT(18)
+/* GLOBAL CTRL */
+#define YT922X_FUNC_ACL BIT(5)
+#define YT922X_FUNC_MIB BIT(4)
+/* CTRL PKT */
+#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_TRAP(port) YT922X_ACT_UNK_ACTn(port, 3) /* steer to CPU */
+#define YT922X_ACT_UNK_ACTn_DROP(port) YT922X_ACT_UNK_ACTn(port, 1) /* discard */
+/* NEVER use this action; see comments in the tag driver */
+#define YT922X_ACT_UNK_ACTn_COPY(port) YT922X_ACT_UNK_ACTn(port, 2) /* flood and copy */
+
+/* CPU PORT */
+#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)
+
+enum yt922x_phy_reg_type {
+ YT922X_PHY_REG_TYPE_COMMON_EXT,
+ YT922X_PHY_REG_TYPE_PHY_COMMON_EXT,
+ YT922X_PHY_REG_TYPE_SDS_COMMON_EXT,
+ YT922X_PHY_REG_TYPE_MII,
+ YT922X_PHY_REG_TYPE_EXT,
+ YT922X_PHY_REG_TYPE_MMD1,
+ YT922X_PHY_REG_TYPE_MMD3,
+ YT922X_PHY_REG_TYPE_MMD7,
+ YT922X_PHY_REG_TYPE_MAX
+};
+
+enum yt922x_phy_reg_space {
+ YT922X_PHY_REG_SPACE_PHY,
+ YT922X_PHY_REG_SPACE_PHY_T1,
+ YT922X_PHY_REG_SPACE_PHY_LDS,
+ YT922X_PHY_REG_SPACE_SGMII,
+ YT922X_PHY_REG_SPACE_USXGMII,
+ YT922X_PHY_REG_SPACE_QSGMII,
+ YT922X_PHY_REG_SPACE_MAX
+};
+
#define yt921x_port_is_internal(port) ((port) < 8)
#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9)
@@ -945,9 +1036,26 @@ struct yt921x_reg_ops {
int (*write)(void *context, u32 reg, u32 val);
};
+enum yt92xx_mode {
+ YT9215,
+ YT9224,
+};
+
+struct yt92xx_chip_info {
+ enum yt92xx_mode mode;
+ const char *name;
+ unsigned int ports;
+ unsigned int lag_id;
+ unsigned int ageing_time_min;
+ unsigned int ageing_time_max;
+ const struct dsa_switch_ops *switch_ops;
+ const struct phylink_mac_ops *mac_ops;
+};
+
struct yt921x_priv {
struct dsa_switch ds;
+ const struct yt92xx_chip_info *chip_info;
const struct yt921x_info *info;
unsigned int meter_slot_ns;
unsigned int port_shape_slot_ns;
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 6f7f5c17b532..0c033ca65b92 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -60,6 +60,7 @@ struct tc_action;
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
#define DSA_TAG_PROTO_KSZ8463_VALUE 34
+#define DSA_TAG_PROTO_YT922X_VALUE 35
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -97,6 +98,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE,
+ DSA_TAG_PROTO_YT922X = DSA_TAG_PROTO_YT922X_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index d5e725b90d78..e4fd3a9a6621 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -215,10 +215,10 @@ config NET_DSA_TAG_XRS700X
Say Y or M if you want to enable support for tagging frames for
Arrow SpeedChips XRS700x switches that use a single byte tag trailer.
-config NET_DSA_TAG_YT921X
- tristate "Tag driver for Motorcomm YT921x switches"
+config NET_DSA_TAG_YT92XX
+ tristate "Tag driver for Motorcomm YT921x and YT922x switches"
help
Say Y or M if you want to enable support for tagging frames for
- Motorcomm YT921x switches.
+ Motorcomm YT921x and YT922x switches.
endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index b8c2667cd14a..2d72a25fd984 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -42,7 +42,7 @@ obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
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_YT92XX) += tag_yt921x.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c
index 294784ab6694..a3012ec39868 100644
--- a/net/dsa/tag_yt921x.c
+++ b/net/dsa/tag_yt921x.c
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Motorcomm YT921x Switch Extended CPU Port Tagging
+ * Motorcomm YT922x Switch Extended CPU Port Tagging
*
* Copyright (c) 2025 David Yang <mmyangfl@gmail.com>
+ * Copyright (c) 2026 Kyle switch <kyle.switch@motor-comm.com>
*
* +----+----+-------+-----+----+---------
* | DA | SA | TagET | Tag | ET | Payload ...
@@ -166,8 +168,115 @@ static const struct dsa_device_ops yt921x_netdev_ops = {
.needed_headroom = YT921X_TAG_LEN,
};
-MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x switches");
-MODULE_LICENSE("GPL");
+DSA_TAG_DRIVER(yt921x_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_TAG_NAME);
-module_dsa_tag_driver(yt921x_netdev_ops);
+/* To define the from cpu tag format 8 bytes:
+ *
+ * 0 1 2 3 4 5 6 7 |0 1 2 3 4 5 6 7
+ *|<------------TPID 0x9988------->|
+ *|<--RESERVE-->|<-----DST POR---->|
+ *|-|<---------RESERVE------------>|
+ *|<------------------------------>|
+ */
+#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)
+#define YT922X_TAG_RX_PRIO_M GENMASK(15, 13)
+
+static struct sk_buff *
+yt922x_tag_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct dsa_port *dp = dsa_user_to_port(netdev);
+ __be16 *tag;
+ u16 ctrl;
+
+ skb_push(skb, YT921X_TAG_LEN);
+ dsa_alloc_etype_header(skb, YT921X_TAG_LEN);
+ tag = dsa_etype_header_pos_tx(skb);
+
+ tag[0] = htons(ETH_P_YT921X);
+ if (dp->index != 0) {
+ /* Port index is not equal 0 in tag[1] */
+ ctrl = YT922X_TAG_PRIO(skb->priority) | YT922X_TAG_FORCE_DST |
+ YT922X_TAG_PORTS(dsa_xmit_port_mask(skb, netdev) - 1);
+ tag[1] = htons(ctrl);
+ tag[2] = 0;
+ } else {
+ /* Port 0 in bit15 in tag[2] */
+ ctrl = YT922X_TAG_PRIO(skb->priority) | YT922X_TAG_FORCE_DST;
+ tag[1] = htons(ctrl);
+ ctrl = YT922X_TAG_PORTMASK_0;
+ tag[2] = htons(ctrl);
+ }
+ 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, YT921X_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, YT921X_TAG_LEN);
+ dsa_strip_etype_header(skb, YT921X_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 = YT921X_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(yt922x_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT922X, YT922X_TAG_NAME);
+
+static struct dsa_tag_driver *dsa_tag_driver_array[] = {
+ &DSA_TAG_DRIVER_NAME(yt921x_netdev_ops),
+ &DSA_TAG_DRIVER_NAME(yt922x_netdev_ops),
+};
+module_dsa_tag_drivers(dsa_tag_driver_array);
+
+MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x and YT922x switches");
+MODULE_LICENSE("GPL");
+
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
2026-08-14 1:50 [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x Kyle Switch
@ 2026-08-16 21:17 ` David Yang
2026-08-17 10:40 ` Kyle Switch
0 siblings, 1 reply; 6+ messages in thread
From: David Yang @ 2026-08-16 21:17 UTC (permalink / raw)
To: Kyle Switch
Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, horms, linux,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang
On Fri, Aug 14, 2026 at 9:50 AM Kyle Switch <kyle.switch@motor-comm.com> wrote:
> @@ -148,10 +148,15 @@ static const struct yt921x_info yt921x_infos[] = {
> YT921X_PORT_MASK_INT0_n(8),
> YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
> },
> + {
> + "YT9224", YT9224_MAJOR, 0, 0,
> + 0x1f1,
> + 0x0,
> + },
Why plain magic numbers here, despite what the above entries shows?
> {}
> };
>
> -#define YT921X_NAME "yt921x"
> +#define YT92XX_NAME "yt92xx"
This changed the module name which may affect end users. While I have
no objection to this, subsystem maintainers may have different
opinions.
> +static int
> +yt922x_intif_ext_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
[...]
> +static int
> +yt921x_intif_ext_write(struct yt921x_priv *priv, int port, int reg, u16 val)
Place them with yt921x_*if_*().
> +static int yt922x_internal_phyaddr_get(int port,
> + enum yt922x_phy_reg_type reg_type,
> + enum yt922x_phy_reg_space reg_space)
[...]
This makes me headache and is prone to errors (an example below).
Reorder it with early returns and/or lookup tables so that it is
obviously correct.
> + default:
> + if (reg_space != YT922X_PHY_REG_SPACE_PHY)
> + res = -EINVAL;
> + break;
What would you expect here, when reg_space == YT922X_PHY_REG_SPACE_PHY?
> +static int yt922x_port_sds_init(struct yt921x_priv *priv, int port,
> + phy_interface_t interface)
[...]
> + addr = yt922x_internal_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII,
> + YT922X_PHY_REG_SPACE_SGMII);
> + if (addr < 0)
> + return res;
> + res = yt921x_intif_read(priv, addr, 0x2000, &data);
> + if (res)
> + return res;
> + data &= ~(1 << 15);
> + res = yt921x_intif_write(priv, addr, 0x2000, data);
> + if (res)
> + return res;
> + addr = yt922x_internal_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII,
> + YT922X_PHY_REG_SPACE_USXGMII);
> + if (addr < 0)
> + return res;
> + res = yt921x_intif_read(priv, addr, 0x0, &data);
> + if (res)
> + return res;
> + data |= 1 << 15;
> + res = yt921x_intif_write(priv, addr, 0x0, data);
> + if (res)
> + return res;
Numerous magic numbers. On YT9215, same finetunes are not required for
a usable port. If you cannot name it, drop it if it is not mandatory.
> +static int yt922x_cpu_tag_mode_set(struct yt921x_priv *priv)
> +{
> + struct device *dev = to_device(priv);
> + u16 eth_p_tag;
> + u32 val;
> + u32 val1;
> + int res;
> +
> + /* cpu tag mode set */
> + 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;
> +
> + /* tpid check */
> + res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
> + if (res)
> + return res;
> + eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
> + if (eth_p_tag != ETH_P_YT921X) {
> + dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
> + ETH_P_YT921X);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
This looks similar to yt921x_chip_reset(), so you'd better merge it
into yt922x_chip_reset() above.
> +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(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;
> +}
Also, snippets from yt921x_chip_setup_dsa() and unaligned function names.
> -static void yt921x_mdio_remove(struct mdio_device *mdiodev)
> +static void yt92xx_mdio_remove(struct mdio_device *mdiodev)
Unnecessary renaming.
> +#define YT922X_PORT_SDS_MODE 0x400
> +#define YT922X_PORT_SDS_MODE_M GENMASK(6, 4)
> +#define YT92XX_SERDES_MODE_SGMII 0
> +#define YT92XX_SERDES_MODE_REVSGMII 1
> +#define YT92XX_SERDES_MODE_1000BASEX 2
> +#define YT92XX_SERDES_MODE_100BASEX 3
> +#define YT92XX_SERDES_MODE_2500BASEX 4
> +#define YT92XX_SERDES_MODE_USXGMII 6
Not an objection, but better to keep the formats with YT921X_SERDESn:
#define YT922X_PORT_SERDES 0x400
#define YT922X_PORT_SERDES_MODE_M GENMASK(6, 4)
#define YT922X_PORT_SERDES_MODE(x)
FIELD_PREP(YT922X_PORT_SERDES_MODE_M, (x))
#define YT922X_PORT_SERDES_MODE_SGMII YT922X_PORT_SERDES_MODE(0)
> +#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_TRAP(port) YT922X_ACT_UNK_ACTn(port, 3) /* steer to CPU */
> +#define YT922X_ACT_UNK_ACTn_DROP(port) YT922X_ACT_UNK_ACTn(port, 1) /* discard */
> +/* NEVER use this action; see comments in the tag driver */
> +#define YT922X_ACT_UNK_ACTn_COPY(port) YT922X_ACT_UNK_ACTn(port, 2) /* flood and copy */
Sort them in numerical order.
> +
> +/* CPU PORT */
> +#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)
Not an objection, but better to keep the indentation formats of yt921x
register definitions above.
> +struct yt92xx_chip_info {
> + enum yt92xx_mode mode;
> + const char *name;
> + unsigned int ports;
> + unsigned int lag_id;
lag_ids, or num_lag_ids (and num_ports) for consistency.
> struct yt921x_priv {
> struct dsa_switch ds;
>
> + const struct yt92xx_chip_info *chip_info;
> const struct yt921x_info *info;
yt921x_priv :: info is chip info already. You may want `series_info`,
or more straightforward, `series` and `struct yt92xx_series`.
> diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c
> index 294784ab6694..a3012ec39868 100644
> --- a/net/dsa/tag_yt921x.c
> +++ b/net/dsa/tag_yt921x.c
[...]
> +/* To define the from cpu tag format 8 bytes:
> + *
> + * 0 1 2 3 4 5 6 7 |0 1 2 3 4 5 6 7
> + *|<------------TPID 0x9988------->|
> + *|<--RESERVE-->|<-----DST POR---->|
> + *|-|<---------RESERVE------------>|
> + *|<------------------------------>|
> + */
> +#define YT922X_TAG_NAME "yt922x"
> +#define YT922X_TAG_PORTMASK_0 BIT(15)
Sort them according to their corresponding word.
> +#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)
> +#define YT922X_TAG_RX_PRIO_M GENMASK(15, 13)
This did not answer the question in the previous review: do tag_yt922x
and tag_yt921x share any common routines? If not, better to make
tag_yt922x a separate file.
> +static struct sk_buff *
> +yt922x_tag_xmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> + struct dsa_port *dp = dsa_user_to_port(netdev);
> + __be16 *tag;
> + u16 ctrl;
> +
> + skb_push(skb, YT921X_TAG_LEN);
> + dsa_alloc_etype_header(skb, YT921X_TAG_LEN);
> + tag = dsa_etype_header_pos_tx(skb);
> +
> + tag[0] = htons(ETH_P_YT921X);
> + if (dp->index != 0) {
> + /* Port index is not equal 0 in tag[1] */
> + ctrl = YT922X_TAG_PRIO(skb->priority) | YT922X_TAG_FORCE_DST |
> + YT922X_TAG_PORTS(dsa_xmit_port_mask(skb, netdev) - 1);
dsa_xmit_port_mask() returns a bit mask - you won't do arithmetic
operations to a bit mask normally.
The field name does suggest it is a port mask. Have you noticed
excessive packets on other ports?
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
2026-08-16 21:17 ` David Yang
@ 2026-08-17 10:40 ` Kyle Switch
2026-08-17 13:12 ` Andrew Lunn
2026-08-17 21:48 ` David Yang
0 siblings, 2 replies; 6+ messages in thread
From: Kyle Switch @ 2026-08-17 10:40 UTC (permalink / raw)
To: David Yang
Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, horms, linux,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang
On 8/17/26 05:17, David Yang wrote:
> On Fri, Aug 14, 2026 at 9:50 AM Kyle Switch <kyle.switch@motor-comm.com> wrote:
>
>> @@ -148,10 +148,15 @@ static const struct yt921x_info yt921x_infos[] = {
>> YT921X_PORT_MASK_INT0_n(8),
>> YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
>> },
>> + {
>> + "YT9224", YT9224_MAJOR, 0, 0,
>> + 0x1f1,
>> + 0x0,
>> + },
> Why plain magic numbers here, despite what the above entries shows?
Ans: will fixed done in patch v2.
>
>> {}
>> };
>>
>> -#define YT921X_NAME "yt921x"
>> +#define YT92XX_NAME "yt92xx"
> This changed the module name which may affect end users. While I have
> no objection to this, subsystem maintainers may have different
> opinions.
Ans: This is used to indicate support for both YT921X and YT922X.
>
>> +static int
>> +yt922x_intif_ext_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
> [...]
>
>> +static int
>> +yt921x_intif_ext_write(struct yt921x_priv *priv, int port, int reg, u16 val)
> Place them with yt921x_*if_*().
Ans: fix done in patch v2.
>
>> +static int yt922x_internal_phyaddr_get(int port,
>> + enum yt922x_phy_reg_type reg_type,
>> + enum yt922x_phy_reg_space reg_space)
> [...]
>
> This makes me headache and is prone to errors (an example below).
> Reorder it with early returns and/or lookup tables so that it is
> obviously correct.
Ans: The interface will be optimized in v1 using early returns.
>
>> + default:
>> + if (reg_space != YT922X_PHY_REG_SPACE_PHY)
>> + res = -EINVAL;
>> + break;
> What would you expect here, when reg_space == YT922X_PHY_REG_SPACE_PHY?
Ans: The expected return result is an error.
>
>> +static int yt922x_port_sds_init(struct yt921x_priv *priv, int port,
>> + phy_interface_t interface)
> [...]
>
>> + addr = yt922x_internal_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII,
>> + YT922X_PHY_REG_SPACE_SGMII);
>> + if (addr < 0)
>> + return res;
>> + res = yt921x_intif_read(priv, addr, 0x2000, &data);
>> + if (res)
>> + return res;
>> + data &= ~(1 << 15);
>> + res = yt921x_intif_write(priv, addr, 0x2000, data);
>> + if (res)
>> + return res;
>> + addr = yt922x_internal_phyaddr_get(port, YT922X_PHY_REG_TYPE_MII,
>> + YT922X_PHY_REG_SPACE_USXGMII);
>> + if (addr < 0)
>> + return res;
>> + res = yt921x_intif_read(priv, addr, 0x0, &data);
>> + if (res)
>> + return res;
>> + data |= 1 << 15;
>> + res = yt921x_intif_write(priv, addr, 0x0, data);
>> + if (res)
>> + return res;
> Numerous magic numbers. On YT9215, same finetunes are not required for
> a usable port. If you cannot name it, drop it if it is not mandatory.
Ans: Some patches are mandatory, while others are for performance
optimization.
These will be further refined/optimized in the future.
>
>> +static int yt922x_cpu_tag_mode_set(struct yt921x_priv *priv)
>> +{
>> + struct device *dev = to_device(priv);
>> + u16 eth_p_tag;
>> + u32 val;
>> + u32 val1;
>> + int res;
>> +
>> + /* cpu tag mode set */
>> + 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;
>> +
>> + /* tpid check */
>> + res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
>> + if (res)
>> + return res;
>> + eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
>> + if (eth_p_tag != ETH_P_YT921X) {
>> + dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
>> + ETH_P_YT921X);
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
> This looks similar to yt921x_chip_reset(), so you'd better merge it
> into yt922x_chip_reset() above.
Ans:Given that this interface is responsible for mode selection related
to CPU tags, the cpu tag
TPID check has been consolidated here as well.
>
>> +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(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;
>> +}
> Also, snippets from yt921x_chip_setup_dsa() and unaligned function names.
>
>> -static void yt921x_mdio_remove(struct mdio_device *mdiodev)
>> +static void yt92xx_mdio_remove(struct mdio_device *mdiodev)
> Unnecessary renaming.
>
>> +#define YT922X_PORT_SDS_MODE 0x400
>> +#define YT922X_PORT_SDS_MODE_M GENMASK(6, 4)
>> +#define YT92XX_SERDES_MODE_SGMII 0
>> +#define YT92XX_SERDES_MODE_REVSGMII 1
>> +#define YT92XX_SERDES_MODE_1000BASEX 2
>> +#define YT92XX_SERDES_MODE_100BASEX 3
>> +#define YT92XX_SERDES_MODE_2500BASEX 4
>> +#define YT92XX_SERDES_MODE_USXGMII 6
> Not an objection, but better to keep the formats with YT921X_SERDESn:
Ans: fix done in patch v2.
>
> #define YT922X_PORT_SERDES 0x400
> #define YT922X_PORT_SERDES_MODE_M GENMASK(6, 4)
> #define YT922X_PORT_SERDES_MODE(x)
> FIELD_PREP(YT922X_PORT_SERDES_MODE_M, (x))
> #define YT922X_PORT_SERDES_MODE_SGMII YT922X_PORT_SERDES_MODE(0)
>
>> +#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_TRAP(port) YT922X_ACT_UNK_ACTn(port, 3) /* steer to CPU */
>> +#define YT922X_ACT_UNK_ACTn_DROP(port) YT922X_ACT_UNK_ACTn(port, 1) /* discard */
>> +/* NEVER use this action; see comments in the tag driver */
>> +#define YT922X_ACT_UNK_ACTn_COPY(port) YT922X_ACT_UNK_ACTn(port, 2) /* flood and copy */
> Sort them in numerical order.
Ans: Fix done.
>
>> +
>> +/* CPU PORT */
>> +#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)
> Not an objection, but better to keep the indentation formats of yt921x
> register definitions above.
>
>> +struct yt92xx_chip_info {
>> + enum yt92xx_mode mode;
>> + const char *name;
>> + unsigned int ports;
>> + unsigned int lag_id;
> lag_ids, or num_lag_ids (and num_ports) for consistency.
>
>> struct yt921x_priv {
>> struct dsa_switch ds;
>>
>> + const struct yt92xx_chip_info *chip_info;
>> const struct yt921x_info *info;
> yt921x_priv :: info is chip info already. You may want `series_info`,
> or more straightforward, `series` and `struct yt92xx_series`.
Ans: Fix done.
>
>> diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c
>> index 294784ab6694..a3012ec39868 100644
>> --- a/net/dsa/tag_yt921x.c
>> +++ b/net/dsa/tag_yt921x.c
> [...]
>
>> +/* To define the from cpu tag format 8 bytes:
>> + *
>> + * 0 1 2 3 4 5 6 7 |0 1 2 3 4 5 6 7
>> + *|<------------TPID 0x9988------->|
>> + *|<--RESERVE-->|<-----DST POR---->|
>> + *|-|<---------RESERVE------------>|
>> + *|<------------------------------>|
>> + */
>> +#define YT922X_TAG_NAME "yt922x"
>> +#define YT922X_TAG_PORTMASK_0 BIT(15)
> Sort them according to their corresponding word.
>
>> +#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)
>> +#define YT922X_TAG_RX_PRIO_M GENMASK(15, 13)
> This did not answer the question in the previous review: do tag_yt922x
> and tag_yt921x share any common routines? If not, better to make
> tag_yt922x a separate file.
Ans: Fix done.
>
>> +static struct sk_buff *
>> +yt922x_tag_xmit(struct sk_buff *skb, struct net_device *netdev)
>> +{
>> + struct dsa_port *dp = dsa_user_to_port(netdev);
>> + __be16 *tag;
>> + u16 ctrl;
>> +
>> + skb_push(skb, YT921X_TAG_LEN);
>> + dsa_alloc_etype_header(skb, YT921X_TAG_LEN);
>> + tag = dsa_etype_header_pos_tx(skb);
>> +
>> + tag[0] = htons(ETH_P_YT921X);
>> + if (dp->index != 0) {
>> + /* Port index is not equal 0 in tag[1] */
>> + ctrl = YT922X_TAG_PRIO(skb->priority) | YT922X_TAG_FORCE_DST |
>> + YT922X_TAG_PORTS(dsa_xmit_port_mask(skb, netdev) - 1);
> dsa_xmit_port_mask() returns a bit mask - you won't do arithmetic
> operations to a bit mask normally.
>
> The field name does suggest it is a port mask. Have you noticed
> excessive packets on other ports?
Ans: yes, There are some issues here that will be fixed in patch v2.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
2026-08-17 10:40 ` Kyle Switch
@ 2026-08-17 13:12 ` Andrew Lunn
2026-08-20 1:33 ` Kyle Switch
2026-08-17 21:48 ` David Yang
1 sibling, 1 reply; 6+ messages in thread
From: Andrew Lunn @ 2026-08-17 13:12 UTC (permalink / raw)
To: Kyle Switch
Cc: David Yang, olteanv, davem, edumazet, kuba, pabeni, horms, linux,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang
On Mon, Aug 17, 2026 at 06:40:02PM +0800, Kyle Switch wrote:
>
> On 8/17/26 05:17, David Yang wrote:
> > On Fri, Aug 14, 2026 at 9:50 AM Kyle Switch <kyle.switch@motor-comm.com> wrote:
> >
> > > @@ -148,10 +148,15 @@ static const struct yt921x_info yt921x_infos[] = {
> > > YT921X_PORT_MASK_INT0_n(8),
> > > YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
> > > },
> > > + {
> > > + "YT9224", YT9224_MAJOR, 0, 0,
> > > + 0x1f1,
> > > + 0x0,
> > > + },
> > Why plain magic numbers here, despite what the above entries shows?
> Ans: will fixed done in patch v2.
> >
> > > {}
> > > };
> > >
> > > -#define YT921X_NAME "yt921x"
> > > +#define YT92XX_NAME "yt92xx"
> > This changed the module name which may affect end users. While I have
> > no objection to this, subsystem maintainers may have different
> > opinions.
> Ans: This is used to indicate support for both YT921X and YT922X.
It is pretty common for newer generations of devices to be mostly
backwards compatible, with older generations. The driver does not
change name in Linux it causes too much pointless churn. Please just
get the names correct in the struct phy_driver.
> > > + res = yt921x_intif_read(priv, addr, 0x0, &data);
> > > + if (res)
> > > + return res;
> > > + data |= 1 << 15;
> > > + res = yt921x_intif_write(priv, addr, 0x0, data);
> > > + if (res)
> > > + return res;
> > Numerous magic numbers. On YT9215, same finetunes are not required for
> > a usable port. If you cannot name it, drop it if it is not mandatory.
>
> Ans: Some patches are mandatory
If they are mandatory, why are they needed at all? Why are they not in
the firmware. Seems like a poor hardware/firmware design to me.
> while others are for performance optimization.
For these, it would be good to fully document them, because board
designers might want to optimze them in other ways.
> > > -static void yt921x_mdio_remove(struct mdio_device *mdiodev)
> > > +static void yt92xx_mdio_remove(struct mdio_device *mdiodev)
> > Unnecessary renaming.
And taking this to its logical conclusion...
In a few years time you want to rename to yt9xxx. And a few years
after that, rename to ytxxxx.
Andrew
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
2026-08-17 10:40 ` Kyle Switch
2026-08-17 13:12 ` Andrew Lunn
@ 2026-08-17 21:48 ` David Yang
1 sibling, 0 replies; 6+ messages in thread
From: David Yang @ 2026-08-17 21:48 UTC (permalink / raw)
To: Kyle Switch
Cc: andrew, olteanv, davem, edumazet, kuba, pabeni, horms, linux,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang
On Mon, Aug 17, 2026 at 6:40 PM Kyle Switch <kyle.switch@motor-comm.com> wrote:
> >> +static int yt922x_cpu_tag_mode_set(struct yt921x_priv *priv)
> >> +{
> >> + struct device *dev = to_device(priv);
> >> + u16 eth_p_tag;
> >> + u32 val;
> >> + u32 val1;
> >> + int res;
> >> +
> >> + /* cpu tag mode set */
> >> + 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;
> >> +
> >> + /* tpid check */
> >> + res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
> >> + if (res)
> >> + return res;
> >> + eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
> >> + if (eth_p_tag != ETH_P_YT921X) {
> >> + dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
> >> + ETH_P_YT921X);
> >> + return -EINVAL;
> >> + }
> >> +
> >> + return 0;
> >> +}
> > This looks similar to yt921x_chip_reset(), so you'd better merge it
> > into yt922x_chip_reset() above.
>
> Ans:Given that this interface is responsible for mode selection related
> to CPU tags, the cpu tag
>
> TPID check has been consolidated here as well.
TPID does not depend on the tag mode, so this is a one-time validation
right after resetting in yt921x_chip_reset().
Also the naming is confusing: why `mode_set` clears the `RX/TX_MODE`.
It would be self-explanatory to rename it to `set_8b` and
`RX/TX_TAG_4B`, and/or mention the existence of 4b mode in the comment
for tag_yt922x as well.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
2026-08-17 13:12 ` Andrew Lunn
@ 2026-08-20 1:33 ` Kyle Switch
0 siblings, 0 replies; 6+ messages in thread
From: Kyle Switch @ 2026-08-20 1:33 UTC (permalink / raw)
To: Andrew Lunn
Cc: David Yang, olteanv, davem, edumazet, kuba, pabeni, horms, linux,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang
On 8/17/26 21:12, Andrew Lunn wrote:
> On Mon, Aug 17, 2026 at 06:40:02PM +0800, Kyle Switch wrote:
>> On 8/17/26 05:17, David Yang wrote:
>>> On Fri, Aug 14, 2026 at 9:50 AM Kyle Switch <kyle.switch@motor-comm.com> wrote:
>>>
>>>> @@ -148,10 +148,15 @@ static const struct yt921x_info yt921x_infos[] = {
>>>> YT921X_PORT_MASK_INT0_n(8),
>>>> YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
>>>> },
>>>> + {
>>>> + "YT9224", YT9224_MAJOR, 0, 0,
>>>> + 0x1f1,
>>>> + 0x0,
>>>> + },
>>> Why plain magic numbers here, despite what the above entries shows?
>> Ans: will fixed done in patch v2.
>>>> {}
>>>> };
>>>>
>>>> -#define YT921X_NAME "yt921x"
>>>> +#define YT92XX_NAME "yt92xx"
>>> This changed the module name which may affect end users. While I have
>>> no objection to this, subsystem maintainers may have different
>>> opinions.
>> Ans: This is used to indicate support for both YT921X and YT922X.
> It is pretty common for newer generations of devices to be mostly
> backwards compatible, with older generations. The driver does not
> change name in Linux it causes too much pointless churn. Please just
> get the names correct in the struct phy_driver.
Ans: The modifications in the next version V2 will retain the previous
naming.
>>>> + res = yt921x_intif_read(priv, addr, 0x0, &data);
>>>> + if (res)
>>>> + return res;
>>>> + data |= 1 << 15;
>>>> + res = yt921x_intif_write(priv, addr, 0x0, data);
>>>> + if (res)
>>>> + return res;
>>> Numerous magic numbers. On YT9215, same finetunes are not required for
>>> a usable port. If you cannot name it, drop it if it is not mandatory.
>> Ans: Some patches are mandatory
> If they are mandatory, why are they needed at all? Why are they not in
> the firmware. Seems like a poor hardware/firmware design to me.
Ans: In this application scenario, there is no firmware, and the eFuse
may not be able to support so many registers.
>
>> while others are for performance optimization.
> For these, it would be good to fully document them, because board
> designers might want to optimze them in other ways.
>
>>>> -static void yt921x_mdio_remove(struct mdio_device *mdiodev)
>>>> +static void yt92xx_mdio_remove(struct mdio_device *mdiodev)
>>> Unnecessary renaming.
> And taking this to its logical conclusion...
>
> In a few years time you want to rename to yt9xxx. And a few years
> after that, rename to ytxxxx.
Ans: Considering that the Motorcomm switch series currently all
start with '92', and there will be driver work for other series
in the future,
the name was modified to 'yt92xx'. However, since it is
uncertain whether
there will be 'yt9xxx' in the future, the name has been reverted
to the
existing one for now.
>
> Andrew
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-20 1:33 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14 1:50 [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x Kyle Switch
2026-08-16 21:17 ` David Yang
2026-08-17 10:40 ` Kyle Switch
2026-08-17 13:12 ` Andrew Lunn
2026-08-20 1:33 ` Kyle Switch
2026-08-17 21:48 ` David Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox