diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile index aeb12cb91f93..1d2c1b3064c4 100644 --- a/drivers/net/dsa/motorcomm/Makefile +++ b/drivers/net/dsa/motorcomm/Makefile @@ -2,4 +2,6 @@ obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o yt921x-objs := chip.o yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o +yt921x-objs += mdio_bus.o +yt921x-objs += pcs-921x.o yt921x-objs += smi.o diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c index 8b0ac5fc8c8b..3ff2de383157 100644 --- a/drivers/net/dsa/motorcomm/chip.c +++ b/drivers/net/dsa/motorcomm/chip.c @@ -27,6 +27,8 @@ #include "chip.h" #include "leds.h" +#include "mdio_bus.h" +#include "pcs.h" #include "smi.h" struct yt921x_mib_desc { @@ -96,58 +98,48 @@ static const struct yt921x_mib_desc yt921x_mib_descs[] = { MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"), }; -struct yt921x_info { - const char *name; - u16 major; - /* Unknown, seems to be plain enumeration */ - u8 mode; - u8 extmode; - /* Ports with integral GbE PHYs, not including MCU Port 10 */ - u16 internal_mask; - /* TODO: see comments in yt921x_dsa_phylink_get_caps() */ - u16 external_mask; -}; - -#define YT921X_PORT_MASK_INTn(port) BIT(port) -#define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0) -#define YT921X_PORT_MASK_EXT0 BIT(8) -#define YT921X_PORT_MASK_EXT1 BIT(9) - static const struct yt921x_info yt921x_infos[] = { { "YT9215SC", YT9215_MAJOR, 1, 0, - YT921X_PORT_MASK_INT0_n(5), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + GENMASK(4, 0), + BIT(9), + BIT(8) | BIT(9), }, { "YT9215S", YT9215_MAJOR, 2, 0, - YT921X_PORT_MASK_INT0_n(5), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + GENMASK(4, 0), + BIT(9), + BIT(8), }, { "YT9215RB", YT9215_MAJOR, 3, 0, - YT921X_PORT_MASK_INT0_n(5), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + GENMASK(4, 0), + BIT(8) | BIT(9), + 0, }, { "YT9214NB", YT9215_MAJOR, 3, 2, - YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + BIT(1) | BIT(3), + BIT(9), + BIT(8), }, { "YT9213NB", YT9215_MAJOR, 3, 3, - YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), - YT921X_PORT_MASK_EXT1, + BIT(1) | BIT(3), + BIT(9), + BIT(9), }, { "YT9218N", YT9218_MAJOR, 0, 0, - YT921X_PORT_MASK_INT0_n(8), + GENMASK(7, 0), + 0, 0, }, { "YT9218MB", YT9218_MAJOR, 1, 0, - YT921X_PORT_MASK_INT0_n(8), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + GENMASK(7, 0), + BIT(8) | BIT(9), + BIT(8) | BIT(9), }, {} }; @@ -276,274 +268,6 @@ static const struct yt921x_reg_ops yt921x_reg_ops_mdio = { /* TODO: SPI/I2C */ -static int yt921x_intif_wait(struct yt921x_priv *priv) -{ - u32 val = 0; - - return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START, - &val); -} - -static int -yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) -{ - struct device *dev = to_device(priv); - u32 mask; - u32 ctrl; - u32 val; - int res; - - res = yt921x_intif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_READ; - res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - res = yt921x_intif_wait(priv); - if (res) - return res; - res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val); - if (res) - return res; - - if ((u16)val != val) - dev_info(dev, - "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", - __func__, port, reg, val); - *valp = (u16)val; - return 0; -} - -static int -yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val) -{ - u32 mask; - u32 ctrl; - int res; - - res = yt921x_intif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_WRITE; - res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - return yt921x_intif_wait(priv); -} - -static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) -{ - struct yt921x_priv *priv = mbus->priv; - u16 val; - int res; - - if (port >= YT921X_PORT_NUM) - return U16_MAX; - - mutex_lock(&priv->reg_lock); - res = yt921x_intif_read(priv, port, reg, &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - return val; -} - -static int -yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data) -{ - struct yt921x_priv *priv = mbus->priv; - int res; - - if (port >= YT921X_PORT_NUM) - return -ENODEV; - - mutex_lock(&priv->reg_lock); - res = yt921x_intif_write(priv, port, reg, data); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) -{ - struct device *dev = to_device(priv); - struct mii_bus *mbus; - int res; - - mbus = devm_mdiobus_alloc(dev); - if (!mbus) - return -ENOMEM; - - mbus->name = "YT921x internal MDIO bus"; - snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); - mbus->priv = priv; - mbus->read = yt921x_mbus_int_read; - mbus->write = yt921x_mbus_int_write; - mbus->parent = dev; - mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0); - - res = devm_of_mdiobus_register(dev, mbus, mnp); - if (res) - return res; - - priv->mbus_int = mbus; - - return 0; -} - -static int yt921x_extif_wait(struct yt921x_priv *priv) -{ - u32 val = 0; - - return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START, - &val); -} - -static int -yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) -{ - struct device *dev = to_device(priv); - u32 mask; - u32 ctrl; - u32 val; - int res; - - res = yt921x_extif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ; - res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - res = yt921x_extif_wait(priv); - if (res) - return res; - res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val); - if (res) - return res; - - if ((u16)val != val) - dev_info(dev, - "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", - __func__, port, reg, val); - *valp = (u16)val; - return 0; -} - -static int -yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val) -{ - u32 mask; - u32 ctrl; - int res; - - res = yt921x_extif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE; - res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - return yt921x_extif_wait(priv); -} - -static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg) -{ - struct yt921x_priv *priv = mbus->priv; - u16 val; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_extif_read(priv, port, reg, &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - return val; -} - -static int -yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data) -{ - struct yt921x_priv *priv = mbus->priv; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_extif_write(priv, port, reg, data); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) -{ - struct device *dev = to_device(priv); - struct mii_bus *mbus; - int res; - - mbus = devm_mdiobus_alloc(dev); - if (!mbus) - return -ENOMEM; - - mbus->name = "YT921x external MDIO bus"; - snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev)); - mbus->priv = priv; - /* TODO: c45? */ - mbus->read = yt921x_mbus_ext_read; - mbus->write = yt921x_mbus_ext_write; - mbus->parent = dev; - - res = devm_of_mdiobus_register(dev, mbus, mnp); - if (res) - return res; - - priv->mbus_ext = mbus; - - return 0; -} - /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */ static int yt921x_read_mib(struct yt921x_priv *priv, int port) { @@ -608,9 +332,8 @@ static void yt921x_poll_mib(struct work_struct *work) { struct yt921x_port *pp = container_of_const(work, struct yt921x_port, mib_read.work); - struct yt921x_priv *priv = (void *)(pp - pp->index) - - offsetof(struct yt921x_priv, ports); unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES; + struct yt921x_priv *priv = yt921x_port_to_priv(pp); int port = pp->index; int res; @@ -3748,20 +3471,23 @@ yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, static int yt921x_port_down(struct yt921x_priv *priv, int port) { + const struct yt921x_info *info = priv->info; u32 mask; int res; - mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; + mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_RX_MAC_EN | + YT921X_PORT_TX_MAC_EN; res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask); if (res) return res; - if (yt921x_port_is_external(port)) { + if (BIT(port) & info->serdes_mask) { mask = YT921X_SERDES_LINK; res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask); if (res) return res; - + } + if (BIT(port) & info->xmii_mask) { mask = YT921X_XMII_LINK; res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask); if (res) @@ -3776,29 +3502,21 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause) { + const struct yt921x_info *info = priv->info; + struct yt921x_port *pp = &priv->ports[port]; + int ps = ethtool_speed_to_yt921x(speed); u32 mask; u32 ctrl; int res; - switch (speed) { - case SPEED_10: - ctrl = YT921X_PORT_SPEED_10; - break; - case SPEED_100: - ctrl = YT921X_PORT_SPEED_100; - break; - case SPEED_1000: - ctrl = YT921X_PORT_SPEED_1000; - break; - case SPEED_2500: - ctrl = YT921X_PORT_SPEED_2500; - break; - case SPEED_10000: - ctrl = YT921X_PORT_SPEED_10000; - break; - default: + if (ps == YT921X_SPEED_INVALID) return -EINVAL; - } + + mask = YT921X_PORT_SPEED_M | YT921X_PORT_TX_MAC_EN | + YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_PAUSE | + YT921X_PORT_RX_PAUSE | YT921X_PORT_DUPLEX_FULL | + YT921X_PORT_CTRL_LINK_AN; + ctrl = YT921X_PORT_SPEED(ps); if (duplex == DUPLEX_FULL) ctrl |= YT921X_PORT_DUPLEX_FULL; if (tx_pause) @@ -3806,31 +3524,18 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, if (rx_pause) ctrl |= YT921X_PORT_RX_PAUSE; ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; - res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl); + if (pp->serdes && pp->inband) + ctrl |= YT921X_PORT_CTRL_LINK_AN; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl); if (res) return res; - if (yt921x_port_is_external(port)) { + if (!(BIT(port) & (info->serdes_mask | info->xmii_mask))) + return 0; + + if (pp->serdes) { mask = YT921X_SERDES_SPEED_M; - switch (speed) { - case SPEED_10: - ctrl = YT921X_SERDES_SPEED_10; - break; - case SPEED_100: - ctrl = YT921X_SERDES_SPEED_100; - break; - case SPEED_1000: - ctrl = YT921X_SERDES_SPEED_1000; - break; - case SPEED_2500: - ctrl = YT921X_SERDES_SPEED_2500; - break; - case SPEED_10000: - ctrl = YT921X_SERDES_SPEED_10000; - break; - default: - return -EINVAL; - } + ctrl = YT921X_SERDES_SPEED(ps); mask |= YT921X_SERDES_DUPLEX_FULL; if (duplex == DUPLEX_FULL) ctrl |= YT921X_SERDES_DUPLEX_FULL; @@ -3841,36 +3546,19 @@ yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, if (rx_pause) ctrl |= YT921X_SERDES_RX_PAUSE; mask |= YT921X_SERDES_LINK; - ctrl |= YT921X_SERDES_LINK; + if (!pp->inband) + ctrl |= YT921X_SERDES_LINK; res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), mask, ctrl); if (res) return res; - + } else { mask = YT921X_XMII_LINK; res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask); if (res) return res; - switch (speed) { - case SPEED_10: - ctrl = YT921X_MDIO_POLLING_SPEED_10; - break; - case SPEED_100: - ctrl = YT921X_MDIO_POLLING_SPEED_100; - break; - case SPEED_1000: - ctrl = YT921X_MDIO_POLLING_SPEED_1000; - break; - case SPEED_2500: - ctrl = YT921X_MDIO_POLLING_SPEED_2500; - break; - case SPEED_10000: - ctrl = YT921X_MDIO_POLLING_SPEED_10000; - break; - default: - return -EINVAL; - } + ctrl = YT921X_MDIO_POLLING_SPEED(ps); if (duplex == DUPLEX_FULL) ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL; ctrl |= YT921X_MDIO_POLLING_LINK; @@ -3886,17 +3574,15 @@ static int yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, phy_interface_t interface) { + const struct yt921x_info *info = priv->info; + struct yt921x_port *pp = &priv->ports[port]; struct device *dev = to_device(priv); u32 mask; - u32 ctrl; int res; - if (!yt921x_port_is_external(port)) { - if (interface != PHY_INTERFACE_MODE_INTERNAL) { - dev_err(dev, "Wrong mode %d on port %d\n", - interface, port); - return -EINVAL; - } + if (BIT(port) & info->internal_mask) { + if (interface != PHY_INTERFACE_MODE_INTERNAL) + goto err; return 0; } @@ -3906,6 +3592,9 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, case PHY_INTERFACE_MODE_100BASEX: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: + if (!(BIT(port) & info->serdes_mask)) + goto err; + mask = YT921X_SERDES_CTRL_PORTn(port); res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask); if (res) @@ -3916,35 +3605,45 @@ yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, if (res) return res; - mask = YT921X_SERDES_MODE_M; - switch (interface) { - case PHY_INTERFACE_MODE_SGMII: - ctrl = YT921X_SERDES_MODE_SGMII; - break; - case PHY_INTERFACE_MODE_100BASEX: - ctrl = YT921X_SERDES_MODE_100BASEX; - break; - case PHY_INTERFACE_MODE_1000BASEX: - ctrl = YT921X_SERDES_MODE_1000BASEX; - break; - case PHY_INTERFACE_MODE_2500BASEX: - ctrl = YT921X_SERDES_MODE_2500BASEX; - break; - default: - return -EINVAL; - } - res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), - mask, ctrl); - if (res) - return res; + /* The order is quite arbitrary - we can't return to a safe + * state on IO errors. + */ + pp->serdes = true; break; /* add XMII support here */ default: - return -EINVAL; + goto err; } return 0; + +err: + dev_err(dev, "Wrong mode %d on port %d\n", interface, port); + return -EINVAL; +} + +static struct phylink_pcs * +yt921x_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = to_yt921x_priv(dp->ds); + const struct yt921x_info *info = priv->info; + int port = dp->index; + + if (!(BIT(port) & info->serdes_mask)) + return NULL; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return &priv->ports[port].pcs; + default: + return NULL; + } } static void @@ -4026,15 +3725,10 @@ yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, */ __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); - } else if (info->external_mask & BIT(port)) { - /* TODO: external ports may support SERDES only, XMII only, or - * SERDES + XMII depending on the chip. However, we can't get - * the accurate config table due to lack of document, thus - * we simply declare SERDES + XMII and rely on the correctness - * of devicetree for now. - */ + return; + } - /* SERDES */ + if (BIT(port) & info->serdes_mask) { __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); /* REVSGMII (SGMII in PHY role) should go here, once @@ -4047,9 +3741,8 @@ yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces); config->mac_capabilities |= MAC_2500FD; - - /* XMII */ - + } + if (BIT(port) & info->xmii_mask) { /* Not tested. To add support for XMII: * - Add proper interface modes below * - Handle them in yt921x_port_config() @@ -4540,6 +4233,11 @@ static int yt921x_chip_setup(struct yt921x_priv *priv) if (res) return res; + res = yt921x_reg_clear_bits(priv, YT921X_SERDES_CTRL, + YT921X_SERDES_CTRL_TEST); + if (res) + return res; + return 0; } @@ -4549,6 +4247,8 @@ static int yt921x_dsa_setup(struct dsa_switch *ds) struct device *dev = to_device(priv); struct device_node *np = dev->of_node; struct device_node *child; + unsigned long mask; + int port; int res; mutex_lock(&priv->reg_lock); @@ -4582,6 +4282,23 @@ static int yt921x_dsa_setup(struct dsa_switch *ds) return -ENODEV; } + mask = priv->info->serdes_mask; + for_each_set_bit(port, &mask, YT921X_PORT_NUM) { + struct yt921x_port *pp = &priv->ports[port]; + + pp->pcs.ops = &yt921x_phylink_pcs_ops; + pp->pcs.poll = true; + + __set_bit(PHY_INTERFACE_MODE_SGMII, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_100BASEX, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + pp->pcs.supported_interfaces); + } + mutex_lock(&priv->reg_lock); res = yt921x_chip_setup(priv); mutex_unlock(&priv->reg_lock); @@ -4599,6 +4316,7 @@ static int yt921x_dsa_setup(struct dsa_switch *ds) } static const struct phylink_mac_ops yt921x_phylink_mac_ops = { + .mac_select_pcs = yt921x_phylink_mac_select_pcs, .mac_link_down = yt921x_phylink_mac_link_down, .mac_link_up = yt921x_phylink_mac_link_up, .mac_config = yt921x_phylink_mac_config, diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h index 83cd454955dd..28e4f26f9cc4 100644 --- a/drivers/net/dsa/motorcomm/chip.h +++ b/drivers/net/dsa/motorcomm/chip.h @@ -42,7 +42,7 @@ #define YT921X_PVID_SEL 0x80014 #define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port) #define YT921X_SERDES_CTRL 0x80028 -#define YT921X_SERDES_CTRL_PORTn_TEST(port) BIT((port) - 3) +#define YT921X_SERDES_CTRL_TEST BIT(6) #define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8) #define YT921X_IO_LEVEL 0x80030 #define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4) @@ -89,16 +89,12 @@ #define YT921X_SERDES_DUPLEX_FULL BIT(3) #define YT921X_SERDES_SPEED_M GENMASK(2, 0) #define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x)) -#define YT921X_SERDES_SPEED_10 YT921X_SERDES_SPEED(0) -#define YT921X_SERDES_SPEED_100 YT921X_SERDES_SPEED(1) -#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2) -#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3) -#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4) -#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) +#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) /* Bit 7-0 shared with PORT_STATUS */ #define YT921X_PORT_CTRL_PAUSE_AN BIT(10) +#define YT921X_PORT_CTRL_LINK_AN BIT(9) +#define YT921X_PORT_CTRL_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */ #define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port)) -#define YT921X_PORT_LINK BIT(9) /* CTRL: auto negotiation */ -#define YT921X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */ +#define YT921X_PORT_LINK BIT(8) #define YT921X_PORT_DUPLEX_FULL BIT(7) #define YT921X_PORT_RX_PAUSE BIT(6) #define YT921X_PORT_TX_PAUSE BIT(5) @@ -106,11 +102,6 @@ #define YT921X_PORT_TX_MAC_EN BIT(3) #define YT921X_PORT_SPEED_M GENMASK(2, 0) #define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x)) -#define YT921X_PORT_SPEED_10 YT921X_PORT_SPEED(0) -#define YT921X_PORT_SPEED_100 YT921X_PORT_SPEED(1) -#define YT921X_PORT_SPEED_1000 YT921X_PORT_SPEED(2) -#define YT921X_PORT_SPEED_10000 YT921X_PORT_SPEED(3) -#define YT921X_PORT_SPEED_2500 YT921X_PORT_SPEED(4) #define YT921X_PON_STRAP_FUNC 0x80320 #define YT921X_PON_STRAP_VAL 0x80324 #define YT921X_PON_STRAP_CAP 0x80328 @@ -121,11 +112,16 @@ #define YT921X_MDIO_POLLING_LINK BIT(3) #define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0) #define YT921X_MDIO_POLLING_SPEED(x) FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x)) -#define YT921X_MDIO_POLLING_SPEED_10 YT921X_MDIO_POLLING_SPEED(0) -#define YT921X_MDIO_POLLING_SPEED_100 YT921X_MDIO_POLLING_SPEED(1) -#define YT921X_MDIO_POLLING_SPEED_1000 YT921X_MDIO_POLLING_SPEED(2) -#define YT921X_MDIO_POLLING_SPEED_10000 YT921X_MDIO_POLLING_SPEED(3) -#define YT921X_MDIO_POLLING_SPEED_2500 YT921X_MDIO_POLLING_SPEED(4) + +enum yt921x_speed { + YT921X_SPEED_10, + YT921X_SPEED_100, + YT921X_SPEED_1000, + YT921X_SPEED_10000, + YT921X_SPEED_2500, + YT921X_SPEED_INVALID +}; + #define YT921X_SENSOR 0x8036c #define YT921X_SENSOR_TEMP BIT(18) #define YT921X_TEMP 0x80374 @@ -247,27 +243,6 @@ #define YT9218_SYS_CLK_167M 0 #define YT921X_SYS_CLK_143M 1 -#define YT921X_EXT_MBUS_OP 0x6a000 -#define YT921X_INT_MBUS_OP 0xf0000 -#define YT921X_MBUS_OP_START BIT(0) -#define YT921X_EXT_MBUS_CTRL 0x6a004 -#define YT921X_INT_MBUS_CTRL 0xf0004 -#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21) -#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x)) -#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16) -#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x)) -#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */ -#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x)) -#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4) -#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */ -#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x)) -#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1) -#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2) -#define YT921X_EXT_MBUS_DOUT 0x6a008 -#define YT921X_INT_MBUS_DOUT 0xf0008 -#define YT921X_EXT_MBUS_DIN 0x6a00c -#define YT921X_INT_MBUS_DIN 0xf000c - #define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port)) #define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4) #define YT921X_PORT_EGR_TPID_CTAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x)) @@ -858,9 +833,6 @@ enum yt921x_fdb_entry_status { #define YT921X_NAME "yt921x" -#define yt921x_port_is_internal(port) ((port) < 8) -#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9) - struct yt921x_mib { u64 rx_broadcast; u64 rx_pause; @@ -936,8 +908,14 @@ struct yt921x_acl_blk { struct yt921x_port { unsigned char index; - bool hairpin; - bool isolated; + /* SerDes in use */ + bool serdes:1; + /* Link from in-band status (PHYLINK_PCS_NEG_INBAND) */ + bool inband:1; + /* BR_HAIRPIN_MODE */ + bool hairpin:1; + /* BR_ISOLATED */ + bool isolated:1; struct delayed_work mib_read; struct yt921x_mib mib; @@ -953,13 +931,30 @@ struct yt921x_port { struct yt921x_led *leds[YT921X_LED_GROUP_NUM]; #endif + + struct phylink_pcs pcs; }; +#define pcs_to_yt921x_port(_pcs) container_of((_pcs), struct yt921x_port, pcs) + struct yt921x_reg_ops { int (*read)(void *context, u32 reg, u32 *valp); int (*write)(void *context, u32 reg, u32 val); }; +struct yt921x_info { + const char *name; + u16 major; + /* Unknown, seems to be plain enumeration */ + u8 mode; + u8 extmode; + /* Ports with integral GbE PHYs, not including MCU Port 10 */ + u16 internal_mask; + /* Note: xmii_mask and serdes_mask may overlap */ + u16 xmii_mask; + u16 serdes_mask; +}; + struct yt921x_priv { struct dsa_switch ds; @@ -989,4 +984,25 @@ struct yt921x_priv { struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM]; }; +#define yt921x_port_to_priv(pp) \ + container_of_const((pp), struct yt921x_priv, ports[(pp)->index]) + +static inline int ethtool_speed_to_yt921x(int speed) +{ + switch (speed) { + case SPEED_10: + return YT921X_SPEED_10; + case SPEED_100: + return YT921X_SPEED_100; + case SPEED_1000: + return YT921X_SPEED_1000; + case SPEED_2500: + return YT921X_SPEED_2500; + case SPEED_10000: + return YT921X_SPEED_10000; + default: + return YT921X_SPEED_INVALID; + } +} + #endif diff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c index 0e8761f37995..6613fe3b0471 100644 --- a/drivers/net/dsa/motorcomm/leds.c +++ b/drivers/net/dsa/motorcomm/leds.c @@ -12,8 +12,6 @@ #define to_yt921x_led(led_cdev) \ container_of_const((led_cdev), struct yt921x_led, cdev) #define to_yt921x_port(led) ((led)->port) -#define to_yt921x_priv(pp) \ - container_of_const((pp), struct yt921x_priv, ports[(pp)->index]) #define to_device(priv) ((priv)->ds.dev) static u32 yt921x_led_regaddr(struct yt921x_priv *priv, int port, int group) @@ -321,7 +319,7 @@ yt921x_cled_brightness_set_blocking(struct led_classdev *led_cdev, { struct yt921x_led *led = to_yt921x_led(led_cdev); struct yt921x_port *pp = to_yt921x_port(led); - struct yt921x_priv *priv = to_yt921x_priv(pp); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); int res; mutex_lock(&priv->reg_lock); @@ -337,7 +335,7 @@ yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, { struct yt921x_led *led = to_yt921x_led(led_cdev); struct yt921x_port *pp = to_yt921x_port(led); - struct yt921x_priv *priv = to_yt921x_priv(pp); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); int res; mutex_lock(&priv->reg_lock); @@ -353,7 +351,7 @@ yt921x_cled_hw_control_get_device(struct led_classdev *led_cdev) { struct yt921x_led *led = to_yt921x_led(led_cdev); struct yt921x_port *pp = to_yt921x_port(led); - struct yt921x_priv *priv = to_yt921x_priv(pp); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); struct dsa_port *dp; dp = dsa_to_port(&priv->ds, pp->index); @@ -374,7 +372,7 @@ yt921x_cled_hw_control_is_supported(struct led_classdev *led_cdev, { struct yt921x_led *led = to_yt921x_led(led_cdev); struct yt921x_port *pp = to_yt921x_port(led); - struct yt921x_priv *priv = to_yt921x_priv(pp); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); if (yt921x_led_trigger_is_supported(priv, pp->index, led->group, flags)) return 0; @@ -386,7 +384,7 @@ yt921x_cled_hw_control_get(struct led_classdev *led_cdev, unsigned long *flagsp) { struct yt921x_led *led = to_yt921x_led(led_cdev); struct yt921x_port *pp = to_yt921x_port(led); - struct yt921x_priv *priv = to_yt921x_priv(pp); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); int res; mutex_lock(&priv->reg_lock); @@ -401,7 +399,7 @@ yt921x_cled_hw_control_set(struct led_classdev *led_cdev, unsigned long flags) { struct yt921x_led *led = to_yt921x_led(led_cdev); struct yt921x_port *pp = to_yt921x_port(led); - struct yt921x_priv *priv = to_yt921x_priv(pp); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); int res; mutex_lock(&priv->reg_lock); diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c new file mode 100644 index 000000000000..1a3f3cc68275 --- /dev/null +++ b/drivers/net/dsa/motorcomm/mdio_bus.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include + +#include "chip.h" +#include "mdio_bus.h" +#include "smi.h" + +#define to_device(priv) ((priv)->ds.dev) + +static int yt921x_intif_wait(struct yt921x_priv *priv) +{ + u32 val = 0; + + return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START, + &val); +} + +int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + struct device *dev = to_device(priv); + u32 mask; + u32 ctrl; + u32 val; + int res; + + res = yt921x_intif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_READ; + res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + res = yt921x_intif_wait(priv); + if (res) + return res; + res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val); + if (res) + return res; + + if ((u16)val != val) + dev_info(dev, + "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", + __func__, port, reg, val); + *valp = (u16)val; + return 0; +} + +int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + u32 mask; + u32 ctrl; + int res; + + res = yt921x_intif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_WRITE; + res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + return yt921x_intif_wait(priv); +} + +int +yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg, + u16 mask, u16 val) +{ + int res; + u16 v; + u16 u; + + res = yt921x_intif_read(priv, port, reg, &v); + if (res) + return res; + + u = v; + u &= ~mask; + u |= val; + if (u == v) + return 0; + + res = yt921x_intif_write(priv, port, reg, u); + if (res) + return res; + + return 1; +} + +static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) +{ + struct yt921x_priv *priv = mbus->priv; + u16 val; + int res; + + if (port >= YT921X_PORT_NUM) + return U16_MAX; + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_read(priv, port, reg, &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + return val; +} + +static int +yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data) +{ + struct yt921x_priv *priv = mbus->priv; + int res; + + if (port >= YT921X_PORT_NUM) + return -ENODEV; + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_write(priv, port, reg, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) +{ + struct device *dev = to_device(priv); + struct mii_bus *mbus; + int res; + + mbus = devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name = "YT921x internal MDIO bus"; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + mbus->priv = priv; + mbus->read = yt921x_mbus_int_read; + mbus->write = yt921x_mbus_int_write; + mbus->parent = dev; + mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0); + + res = devm_of_mdiobus_register(dev, mbus, mnp); + if (res) + return res; + + priv->mbus_int = mbus; + + return 0; +} + +static int yt921x_extif_wait(struct yt921x_priv *priv) +{ + u32 val = 0; + + return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START, + &val); +} + +static int +yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + struct device *dev = to_device(priv); + u32 mask; + u32 ctrl; + u32 val; + int res; + + res = yt921x_extif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ; + res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + res = yt921x_extif_wait(priv); + if (res) + return res; + res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val); + if (res) + return res; + + if ((u16)val != val) + dev_info(dev, + "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", + __func__, port, reg, val); + *valp = (u16)val; + return 0; +} + +static int +yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + u32 mask; + u32 ctrl; + int res; + + res = yt921x_extif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE; + res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + return yt921x_extif_wait(priv); +} + +static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg) +{ + struct yt921x_priv *priv = mbus->priv; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_extif_read(priv, port, reg, &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + return val; +} + +static int +yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data) +{ + struct yt921x_priv *priv = mbus->priv; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_extif_write(priv, port, reg, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) +{ + struct device *dev = to_device(priv); + struct mii_bus *mbus; + int res; + + mbus = devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name = "YT921x external MDIO bus"; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev)); + mbus->priv = priv; + /* TODO: c45? */ + mbus->read = yt921x_mbus_ext_read; + mbus->write = yt921x_mbus_ext_write; + mbus->parent = dev; + + res = devm_of_mdiobus_register(dev, mbus, mnp); + if (res) + return res; + + priv->mbus_ext = mbus; + + return 0; +} diff --git a/drivers/net/dsa/motorcomm/mdio_bus.h b/drivers/net/dsa/motorcomm/mdio_bus.h new file mode 100644 index 000000000000..e79b725d435b --- /dev/null +++ b/drivers/net/dsa/motorcomm/mdio_bus.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_MDIO_BUS_H +#define _YT_MDIO_BUS_H + +#include +#include +#include + +#define YT921X_EXT_MBUS_OP 0x6a000 +#define YT921X_INT_MBUS_OP 0xf0000 +#define YT921X_MBUS_OP_START BIT(0) +#define YT921X_EXT_MBUS_CTRL 0x6a004 +#define YT921X_INT_MBUS_CTRL 0xf0004 +#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21) +#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x)) +#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16) +#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x)) +#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */ +#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x)) +#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4) +#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */ +#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x)) +#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1) +#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2) +#define YT921X_EXT_MBUS_DOUT 0x6a008 +#define YT921X_INT_MBUS_DOUT 0xf0008 +#define YT921X_EXT_MBUS_DIN 0x6a00c +#define YT921X_INT_MBUS_DIN 0xf000c + +struct yt921x_priv; + +int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp); +int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val); +int yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg, + u16 mask, u16 val); + +static inline int +yt921x_intif_modify(struct yt921x_priv *priv, int port, int reg, u16 mask, + u16 val) +{ + int res; + + res = yt921x_intif_modify_changed(priv, port, reg, mask, val); + return res >= 0 ? 0 : res; +} + +int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp); +int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp); + +#endif diff --git a/drivers/net/dsa/motorcomm/pcs-921x.c b/drivers/net/dsa/motorcomm/pcs-921x.c new file mode 100644 index 000000000000..47467d803e13 --- /dev/null +++ b/drivers/net/dsa/motorcomm/pcs-921x.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include "chip.h" +#include "mdio_bus.h" +#include "pcs.h" +#include "smi.h" + +#define to_device(priv) ((priv)->ds.dev) + +static int +yt921x_serdes_config(struct yt921x_priv *priv, int port, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, bool permit_pause_to_mac) +{ + bool inband = neg_mode & PHYLINK_PCS_NEG_INBAND; + struct yt921x_port *pp = &priv->ports[port]; + bool changed = false; + u16 bmcr; + u32 mask; + u32 ctrl; + u16 val; + int adv; + int res; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + ctrl = YT921X_SERDES_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_100BASEX: + ctrl = YT921X_SERDES_MODE_100BASEX; + break; + case PHY_INTERFACE_MODE_1000BASEX: + ctrl = YT921X_SERDES_MODE_1000BASEX; + break; + case PHY_INTERFACE_MODE_2500BASEX: + ctrl = YT921X_SERDES_MODE_2500BASEX; + break; + default: + return -EOPNOTSUPP; + } + + res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), + YT921X_SERDES_MODE_M, ctrl); + if (res) + return res; + + mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_CTRL_PAUSE_AN; + ctrl = 0; + if (inband) + ctrl |= YT921X_PORT_CTRL_LINK_AN; + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && permit_pause_to_mac && + interface != PHY_INTERFACE_MODE_SGMII) + ctrl |= YT921X_PORT_CTRL_PAUSE_AN; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl); + if (res) + return res; + + if (inband) { + res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), + YT921X_SERDES_LINK); + if (res) + return res; + } + + adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); + if (adv >= 0) { + res = yt921x_intif_modify_changed(priv, port, MII_ADVERTISE, + U16_MAX, adv); + if (res < 0) + return res; + + changed = !!res; + } + + res = yt921x_intif_read(priv, port, MII_BMCR, &val); + if (res) + return res; + + bmcr = val; + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + bmcr |= BMCR_ANENABLE; + else + bmcr &= ~BMCR_ANENABLE; + + /* If the ANENABLE bit was changed, the PHY will restart negotiation, + * so we don't need to flag a change to trigger its own restart. + */ + if (bmcr != val) + changed = false; + + bmcr &= ~BMCR_ISOLATE; + res = yt921x_intif_write(priv, port, MII_BMCR, bmcr); + if (res) + return res; + + pp->inband = inband; + return changed; +} + +static unsigned int +yt921x_phylink_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_100BASEX) + return LINK_INBAND_DISABLE; + + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; +} + +static void +yt921x_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, + struct phylink_link_state *state) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct device *dev = to_device(priv); + int port = pp->index; + u16 bmsr; + u16 lpa; + int res; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, port, MII_BMSR, &bmsr); + if (res) + goto end; + + res = yt921x_intif_read(priv, port, MII_LPA, &lpa); + +end: + mutex_unlock(&priv->reg_lock); + + if (res) { + dev_err_ratelimited(dev, "Failed to %s PCS port %d: %i\n", + "get state of", port, res); + state->link = false; + return; + } + + if (state->interface == PHY_INTERFACE_MODE_100BASEX) { + state->speed = SPEED_100; + state->duplex = DUPLEX_FULL; + state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; + state->link = !!(bmsr & BMSR_LSTATUS); + return; + } + + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa); +} + +static void yt921x_phylink_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct device *dev = to_device(priv); + int port = pp->index; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, port, MII_BMCR, &val); + if (res) + goto end; + + val |= BMCR_ANRESTART; + res = yt921x_intif_write(priv, port, MII_BMCR, val); + +end: + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s PCS port %d: %i\n", "restart", + port, res); +} + +static void yt921x_phylink_pcs_disable(struct phylink_pcs *pcs) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct device *dev = to_device(priv); + int port = pp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_modify(priv, port, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s PCS port %d: %i\n", "power down", + port, res); +} + +static int yt921x_phylink_pcs_enable(struct phylink_pcs *pcs) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int port = pp->index; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, port, MII_BMCR, &val); + if (res) + goto end; + + val &= ~BMCR_PDOWN; + val |= BMCR_ANRESTART; + res = yt921x_intif_write(priv, port, MII_BMCR, val); + +end: + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int port = pp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_serdes_config(priv, port, neg_mode, interface, + advertising, permit_pause_to_mac); + mutex_unlock(&priv->reg_lock); + + return res; +} + +const struct phylink_pcs_ops yt921x_phylink_pcs_ops = { + .pcs_inband_caps = yt921x_phylink_pcs_inband_caps, + .pcs_get_state = yt921x_phylink_pcs_get_state, + .pcs_an_restart = yt921x_phylink_pcs_an_restart, + .pcs_disable = yt921x_phylink_pcs_disable, + .pcs_enable = yt921x_phylink_pcs_enable, + .pcs_config = yt921x_phylink_pcs_config, +}; diff --git a/drivers/net/dsa/motorcomm/pcs.h b/drivers/net/dsa/motorcomm/pcs.h new file mode 100644 index 000000000000..42426558086a --- /dev/null +++ b/drivers/net/dsa/motorcomm/pcs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_PCS_H +#define _YT_PCS_H + +#include + +extern const struct phylink_pcs_ops yt921x_phylink_pcs_ops; + +#endif