* [PATCH net-next v1] net:dsa:yt922x: Add support for Motorcomm YT922x
@ 2026-08-14 1:50 Kyle Switch
0 siblings, 0 replies; only message 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] only message in thread
only message in thread, other threads:[~2026-08-14 1:50 UTC | newest]
Thread overview: (only message) (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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox