diff options
Diffstat (limited to 'drivers/net/dsa')
38 files changed, 3467 insertions, 2078 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index c0c91440340a..37a3dabdce31 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -68,20 +68,11 @@ config NET_DSA_QCA8K This enables support for the Qualcomm Atheros QCA8K Ethernet switch chips. -config NET_DSA_REALTEK_SMI - tristate "Realtek SMI Ethernet switch family support" - select NET_DSA_TAG_RTL4_A - select NET_DSA_TAG_RTL8_4 - select FIXED_PHY - select IRQ_DOMAIN - select REALTEK_PHY - select REGMAP - help - This enables support for the Realtek SMI-based switch - chips, currently only RTL8366RB. +source "drivers/net/dsa/realtek/Kconfig" config NET_DSA_SMSC_LAN9303 tristate + depends on VLAN_8021Q || VLAN_8021Q=n select NET_DSA_TAG_LAN9303 select REGMAP help diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 8da1569a34e6..e73838c12256 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -9,8 +9,6 @@ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o -obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o -realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o rtl8365mb.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o @@ -23,5 +21,6 @@ obj-y += microchip/ obj-y += mv88e6xxx/ obj-y += ocelot/ obj-y += qca/ +obj-y += realtek/ obj-y += sja1105/ obj-y += xrs700x/ diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 3867f3d4545f..a3b98992f180 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -2186,7 +2186,7 @@ int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) { int ret; - ret = phy_init_eee(phy, 0); + ret = phy_init_eee(phy, false); if (ret) return 0; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 33499fcd8848..cf82b1fa9725 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -621,7 +621,7 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds) get_device(&priv->master_mii_bus->dev); priv->master_mii_dn = dn; - priv->slave_mii_bus = devm_mdiobus_alloc(ds->dev); + priv->slave_mii_bus = mdiobus_alloc(); if (!priv->slave_mii_bus) { of_node_put(dn); return -ENOMEM; @@ -681,8 +681,10 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds) } err = mdiobus_register(priv->slave_mii_bus); - if (err && dn) + if (err && dn) { + mdiobus_free(priv->slave_mii_bus); of_node_put(dn); + } return err; } @@ -690,6 +692,7 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds) static void bcm_sf2_mdio_unregister(struct bcm_sf2_priv *priv) { mdiobus_unregister(priv->slave_mii_bus); + mdiobus_free(priv->slave_mii_bus); of_node_put(priv->master_mii_dn); } @@ -709,49 +712,25 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port) PHY_BRCM_IDDQ_SUSPEND; } -static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void bcm_sf2_sw_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { + unsigned long *interfaces = config->supported_interfaces; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - - if (!phy_interface_mode_is_rgmii(state->interface) && - state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_REVMII && - state->interface != PHY_INTERFACE_MODE_GMII && - state->interface != PHY_INTERFACE_MODE_INTERNAL && - state->interface != PHY_INTERFACE_MODE_MOCA) { - linkmode_zero(supported); - if (port != core_readl(priv, CORE_IMP0_PRT_ID)) - dev_err(ds->dev, - "Unsupported interface: %d for port %d\n", - state->interface, port); - return; - } - - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set_port_modes(mask); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - /* With the exclusion of MII and Reverse MII, we support Gigabit, - * including Half duplex - */ - if (state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_REVMII) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); + if (priv->int_phy_mask & BIT(port)) { + __set_bit(PHY_INTERFACE_MODE_INTERNAL, interfaces); + } else if (priv->moca_port == port) { + __set_bit(PHY_INTERFACE_MODE_MOCA, interfaces); + } else { + __set_bit(PHY_INTERFACE_MODE_MII, interfaces); + __set_bit(PHY_INTERFACE_MODE_REVMII, interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); + phy_interface_set_rgmii(interfaces); } - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000; } static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, @@ -1218,7 +1197,7 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .get_sset_count = bcm_sf2_sw_get_sset_count, .get_ethtool_phy_stats = b53_get_ethtool_phy_stats, .get_phy_flags = bcm_sf2_sw_get_phy_flags, - .phylink_validate = bcm_sf2_sw_validate, + .phylink_get_caps = bcm_sf2_sw_get_caps, .phylink_mac_config = bcm_sf2_sw_mac_config, .phylink_mac_link_down = bcm_sf2_sw_mac_link_down, .phylink_mac_link_up = bcm_sf2_sw_mac_link_up, diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index d55784d19fa4..3969d89fa4db 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -10,6 +10,7 @@ #include <linux/mii.h> #include <linux/phy.h> #include <linux/if_bridge.h> +#include <linux/if_vlan.h> #include <linux/etherdevice.h> #include "lan9303.h" @@ -1083,21 +1084,27 @@ static void lan9303_adjust_link(struct dsa_switch *ds, int port, static int lan9303_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) { + struct dsa_port *dp = dsa_to_port(ds, port); struct lan9303 *chip = ds->priv; - if (!dsa_is_user_port(ds, port)) + if (!dsa_port_is_user(dp)) return 0; + vlan_vid_add(dp->cpu_dp->master, htons(ETH_P_8021Q), port); + return lan9303_enable_processing_port(chip, port); } static void lan9303_port_disable(struct dsa_switch *ds, int port) { + struct dsa_port *dp = dsa_to_port(ds, port); struct lan9303 *chip = ds->priv; - if (!dsa_is_user_port(ds, port)) + if (!dsa_port_is_user(dp)) return; + vlan_vid_del(dp->cpu_dp->master, htons(ETH_P_8021Q), port); + lan9303_disable_processing_port(chip, port); lan9303_phy_write(ds, chip->phy_addr_base + port, MII_BMCR, BMCR_PDOWN); } @@ -1310,7 +1317,7 @@ static int lan9303_probe_reset_gpio(struct lan9303 *chip, struct device_node *np) { chip->reset_gpio = devm_gpiod_get_optional(chip->dev, "reset", - GPIOD_OUT_LOW); + GPIOD_OUT_HIGH); if (IS_ERR(chip->reset_gpio)) return PTR_ERR(chip->reset_gpio); diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 46ed953e787e..8a7a8093a156 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -498,8 +498,9 @@ static int gswip_mdio_rd(struct mii_bus *bus, int addr, int reg) static int gswip_mdio(struct gswip_priv *priv, struct device_node *mdio_np) { struct dsa_switch *ds = priv->ds; + int err; - ds->slave_mii_bus = devm_mdiobus_alloc(priv->dev); + ds->slave_mii_bus = mdiobus_alloc(); if (!ds->slave_mii_bus) return -ENOMEM; @@ -512,7 +513,11 @@ static int gswip_mdio(struct gswip_priv *priv, struct device_node *mdio_np) ds->slave_mii_bus->parent = priv->dev; ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask; - return of_mdiobus_register(ds->slave_mii_bus, mdio_np); + err = of_mdiobus_register(ds->slave_mii_bus, mdio_np); + if (err) + mdiobus_free(ds->slave_mii_bus); + + return err; } static int gswip_pce_table_entry_read(struct gswip_priv *priv, @@ -2145,8 +2150,10 @@ disable_switch: gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB); dsa_unregister_switch(priv->ds); mdio_bus: - if (mdio_np) + if (mdio_np) { mdiobus_unregister(priv->ds->slave_mii_bus); + mdiobus_free(priv->ds->slave_mii_bus); + } put_mdio_node: of_node_put(mdio_np); for (i = 0; i < priv->num_gphy_fw; i++) @@ -2170,6 +2177,7 @@ static int gswip_remove(struct platform_device *pdev) if (priv->ds->slave_mii_bus) { mdiobus_unregister(priv->ds->slave_mii_bus); of_node_put(priv->ds->slave_mii_bus->dev.of_node); + mdiobus_free(priv->ds->slave_mii_bus); } for (i = 0; i < priv->num_gphy_fw; i++) diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 991b9c6b6ce7..5dc9899bc0a6 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -1461,27 +1461,22 @@ static int ksz8_setup(struct dsa_switch *ds) return 0; } -static void ksz8_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void ksz8_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct ksz_device *dev = ds->priv; if (port == dev->cpu_port) { - if (state->interface != PHY_INTERFACE_MODE_RMII && - state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_NA) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_MII, + config->supported_interfaces); } else { - if (state->interface != PHY_INTERFACE_MODE_INTERNAL && - state->interface != PHY_INTERFACE_MODE_NA) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); } - /* Allow all the expected bits */ - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); + config->mac_capabilities = MAC_10 | MAC_100; /* Silicon Errata Sheet (DS80000830A): * "Port 1 does not respond to received flow control PAUSE frames" @@ -1489,27 +1484,11 @@ static void ksz8_validate(struct dsa_switch *ds, int port, * switches. */ if (!ksz_is_ksz88x3(dev) || port) - phylink_set(mask, Pause); + config->mac_capabilities |= MAC_SYM_PAUSE; /* Asym pause is not supported on KSZ8863 and KSZ8873 */ if (!ksz_is_ksz88x3(dev)) - phylink_set(mask, Asym_Pause); - - /* 10M and 100M are only supported */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - return; - -unsupported: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported interface: %s, port: %d\n", - phy_modes(state->interface), port); + config->mac_capabilities |= MAC_ASYM_PAUSE; } static const struct dsa_switch_ops ksz8_switch_ops = { @@ -1518,7 +1497,7 @@ static const struct dsa_switch_ops ksz8_switch_ops = { .setup = ksz8_setup, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, - .phylink_validate = ksz8_validate, + .phylink_get_caps = ksz8_get_caps, .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_port, .get_strings = ksz8_get_strings, diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 353b5f981740..a85d990896b0 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -222,9 +222,12 @@ static int ksz9477_reset_switch(struct ksz_device *dev) (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100); - if (dev->synclko_125) - ksz_write8(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, - SW_ENABLE_REFCLKO | SW_REFCLKO_IS_125MHZ); + data8 = SW_ENABLE_REFCLKO; + if (dev->synclko_disable) + data8 = 0; + else if (dev->synclko_125) + data8 = SW_ENABLE_REFCLKO | SW_REFCLKO_IS_125MHZ; + ksz_write8(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, data8); return 0; } diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 55dbda04ea62..7e33ec73f803 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -434,6 +434,12 @@ int ksz_switch_register(struct ksz_device *dev, } dev->synclko_125 = of_property_read_bool(dev->dev->of_node, "microchip,synclko-125"); + dev->synclko_disable = of_property_read_bool(dev->dev->of_node, + "microchip,synclko-disable"); + if (dev->synclko_125 && dev->synclko_disable) { + dev_err(dev->dev, "inconsistent synclko settings\n"); + return -EINVAL; + } } ret = dsa_register_switch(dev->ds); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index df8ae59c8525..3db63f62f0a1 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -75,6 +75,7 @@ struct ksz_device { u32 regs_size; bool phy_errata_9477; bool synclko_125; + bool synclko_disable; struct vlan_table *vlan_cache; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index b82512e5b33b..f74f25f479ed 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -2074,7 +2074,7 @@ mt7530_setup_mdio(struct mt7530_priv *priv) if (priv->irq) mt7530_setup_mdio_irq(priv); - ret = mdiobus_register(bus); + ret = devm_mdiobus_register(dev, bus); if (ret) { dev_err(dev, "failed to register MDIO bus: %d\n", ret); if (priv->irq) @@ -2846,7 +2846,7 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, mcr |= PMCR_RX_FC_EN; } - if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, 0) >= 0) { + if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) { switch (speed) { case SPEED_1000: mcr |= PMCR_FORCE_EEE1G; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 58ca684d73f7..7d5e72cdc125 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -86,12 +86,16 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask, u16 val) { + const unsigned long timeout = jiffies + msecs_to_jiffies(50); u16 data; int err; int i; - /* There's no bus specific operation to wait for a mask */ - for (i = 0; i < 16; i++) { + /* There's no bus specific operation to wait for a mask. Even + * if the initial poll takes longer than 50ms, always do at + * least one more attempt. + */ + for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) { err = mv88e6xxx_read(chip, addr, reg, &data); if (err) return err; @@ -99,7 +103,10 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, if ((data & mask) == val) return 0; - usleep_range(1000, 2000); + if (i < 2) + cpu_relax(); + else + usleep_range(1000, 2000); } dev_err(chip->dev, "Timeout while waiting for switch\n"); @@ -563,133 +570,268 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, return 0; } -static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static const u8 mv88e6185_phy_interface_modes[] = { + [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII, + [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_MII_100] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_MII_10] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_SERDES] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6185_PORT_STS_CMODE_1000BASE_X] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII, +}; + +static void mv88e6095_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (!phy_interface_mode_is_8023z(state->interface)) { - /* 10M and 100M are only supported in non-802.3z mode */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); + u8 cmode = chip->ports[port].cmode; + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; + + if (mv88e6xxx_phy_is_internal(chip->ds, port)) { + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + } else { + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); + + config->mac_capabilities |= MAC_1000FD; } } -static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - /* FIXME: if the port is in 1000Base-X mode, then it only supports - * 1000M FD speeds. In this case, CMODE will indicate 5. + u8 cmode = chip->ports[port].cmode; + + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; +} + +static const u8 mv88e6xxx_phy_interface_modes[] = { + [MV88E6XXX_PORT_STS_CMODE_MII_PHY] = PHY_INTERFACE_MODE_MII, + [MV88E6XXX_PORT_STS_CMODE_MII] = PHY_INTERFACE_MODE_MII, + [MV88E6XXX_PORT_STS_CMODE_GMII] = PHY_INTERFACE_MODE_GMII, + [MV88E6XXX_PORT_STS_CMODE_RMII_PHY] = PHY_INTERFACE_MODE_RMII, + [MV88E6XXX_PORT_STS_CMODE_RMII] = PHY_INTERFACE_MODE_RMII, + [MV88E6XXX_PORT_STS_CMODE_100BASEX] = PHY_INTERFACE_MODE_100BASEX, + [MV88E6XXX_PORT_STS_CMODE_1000BASEX] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6XXX_PORT_STS_CMODE_SGMII] = PHY_INTERFACE_MODE_SGMII, + /* higher interface modes are not needed here, since ports supporting + * them are writable, and so the supported interfaces are filled in the + * corresponding .phylink_set_interfaces() implementation below */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); +}; - mv88e6065_phylink_validate(chip, port, mask, state); +static void mv88e6xxx_translate_cmode(u8 cmode, unsigned long *supported) +{ + if (cmode < ARRAY_SIZE(mv88e6xxx_phy_interface_modes) && + mv88e6xxx_phy_interface_modes[cmode]) + __set_bit(mv88e6xxx_phy_interface_modes[cmode], supported); + else if (cmode == MV88E6XXX_PORT_STS_CMODE_RGMII) + phy_interface_set_rgmii(supported); } -static void mv88e6341_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6250_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 5) - phylink_set(mask, 2500baseX_Full); + unsigned long *supported = config->supported_interfaces; - /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); - mv88e6065_phylink_validate(chip, port, mask, state); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; } -static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static int mv88e6352_get_port4_serdes_cmode(struct mv88e6xxx_chip *chip) { - /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + u16 reg, val; + int err; + + err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + /* If PHY_DETECT is zero, then we are not in auto-media mode */ + if (!(reg & MV88E6XXX_PORT_STS_PHY_DETECT)) + return 0xf; + + val = reg & ~MV88E6XXX_PORT_STS_PHY_DETECT; + err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, val); + if (err) + return err; + + err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, &val); + if (err) + return err; + + /* Restore PHY_DETECT value */ + err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, reg); + if (err) + return err; - mv88e6065_phylink_validate(chip, port, mask, state); + return val & MV88E6XXX_PORT_STS_CMODE_MASK; } -static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 9) { - phylink_set(mask, 2500baseX_Full); - phylink_set(mask, 2500baseT_Full); + unsigned long *supported = config->supported_interfaces; + int err, cmode; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* Port 4 supports automedia if the serdes is associated with it. */ + if (port == 4) { + mv88e6xxx_reg_lock(chip); + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err < 0) + dev_err(chip->dev, "p%d: failed to read scratch\n", + port); + if (err <= 0) + goto unlock; + + cmode = mv88e6352_get_port4_serdes_cmode(chip); + if (cmode < 0) + dev_err(chip->dev, "p%d: failed to read serdes cmode\n", + port); + else + mv88e6xxx_translate_cmode(cmode, supported); +unlock: + mv88e6xxx_reg_unlock(chip); } +} + +static void mv88e6341_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field is programmable on port 5 */ + if (port == 5) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); - mv88e6065_phylink_validate(chip, port, mask, state); + config->mac_capabilities |= MAC_2500FD; + } } -static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6390_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 9) { - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseKR_Full); + unsigned long *supported = config->supported_interfaces; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + /* No ethtool bits for 200Mbps */ + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field is programmable on ports 9 and 10 */ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + + config->mac_capabilities |= MAC_2500FD; } +} + +static void mv88e6390x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; + + mv88e6390_phylink_get_caps(chip, port, config); + + /* For the 6x90X, ports 2-7 can be in automedia mode. + * (Note that 6x90 doesn't support RXAUI nor XAUI). + * + * Port 2 can also support 1000BASE-X in automedia mode if port 9 is + * configured for 1000BASE-X, SGMII or 2500BASE-X. + * Port 3-4 can also support 1000BASE-X in automedia mode if port 9 is + * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. + * + * Port 5 can also support 1000BASE-X in automedia mode if port 10 is + * configured for 1000BASE-X, SGMII or 2500BASE-X. + * Port 6-7 can also support 1000BASE-X in automedia mode if port 10 is + * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. + * + * For now, be permissive (as the old code was) and allow 1000BASE-X + * on ports 2..7. + */ + if (port >= 2 && port <= 7) + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); - mv88e6390_phylink_validate(chip, port, mask, state); + /* The C_Mode field can also be programmed for 10G speeds */ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_XAUI, supported); + __set_bit(PHY_INTERFACE_MODE_RXAUI, supported); + + config->mac_capabilities |= MAC_10000FD; + } } -static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6393x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { + unsigned long *supported = config->supported_interfaces; bool is_6191x = chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X; - if (((port == 0 || port == 9) && !is_6191x) || port == 10) { - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseKR_Full); - phylink_set(mask, 10000baseCR_Full); - phylink_set(mask, 10000baseSR_Full); - phylink_set(mask, 10000baseLR_Full); - phylink_set(mask, 10000baseLRM_Full); - phylink_set(mask, 10000baseER_Full); - phylink_set(mask, 5000baseT_Full); - phylink_set(mask, 2500baseX_Full); - phylink_set(mask, 2500baseT_Full); - } + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + /* The C_Mode field can be programmed for ports 0, 9 and 10 */ + if (port == 0 || port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); - mv88e6065_phylink_validate(chip, port, mask, state); + /* 6191X supports >1G modes only on port 10 */ + if (!is_6191x || port == 10) { + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); + __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); + /* FIXME: USXGMII is not supported yet */ + /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */ + + config->mac_capabilities |= MAC_2500FD | MAC_5000FD | + MAC_10000FD; + } + } } -static void mv88e6xxx_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct mv88e6xxx_chip *chip = ds->priv; - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set_port_modes(mask); - - if (chip->info->ops->phylink_validate) - chip->info->ops->phylink_validate(chip, port, mask, state); + chip->info->ops->phylink_get_caps(chip, port, config); - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - /* We can only operate at 2500BaseX or 1000BaseX. If requested - * to advertise both, only report advertising at 2500BaseX. - */ - phylink_helper_basex_speed(state); + /* Internal ports need GMII for PHYLIB */ + if (mv88e6xxx_phy_is_internal(ds, port)) + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); } static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, @@ -1283,8 +1425,15 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) pvlan = 0; - /* Frames from user ports can egress any local DSA links and CPU ports, - * as well as any local member of their bridge group. + /* Frames from standalone user ports can only egress on the + * upstream port. + */ + if (!dsa_port_bridge_dev_get(dp)) + return BIT(dsa_switch_upstream_port(ds)); + + /* Frames from bridged user ports can egress any local DSA + * links and CPU ports, as well as any local member of their + * bridge group. */ dsa_switch_for_each_port(other_dp, ds) if (other_dp->type == DSA_PORT_TYPE_CPU || @@ -1616,21 +1765,11 @@ static int mv88e6xxx_fid_map_vlan(struct mv88e6xxx_chip *chip, int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap) { - int i, err; - u16 fid; - bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); - /* Set every FID bit used by the (un)bridged ports */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - err = mv88e6xxx_port_get_fid(chip, i, &fid); - if (err) - return err; - - set_bit(fid, fid_bitmap); - } - - /* Set every FID bit used by the VLAN entries */ + /* Every FID has an associated VID, so walking the VTU + * will discover the full set of FIDs in use. + */ return mv88e6xxx_vtu_walk(chip, mv88e6xxx_fid_map_vlan, fid_bitmap); } @@ -1643,10 +1782,7 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) if (err) return err; - /* The reset value 0x000 is used to indicate that multiple address - * databases are not needed. Return the next positive available. - */ - *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); + *fid = find_first_zero_bit(fid_bitmap, MV88E6XXX_N_FID); if (unlikely(*fid >= mv88e6xxx_num_databases(chip))) return -ENOSPC; @@ -2138,6 +2274,9 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, if (!vlan.valid) { memset(&vlan, 0, sizeof(vlan)); + if (vid == MV88E6XXX_VID_STANDALONE) + vlan.policy = true; + err = mv88e6xxx_atu_new(chip, &vlan.fid); if (err) return err; @@ -2284,6 +2423,13 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, if (!mv88e6xxx_max_vid(chip)) return -EOPNOTSUPP; + /* The ATU removal procedure needs the FID to be mapped in the VTU, + * but FDB deletion runs concurrently with VLAN deletion. Flush the DSA + * switchdev workqueue to ensure that all FDB entries are deleted + * before we remove the VLAN. + */ + dsa_flush_workqueue(); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_get_pvid(chip, port, &pvid); @@ -2480,6 +2626,10 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, if (err) goto unlock; + err = mv88e6xxx_port_set_map_da(chip, port, true); + if (err) + goto unlock; + err = mv88e6xxx_port_commit_pvid(chip, port); if (err) goto unlock; @@ -2514,6 +2664,12 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, mv88e6xxx_port_vlan_map(chip, port)) dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); + err = mv88e6xxx_port_set_map_da(chip, port, false); + if (err) + dev_err(ds->dev, + "port %d failed to restore map-DA: %pe\n", + port, ERR_PTR(err)); + err = mv88e6xxx_port_commit_pvid(chip, port); if (err) dev_err(ds->dev, @@ -2857,7 +3013,10 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { + struct device_node *phy_handle = NULL; struct dsa_switch *ds = chip->ds; + struct dsa_port *dp; + int tx_amp; int err; u16 reg; @@ -2911,12 +3070,13 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; /* Port Control 2: don't force a good FCS, set the MTU size to - * 10222 bytes, disable 802.1q tags checking, don't discard tagged or - * untagged frames on this port, do a destination address lookup on all - * received packets as usual, disable ARP mirroring and don't send a - * copy of all transmitted/received frames on this port to the CPU. + * 10222 bytes, disable 802.1q tags checking, don't discard + * tagged or untagged frames on this port, skip destination + * address lookup on user ports, disable ARP mirroring and don't + * send a copy of all transmitted/received frames on this port + * to the CPU. */ - err = mv88e6xxx_port_set_map_da(chip, port); + err = mv88e6xxx_port_set_map_da(chip, port, !dsa_is_user_port(ds, port)); if (err) return err; @@ -2924,8 +3084,44 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; + /* On chips that support it, set all downstream DSA ports' + * VLAN policy to TRAP. In combination with loading + * MV88E6XXX_VID_STANDALONE as a policy entry in the VTU, this + * provides a better isolation barrier between standalone + * ports, as the ATU is bypassed on any intermediate switches + * between the incoming port and the CPU. + */ + if (dsa_is_downstream_port(ds, port) && + chip->info->ops->port_set_policy) { + err = chip->info->ops->port_set_policy(chip, port, + MV88E6XXX_POLICY_MAPPING_VTU, + MV88E6XXX_POLICY_ACTION_TRAP); + if (err) + return err; + } + + /* User ports start out in standalone mode and 802.1Q is + * therefore disabled. On DSA ports, all valid VIDs are always + * loaded in the VTU - therefore, enable 802.1Q in order to take + * advantage of VLAN policy on chips that supports it. + */ err = mv88e6xxx_port_set_8021q_mode(chip, port, - MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED); + dsa_is_user_port(ds, port) ? + MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED : + MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE); + if (err) + return err; + + /* Bind MV88E6XXX_VID_STANDALONE to MV88E6XXX_FID_STANDALONE by + * virtue of the fact that mv88e6xxx_atu_new() will pick it as + * the first free FID. This will be used as the private PVID for + * unbridged ports. Shared (DSA and CPU) ports must also be + * members of this VID, in order to trap all frames assigned to + * it to the CPU. + */ + err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_STANDALONE, + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, + false); if (err) return err; @@ -2938,7 +3134,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) * relying on their port default FID. */ err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED, - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED, + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, false); if (err) return err; @@ -3011,6 +3207,23 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } + if (chip->info->ops->serdes_set_tx_amplitude) { + dp = dsa_to_port(ds, port); + if (dp) + phy_handle = of_parse_phandle(dp->dn, "phy-handle", 0); + + if (phy_handle && !of_property_read_u32(phy_handle, + "tx-p2p-microvolt", + &tx_amp)) + err = chip->info->ops->serdes_set_tx_amplitude(chip, + port, tx_amp); + if (phy_handle) { + of_node_put(phy_handle); + if (err) + return err; + } + } + /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the * CPU and DSA port(s), and the other ports. @@ -3399,7 +3612,7 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, return err; } - bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus)); + bus = mdiobus_alloc_size(sizeof(*mdio_bus)); if (!bus) return -ENOMEM; @@ -3424,14 +3637,14 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, if (!external) { err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); if (err) - return err; + goto out; } err = of_mdiobus_register(bus, np); if (err) { dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); mv88e6xxx_g2_irq_mdio_free(chip, bus); - return err; + goto out; } if (external) @@ -3440,21 +3653,26 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, list_add(&mdio_bus->list, &chip->mdios); return 0; + +out: + mdiobus_free(bus); + return err; } static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) { - struct mv88e6xxx_mdio_bus *mdio_bus; + struct mv88e6xxx_mdio_bus *mdio_bus, *p; struct mii_bus *bus; - list_for_each_entry(mdio_bus, &chip->mdios, list) { + list_for_each_entry_safe(mdio_bus, p, &chip->mdios, list) { bus = mdio_bus->bus; if (!mdio_bus->external) mv88e6xxx_g2_irq_mdio_free(chip, bus); mdiobus_unregister(bus); + mdiobus_free(bus); } } @@ -3577,7 +3795,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3611,7 +3829,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6095_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3627,6 +3845,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_sync_link = mv88e6185_port_sync_link, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, @@ -3657,7 +3876,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6095_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3694,7 +3913,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3735,7 +3954,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -3799,7 +4018,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6341_phylink_validate, + .phylink_get_caps = mv88e6341_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3841,7 +4060,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3877,7 +4096,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3919,7 +4138,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3974,7 +4193,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -4016,7 +4235,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -4073,8 +4292,9 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -4113,7 +4333,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -4175,7 +4395,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -4236,7 +4456,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390x_phylink_validate, + .phylink_get_caps = mv88e6390x_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -4296,7 +4516,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_get_regs = mv88e6390_serdes_get_regs, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -4353,10 +4573,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6250_ops = { @@ -4396,7 +4617,7 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6250_ptp_ops, - .phylink_validate = mv88e6065_phylink_validate, + .phylink_get_caps = mv88e6250_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -4458,7 +4679,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -4502,7 +4723,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -4544,7 +4765,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -4610,7 +4831,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6341_phylink_validate, + .phylink_get_caps = mv88e6341_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -4652,7 +4873,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -4696,7 +4917,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -4759,7 +4980,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_stats = mv88e6352_serdes_get_stats, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, - .phylink_validate = mv88e6352_phylink_validate, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -4824,7 +5046,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -4888,7 +5110,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390x_phylink_validate, + .phylink_get_caps = mv88e6390x_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6393x_ops = { @@ -4952,7 +5174,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6393x_phylink_validate, + .phylink_get_caps = mv88e6393x_phylink_get_caps, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { @@ -6221,7 +6443,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .teardown = mv88e6xxx_teardown, .port_setup = mv88e6xxx_port_setup, .port_teardown = mv88e6xxx_port_teardown, - .phylink_validate = mv88e6xxx_validate, + .phylink_get_caps = mv88e6xxx_get_caps, .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, .phylink_mac_config = mv88e6xxx_mac_config, .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 8271b8aa7b71..30b92a265613 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -179,6 +179,7 @@ struct mv88e6xxx_vtu_entry { u16 fid; u8 sid; bool valid; + bool policy; u8 member[DSA_MAX_PORTS]; u8 state[DSA_MAX_PORTS]; }; @@ -392,6 +393,7 @@ struct mv88e6xxx_chip { struct mv88e6xxx_bus_ops { int (*read)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); int (*write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); + int (*init)(struct mv88e6xxx_chip *chip); }; struct mv88e6xxx_mdio_bus { @@ -586,6 +588,10 @@ struct mv88e6xxx_ops { void (*serdes_get_regs)(struct mv88e6xxx_chip *chip, int port, void *_p); + /* SERDES SGMII/Fiber Output Amplitude */ + int (*serdes_set_tx_amplitude)(struct mv88e6xxx_chip *chip, int port, + int val); + /* Address Translation Unit operations */ int (*atu_get_hash)(struct mv88e6xxx_chip *chip, u8 *hash); int (*atu_set_hash)(struct mv88e6xxx_chip *chip, u8 hash); @@ -609,9 +615,8 @@ struct mv88e6xxx_ops { const struct mv88e6xxx_ptp_ops *ptp_ops; /* Phylink */ - void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state); + void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config); /* Max Frame Size */ int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu); diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 4f3dbb015f77..2c1607c858a1 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -46,6 +46,7 @@ /* Offset 0x02: VTU FID Register */ #define MV88E6352_G1_VTU_FID 0x02 +#define MV88E6352_G1_VTU_FID_VID_POLICY 0x1000 #define MV88E6352_G1_VTU_FID_MASK 0x0fff /* Offset 0x03: VTU SID Register */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index ae12c981923e..b1bd9274a562 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -27,7 +27,7 @@ static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, return err; entry->fid = val & MV88E6352_G1_VTU_FID_MASK; - + entry->policy = !!(val & MV88E6352_G1_VTU_FID_VID_POLICY); return 0; } @@ -36,6 +36,9 @@ static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, { u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK; + if (entry->policy) + val |= MV88E6352_G1_VTU_FID_VID_POLICY; + return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val); } diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index f3e27573a386..807aeaad9830 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -299,6 +299,8 @@ #define MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU BIT(2) #define MV88E6352_G2_SCRATCH_CONFIG_DATA2 0x72 #define MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK 0x3 +#define MV88E6352_G2_SCRATCH_CONFIG_DATA3 0x73 +#define MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL BIT(1) #define MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO 0 #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1 @@ -370,6 +372,7 @@ extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops; int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, bool external); +int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin); int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats); diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c index eda710062933..a9d6e40321a2 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c +++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c @@ -289,3 +289,31 @@ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val); } + +/** + * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes + * @chip: chip private data + * @port: port number to check for serdes + * + * Indicates whether the port may have a serdes attached according to the + * pin strapping. Returns negative error number, 0 if the port is not + * configured to have a serdes, and 1 if the port is configured to have a + * serdes attached. + */ +int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port) +{ + u8 config3, p; + int err; + + err = mv88e6xxx_g2_scratch_read(chip, MV88E6352_G2_SCRATCH_CONFIG_DATA3, + &config3); + if (err) + return err; + + if (config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL) + p = 5; + else + p = 4; + + return port == p; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index ab41619a809b..ceb450113f88 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -1278,7 +1278,7 @@ int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new); } -int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port) +int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map) { u16 reg; int err; @@ -1287,7 +1287,10 @@ int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port) if (err) return err; - reg |= MV88E6XXX_PORT_CTL2_MAP_DA; + if (map) + reg |= MV88E6XXX_PORT_CTL2_MAP_DA; + else + reg &= ~MV88E6XXX_PORT_CTL2_MAP_DA; return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); } diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 03382b66f800..3a13db2ec27b 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -42,6 +42,11 @@ #define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020 #define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010 #define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f +#define MV88E6XXX_PORT_STS_CMODE_MII_PHY 0x0001 +#define MV88E6XXX_PORT_STS_CMODE_MII 0x0002 +#define MV88E6XXX_PORT_STS_CMODE_GMII 0x0003 +#define MV88E6XXX_PORT_STS_CMODE_RMII_PHY 0x0004 +#define MV88E6XXX_PORT_STS_CMODE_RMII 0x0005 #define MV88E6XXX_PORT_STS_CMODE_RGMII 0x0007 #define MV88E6XXX_PORT_STS_CMODE_100BASEX 0x0008 #define MV88E6XXX_PORT_STS_CMODE_1000BASEX 0x0009 @@ -425,7 +430,7 @@ int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, bool drop_untagged); -int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map); int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int upstream_port); int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 2b05ead515cd..7b37d45bc9fb 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -272,14 +272,6 @@ int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) return lane; } -static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) -{ - if (mv88e6xxx_serdes_get_lane(chip, port) >= 0) - return true; - - return false; -} - struct mv88e6352_serdes_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -293,20 +285,24 @@ static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) { - if (mv88e6352_port_has_serdes(chip, port)) - return ARRAY_SIZE(mv88e6352_serdes_hw_stats); + int err; - return 0; + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; + + return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data) { struct mv88e6352_serdes_hw_stat *stat; - int i; + int err, i; - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { stat = &mv88e6352_serdes_hw_stats[i]; @@ -348,11 +344,12 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, { struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; struct mv88e6352_serdes_hw_stat *stat; + int i, err; u64 value; - int i; - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); @@ -419,8 +416,13 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) { - if (!mv88e6352_port_has_serdes(chip, port)) - return 0; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + mv88e6xxx_reg_unlock(chip); + if (err <= 0) + return err; return 32 * sizeof(u16); } @@ -432,7 +434,8 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) int err; int i; - if (!mv88e6352_port_has_serdes(chip, port)) + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) return; for (i = 0 ; i < 32; i++) { @@ -1310,6 +1313,44 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) } } +static const int mv88e6352_serdes_p2p_to_reg[] = { + /* Index of value in microvolts corresponds to the register value */ + 14000, 112000, 210000, 308000, 406000, 504000, 602000, 700000, +}; + +int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port, + int val) +{ + bool found = false; + u16 ctrl, reg; + int err; + int i; + + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err <= 0) + return err; + + for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_p2p_to_reg); ++i) { + if (mv88e6352_serdes_p2p_to_reg[i] == val) { + reg = i; + found = true; + break; + } + } + + if (!found) + return -EINVAL; + + err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_SPEC_CTRL2, &ctrl); + if (err) + return err; + + ctrl &= ~MV88E6352_SERDES_OUT_AMP_MASK; + ctrl |= reg; + + return mv88e6352_serdes_write(chip, MV88E6352_SERDES_SPEC_CTRL2, ctrl); +} + static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane, bool on) { diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 8dd8ed225b45..29bb4e91e2f6 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -27,6 +27,8 @@ #define MV88E6352_SERDES_INT_FIBRE_ENERGY BIT(4) #define MV88E6352_SERDES_INT_STATUS 0x13 +#define MV88E6352_SERDES_SPEC_CTRL2 0x1a +#define MV88E6352_SERDES_OUT_AMP_MASK 0x0007 #define MV88E6341_PORT5_LANE 0x15 @@ -176,6 +178,9 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); +int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port, + int val); + /* Return the (first) SERDES lane address a port is using, -errno otherwise. */ static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c index 282fe08db050..a990271b7482 100644 --- a/drivers/net/dsa/mv88e6xxx/smi.c +++ b/drivers/net/dsa/mv88e6xxx/smi.c @@ -55,11 +55,15 @@ static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip, static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, int dev, int reg, int bit, int val) { + const unsigned long timeout = jiffies + msecs_to_jiffies(50); u16 data; int err; int i; - for (i = 0; i < 16; i++) { + /* Even if the initial poll takes longer than 50ms, always do + * at least one more attempt. + */ + for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) { err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data); if (err) return err; @@ -67,7 +71,10 @@ static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, if (!!(data & BIT(bit)) == !!val) return 0; - usleep_range(1000, 2000); + if (i < 2) + cpu_relax(); + else + usleep_range(1000, 2000); } return -ETIMEDOUT; @@ -104,11 +111,6 @@ static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip, { int err; - err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, - MV88E6XXX_SMI_CMD, 15, 0); - if (err) - return err; - err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, MV88E6XXX_SMI_CMD, MV88E6XXX_SMI_CMD_BUSY | @@ -132,11 +134,6 @@ static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, { int err; - err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, - MV88E6XXX_SMI_CMD, 15, 0); - if (err) - return err; - err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, MV88E6XXX_SMI_DATA, data); if (err) @@ -155,9 +152,20 @@ static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, MV88E6XXX_SMI_CMD, 15, 0); } +static int mv88e6xxx_smi_indirect_init(struct mv88e6xxx_chip *chip) +{ + /* Ensure that the chip starts out in the ready state. As both + * reads and writes always ensure this on return, they can + * safely depend on the chip not being busy on entry. + */ + return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, 15, 0); +} + static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = { .read = mv88e6xxx_smi_indirect_read, .write = mv88e6xxx_smi_indirect_write, + .init = mv88e6xxx_smi_indirect_init, }; int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, @@ -175,5 +183,8 @@ int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, chip->bus = bus; chip->sw_addr = sw_addr; + if (chip->smi_ops->init) + return chip->smi_ops->init(chip); + return 0; } diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 9957772201d5..9ffd5491bf2d 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -50,7 +50,7 @@ static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid, outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; outer_tagging_rule->prio = 1; - outer_tagging_rule->id.cookie = port; + outer_tagging_rule->id.cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port); outer_tagging_rule->id.tc_offload = false; outer_tagging_rule->block_id = VCAP_ES0; outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -103,7 +103,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, untagging_rule->vlan.vid.value = vid; untagging_rule->vlan.vid.mask = VLAN_VID_MASK; untagging_rule->prio = 1; - untagging_rule->id.cookie = port; + untagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule->id.tc_offload = false; untagging_rule->block_id = VCAP_IS1; untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -124,7 +124,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, redirect_rule->ingress_port_mask = BIT(upstream); redirect_rule->pag = port; redirect_rule->prio = 1; - redirect_rule->id.cookie = port; + redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); redirect_rule->id.tc_offload = false; redirect_rule->block_id = VCAP_IS2; redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -267,148 +267,82 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) mutex_unlock(&ocelot->fwd_domain_lock); } -/* Set up a VCAP IS2 rule for delivering PTP frames to the CPU port module. - * If the quirk_no_xtr_irq is in place, then also copy those PTP frames to the - * tag_8021q CPU port. +/* On switches with no extraction IRQ wired, trapped packets need to be + * replicated over Ethernet as well, otherwise we'd get no notification of + * their arrival when using the ocelot-8021q tagging protocol. */ -static int felix_setup_mmio_filtering(struct felix *felix) +static int felix_update_trapping_destinations(struct dsa_switch *ds, + bool using_tag_8021q) { - unsigned long user_ports = dsa_user_ports(felix->ds); - struct ocelot_vcap_filter *redirect_rule; - struct ocelot_vcap_filter *tagging_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; - int cpu = -1, port, ret; + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + struct ocelot_vcap_filter *trap; + enum ocelot_mask_mode mask_mode; + unsigned long port_mask; + struct dsa_port *dp; + bool cpu_copy_ena; + int cpu = -1, err; - tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); - if (!tagging_rule) - return -ENOMEM; + if (!felix->info->quirk_no_xtr_irq) + return 0; - redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); - if (!redirect_rule) { - kfree(tagging_rule); - return -ENOMEM; + /* Figure out the current CPU port */ + dsa_switch_for_each_cpu_port(dp, ds) { + cpu = dp->index; + break; } - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_cpu_port(ds, port)) { - cpu = port; - break; + /* We are sure that "cpu" was found, otherwise + * dsa_tree_setup_default_cpu() would have failed earlier. + */ + + /* Make sure all traps are set up for that destination */ + list_for_each_entry(trap, &ocelot->traps, trap_list) { + /* Figure out the current trapping destination */ + if (using_tag_8021q) { + /* Redirect to the tag_8021q CPU port. If timestamps + * are necessary, also copy trapped packets to the CPU + * port module. + */ + mask_mode = OCELOT_MASK_MODE_REDIRECT; + port_mask = BIT(cpu); + cpu_copy_ena = !!trap->take_ts; + } else { + /* Trap packets only to the CPU port module, which is + * redirected to the NPI port (the DSA CPU port) + */ + mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; + port_mask = 0; + cpu_copy_ena = true; } - } - if (cpu < 0) { - kfree(tagging_rule); - kfree(redirect_rule); - return -EINVAL; - } + if (trap->action.mask_mode == mask_mode && + trap->action.port_mask == port_mask && + trap->action.cpu_copy_ena == cpu_copy_ena) + continue; - tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE; - *(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588); - *(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff); - tagging_rule->ingress_port_mask = user_ports; - tagging_rule->prio = 1; - tagging_rule->id.cookie = ocelot->num_phys_ports; - tagging_rule->id.tc_offload = false; - tagging_rule->block_id = VCAP_IS1; - tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; - tagging_rule->lookup = 0; - tagging_rule->action.pag_override_mask = 0xff; - tagging_rule->action.pag_val = ocelot->num_phys_ports; - - ret = ocelot_vcap_filter_add(ocelot, tagging_rule, NULL); - if (ret) { - kfree(tagging_rule); - kfree(redirect_rule); - return ret; - } + trap->action.mask_mode = mask_mode; + trap->action.port_mask = port_mask; + trap->action.cpu_copy_ena = cpu_copy_ena; - redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; - redirect_rule->ingress_port_mask = user_ports; - redirect_rule->pag = ocelot->num_phys_ports; - redirect_rule->prio = 1; - redirect_rule->id.cookie = ocelot->num_phys_ports; - redirect_rule->id.tc_offload = false; - redirect_rule->block_id = VCAP_IS2; - redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; - redirect_rule->lookup = 0; - redirect_rule->action.cpu_copy_ena = true; - if (felix->info->quirk_no_xtr_irq) { - /* Redirect to the tag_8021q CPU but also copy PTP packets to - * the CPU port module - */ - redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; - redirect_rule->action.port_mask = BIT(cpu); - } else { - /* Trap PTP packets only to the CPU port module (which is - * redirected to the NPI port) - */ - redirect_rule->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; - redirect_rule->action.port_mask = 0; - } - - ret = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL); - if (ret) { - ocelot_vcap_filter_del(ocelot, tagging_rule); - kfree(redirect_rule); - return ret; + err = ocelot_vcap_filter_replace(ocelot, trap); + if (err) + return err; } - /* The ownership of the CPU port module's queues might have just been - * transferred to the tag_8021q tagger from the NPI-based tagger. - * So there might still be all sorts of crap in the queues. On the - * other hand, the MMIO-based matching of PTP frames is very brittle, - * so we need to be careful that there are no extra frames to be - * dequeued over MMIO, since we would never know to discard them. - */ - ocelot_drain_cpu_queue(ocelot, 0); - return 0; } -static int felix_teardown_mmio_filtering(struct felix *felix) -{ - struct ocelot_vcap_filter *tagging_rule, *redirect_rule; - struct ocelot_vcap_block *block_vcap_is1; - struct ocelot_vcap_block *block_vcap_is2; - struct ocelot *ocelot = &felix->ocelot; - int err; - - block_vcap_is1 = &ocelot->block[VCAP_IS1]; - block_vcap_is2 = &ocelot->block[VCAP_IS2]; - - tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, - ocelot->num_phys_ports, - false); - if (!tagging_rule) - return -ENOENT; - - err = ocelot_vcap_filter_del(ocelot, tagging_rule); - if (err) - return err; - - redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, - ocelot->num_phys_ports, - false); - if (!redirect_rule) - return -ENOENT; - - return ocelot_vcap_filter_del(ocelot, redirect_rule); -} - static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); unsigned long cpu_flood; - int port, err; + struct dsa_port *dp; + int err; felix_8021q_cpu_port_init(ocelot, cpu); - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - + dsa_switch_for_each_available_port(dp, ds) { /* This overwrites ocelot_init(): * Do not forward BPDU frames to the CPU port module, * for 2 reasons: @@ -421,7 +355,7 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) */ ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0), - ANA_PORT_CPU_FWD_BPDU_CFG, port); + ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); } /* In tag_8021q mode, the CPU port module is unused, except for PTP @@ -437,10 +371,19 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) if (err) return err; - err = felix_setup_mmio_filtering(felix); + err = felix_update_trapping_destinations(ds, true); if (err) goto out_tag_8021q_unregister; + /* The ownership of the CPU port module's queues might have just been + * transferred to the tag_8021q tagger from the NPI-based tagger. + * So there might still be all sorts of crap in the queues. On the + * other hand, the MMIO-based matching of PTP frames is very brittle, + * so we need to be careful that there are no extra frames to be + * dequeued over MMIO, since we would never know to discard them. + */ + ocelot_drain_cpu_queue(ocelot, 0); + return 0; out_tag_8021q_unregister: @@ -451,27 +394,24 @@ out_tag_8021q_unregister: static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); - int err, port; + struct dsa_port *dp; + int err; - err = felix_teardown_mmio_filtering(felix); + err = felix_update_trapping_destinations(ds, false); if (err) dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d", err); dsa_tag_8021q_unregister(ds); - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - + dsa_switch_for_each_available_port(dp, ds) { /* Restore the logic from ocelot_init: * do not forward BPDU frames to the front ports. */ ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff), ANA_PORT_CPU_FWD_BPDU_CFG, - port); + dp->index); } felix_8021q_cpu_port_deinit(ocelot, cpu); @@ -1192,7 +1132,8 @@ static int felix_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int port, err; + struct dsa_port *dp; + int err; err = felix_init_structs(felix, ds->num_ports); if (err) @@ -1211,30 +1152,24 @@ static int felix_setup(struct dsa_switch *ds) } } - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_init_port(ocelot, port); + dsa_switch_for_each_available_port(dp, ds) { + ocelot_init_port(ocelot, dp->index); /* Set the default QoS Classification based on PCP and DEI * bits of vlan tag. */ - felix_port_qos_map_init(ocelot, port); + felix_port_qos_map_init(ocelot, dp->index); } err = ocelot_devlink_sb_register(ocelot); if (err) goto out_deinit_ports; - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_cpu_port(ds, port)) - continue; - + dsa_switch_for_each_cpu_port(dp, ds) { /* The initial tag protocol is NPI which always returns 0, so * there's no real point in checking for errors. */ - felix_set_tag_protocol(ds, port, felix->tag_proto); + felix_set_tag_protocol(ds, dp->index, felix->tag_proto); break; } @@ -1244,12 +1179,8 @@ static int felix_setup(struct dsa_switch *ds) return 0; out_deinit_ports: - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_deinit_port(ocelot, port); - } + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); ocelot_deinit_timestamp(ocelot); ocelot_deinit(ocelot); @@ -1265,22 +1196,15 @@ static void felix_teardown(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int port; - - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_cpu_port(ds, port)) - continue; + struct dsa_port *dp; - felix_del_tag_protocol(ds, port, felix->tag_proto); + dsa_switch_for_each_cpu_port(dp, ds) { + felix_del_tag_protocol(ds, dp->index, felix->tag_proto); break; } - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_deinit_port(ocelot, port); - } + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); ocelot_devlink_sb_unregister(ocelot); ocelot_deinit_timestamp(ocelot); @@ -1302,8 +1226,17 @@ static int felix_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + bool using_tag_8021q; + int err; + + err = ocelot_hwstamp_set(ocelot, port, ifr); + if (err) + return err; + + using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; - return ocelot_hwstamp_set(ocelot, port, ifr); + return felix_update_trapping_destinations(ds, using_tag_8021q); } static bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type) @@ -1430,8 +1363,17 @@ static int felix_cls_flower_add(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, bool ingress) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + bool using_tag_8021q; + int err; + + err = ocelot_cls_flower_replace(ocelot, port, cls, ingress); + if (err) + return err; + + using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; - return ocelot_cls_flower_replace(ocelot, port, cls, ingress); + return felix_update_trapping_destinations(ds, using_tag_8021q); } static int felix_cls_flower_del(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index bf8d38239e7e..33f0ceae381d 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1061,7 +1061,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) return PTR_ERR(hw); } - bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); + bus = mdiobus_alloc_size(sizeof(*mdio_priv)); if (!bus) return -ENOMEM; @@ -1081,6 +1081,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) rc = mdiobus_register(bus); if (rc < 0) { dev_err(dev, "failed to register MDIO bus\n"); + mdiobus_free(bus); return rc; } @@ -1132,6 +1133,7 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot) lynx_pcs_destroy(phylink_pcs); } mdiobus_unregister(felix->imdio); + mdiobus_free(felix->imdio); } static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 8c1c9da61602..f2f1608a476c 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -1029,7 +1029,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot) } /* Needed in order to initialize the bus mutex lock */ - rc = of_mdiobus_register(bus, NULL); + rc = devm_of_mdiobus_register(dev, bus, NULL); if (rc < 0) { dev_err(dev, "failed to register MDIO bus\n"); return rc; @@ -1083,7 +1083,8 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot) mdio_device_free(mdio_device); lynx_pcs_destroy(phylink_pcs); } - mdiobus_unregister(felix->imdio); + + /* mdiobus_unregister and mdiobus_free handled by devres */ } static const struct felix_info seville_info_vsc9953 = { diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index da0d7e68643a..e5098cfe44bc 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -378,7 +378,7 @@ static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv) if (!mnp) return -ENODEV; - ret = of_mdiobus_register(mbus, mnp); + ret = devm_of_mdiobus_register(dev, mbus, mnp); of_node_put(mnp); if (ret) return ret; @@ -499,52 +499,27 @@ static enum dsa_tag_protocol ar9331_sw_get_tag_protocol(struct dsa_switch *ds, return DSA_TAG_PROTO_AR9331; } -static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void ar9331_sw_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100; switch (port) { case 0: - if (state->interface != PHY_INTERFACE_MODE_GMII) - goto unsupported; - - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + config->mac_capabilities |= MAC_1000; break; case 1: case 2: case 3: case 4: case 5: - if (state->interface != PHY_INTERFACE_MODE_INTERNAL) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); break; - default: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported port: %i\n", port); - return; } - - phylink_set_port_modes(mask); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - return; - -unsupported: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported interface: %d, port: %d\n", - state->interface, port); } static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port, @@ -697,7 +672,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, .port_disable = ar9331_sw_port_disable, - .phylink_validate = ar9331_sw_phylink_validate, + .phylink_get_caps = ar9331_sw_phylink_get_caps, .phylink_mac_config = ar9331_sw_phylink_mac_config, .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, @@ -1091,7 +1066,6 @@ static void ar9331_sw_remove(struct mdio_device *mdiodev) } irq_domain_remove(priv->irqdomain); - mdiobus_unregister(priv->mbus); dsa_unregister_switch(&priv->ds); reset_control_assert(priv->sw_reset); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 039694518788..04fa21e37dfa 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -20,6 +20,7 @@ #include <linux/phylink.h> #include <linux/gpio/consumer.h> #include <linux/etherdevice.h> +#include <linux/dsa/tag_qca.h> #include "qca8k.h" @@ -74,12 +75,6 @@ static const struct qca8k_mib_desc ar8327_mib[] = { MIB_DESC(1, 0xac, "TXUnicast"), }; -/* The 32bit switch registers are accessed indirectly. To achieve this we need - * to set the page of the register. Track the last page that was set to reduce - * mdio writes - */ -static u16 qca8k_current_page = 0xffff; - static void qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) { @@ -94,6 +89,44 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) } static int +qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) +{ + u16 *cached_lo = &priv->mdio_cache.lo; + struct mii_bus *bus = priv->bus; + int ret; + + if (lo == *cached_lo) + return 0; + + ret = bus->write(bus, phy_id, regnum, lo); + if (ret < 0) + dev_err_ratelimited(&bus->dev, + "failed to write qca8k 32bit lo register\n"); + + *cached_lo = lo; + return 0; +} + +static int +qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) +{ + u16 *cached_hi = &priv->mdio_cache.hi; + struct mii_bus *bus = priv->bus; + int ret; + + if (hi == *cached_hi) + return 0; + + ret = bus->write(bus, phy_id, regnum, hi); + if (ret < 0) + dev_err_ratelimited(&bus->dev, + "failed to write qca8k 32bit hi register\n"); + + *cached_hi = hi; + return 0; +} + +static int qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) { int ret; @@ -116,7 +149,7 @@ qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) } static void -qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) +qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) { u16 lo, hi; int ret; @@ -124,20 +157,19 @@ qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) lo = val & 0xffff; hi = (u16)(val >> 16); - ret = bus->write(bus, phy_id, regnum, lo); + ret = qca8k_set_lo(priv, phy_id, regnum, lo); if (ret >= 0) - ret = bus->write(bus, phy_id, regnum + 1, hi); - if (ret < 0) - dev_err_ratelimited(&bus->dev, - "failed to write qca8k 32bit register\n"); + ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); } static int -qca8k_set_page(struct mii_bus *bus, u16 page) +qca8k_set_page(struct qca8k_priv *priv, u16 page) { + u16 *cached_page = &priv->mdio_cache.page; + struct mii_bus *bus = priv->bus; int ret; - if (page == qca8k_current_page) + if (page == *cached_page) return 0; ret = bus->write(bus, 0x18, 0, page); @@ -147,7 +179,7 @@ qca8k_set_page(struct mii_bus *bus, u16 page) return ret; } - qca8k_current_page = page; + *cached_page = page; usleep_range(1000, 2000); return 0; } @@ -170,6 +202,252 @@ qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) return regmap_update_bits(priv->regmap, reg, mask, write_val); } +static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) +{ + struct qca8k_mgmt_eth_data *mgmt_eth_data; + struct qca8k_priv *priv = ds->priv; + struct qca_mgmt_ethhdr *mgmt_ethhdr; + u8 len, cmd; + + mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); + mgmt_eth_data = &priv->mgmt_eth_data; + + cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); + len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); + + /* Make sure the seq match the requested packet */ + if (mgmt_ethhdr->seq == mgmt_eth_data->seq) + mgmt_eth_data->ack = true; + + if (cmd == MDIO_READ) { + mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; + + /* Get the rest of the 12 byte of data. + * The read/write function will extract the requested data. + */ + if (len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(mgmt_eth_data->data + 1, skb->data, + QCA_HDR_MGMT_DATA2_LEN); + } + + complete(&mgmt_eth_data->rw_done); +} + +static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, + int priority, unsigned int len) +{ + struct qca_mgmt_ethhdr *mgmt_ethhdr; + unsigned int real_len; + struct sk_buff *skb; + u32 *data2; + u16 hdr; + + skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); + if (!skb) + return NULL; + + /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte + * Actually for some reason the steps are: + * 0: nothing + * 1-4: first 4 byte + * 5-6: first 12 byte + * 7-15: all 16 byte + */ + if (len == 16) + real_len = 15; + else + real_len = len; + + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb->len); + + mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); + + hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); + hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); + hdr |= QCA_HDR_XMIT_FROM_CPU; + hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); + hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); + + mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, + QCA_HDR_MGMT_CHECK_CODE_VAL); + + if (cmd == MDIO_WRITE) + mgmt_ethhdr->mdio_data = *val; + + mgmt_ethhdr->hdr = htons(hdr); + + data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); + if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); + + return skb; +} + +static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) +{ + struct qca_mgmt_ethhdr *mgmt_ethhdr; + + mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; + mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); +} + +static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; + struct sk_buff *skb; + bool ack; + int ret; + + skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, + QCA8K_ETHERNET_MDIO_PRIORITY, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&mgmt_eth_data->mutex); + + /* Check mgmt_master if is operational */ + if (!priv->mgmt_master) { + kfree_skb(skb); + mutex_unlock(&mgmt_eth_data->mutex); + return -EINVAL; + } + + skb->dev = priv->mgmt_master; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the mdio pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); + + *val = mgmt_eth_data->data[0]; + if (len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); + + ack = mgmt_eth_data->ack; + + mutex_unlock(&mgmt_eth_data->mutex); + + if (ret <= 0) + return -ETIMEDOUT; + + if (!ack) + return -EINVAL; + + return 0; +} + +static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; + struct sk_buff *skb; + bool ack; + int ret; + + skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, + QCA8K_ETHERNET_MDIO_PRIORITY, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&mgmt_eth_data->mutex); + + /* Check mgmt_master if is operational */ + if (!priv->mgmt_master) { + kfree_skb(skb); + mutex_unlock(&mgmt_eth_data->mutex); + return -EINVAL; + } + + skb->dev = priv->mgmt_master; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the mdio pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); + + ack = mgmt_eth_data->ack; + + mutex_unlock(&mgmt_eth_data->mutex); + + if (ret <= 0) + return -ETIMEDOUT; + + if (!ack) + return -EINVAL; + + return 0; +} + +static int +qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +{ + u32 val = 0; + int ret; + + ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); + if (ret) + return ret; + + val &= ~mask; + val |= write_val; + + return qca8k_write_eth(priv, reg, &val, sizeof(val)); +} + +static int +qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + int i, count = len / sizeof(u32), ret; + + if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) + return 0; + + for (i = 0; i < count; i++) { + ret = regmap_read(priv->regmap, reg + (i * 4), val + i); + if (ret < 0) + return ret; + } + + return 0; +} + +static int +qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +{ + int i, count = len / sizeof(u32), ret; + u32 tmp; + + if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) + return 0; + + for (i = 0; i < count; i++) { + tmp = val[i]; + + ret = regmap_write(priv->regmap, reg + (i * 4), tmp); + if (ret < 0) + return ret; + } + + return 0; +} + static int qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) { @@ -178,11 +456,14 @@ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) u16 r1, r2, page; int ret; + if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) + return 0; + qca8k_split_addr(reg, &r1, &r2, &page); mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret < 0) goto exit; @@ -201,15 +482,18 @@ qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) u16 r1, r2, page; int ret; + if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) + return 0; + qca8k_split_addr(reg, &r1, &r2, &page); mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret < 0) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); exit: mutex_unlock(&bus->mdio_lock); @@ -225,11 +509,14 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_ u32 val; int ret; + if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) + return 0; + qca8k_split_addr(reg, &r1, &r2, &page); mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret < 0) goto exit; @@ -239,7 +526,7 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_ val &= ~mask; val |= write_val; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); exit: mutex_unlock(&bus->mdio_lock); @@ -296,17 +583,13 @@ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) { - u32 reg[4], val; - int i, ret; + u32 reg[3]; + int ret; /* load the ARL table into an array */ - for (i = 0; i < 4; i++) { - ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val); - if (ret < 0) - return ret; - - reg[i] = val; - } + ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); + if (ret) + return ret; /* vid - 83:72 */ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); @@ -330,7 +613,6 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, u8 aging) { u32 reg[3] = { 0 }; - int i; /* vid - 83:72 */ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); @@ -347,8 +629,7 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); /* load the array into the ARL table */ - for (i = 0; i < 3; i++) - qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]); + qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); } static int @@ -632,7 +913,10 @@ qca8k_mib_init(struct qca8k_priv *priv) int ret; mutex_lock(&priv->reg_mutex); - ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); + ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, + QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, + FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | + QCA8K_MIB_BUSY); if (ret) goto exit; @@ -666,6 +950,199 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); } +static int +qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, + struct sk_buff *read_skb, u32 *val) +{ + struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); + bool ack; + int ret; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the copy pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + ack = mgmt_eth_data->ack; + + if (ret <= 0) + return -ETIMEDOUT; + + if (!ack) + return -EINVAL; + + *val = mgmt_eth_data->data[0]; + + return 0; +} + +static int +qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, + int regnum, u16 data) +{ + struct sk_buff *write_skb, *clear_skb, *read_skb; + struct qca8k_mgmt_eth_data *mgmt_eth_data; + u32 write_val, clear_val = 0, val; + struct net_device *mgmt_master; + int ret, ret1; + bool ack; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; + + mgmt_eth_data = &priv->mgmt_eth_data; + + write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | + QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + + if (read) { + write_val |= QCA8K_MDIO_MASTER_READ; + } else { + write_val |= QCA8K_MDIO_MASTER_WRITE; + write_val |= QCA8K_MDIO_MASTER_DATA(data); + } + + /* Prealloc all the needed skb before the lock */ + write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); + if (!write_skb) + return -ENOMEM; + + clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); + if (!clear_skb) { + ret = -ENOMEM; + goto err_clear_skb; + } + + read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); + if (!read_skb) { + ret = -ENOMEM; + goto err_read_skb; + } + + /* Actually start the request: + * 1. Send mdio master packet + * 2. Busy Wait for mdio master command + * 3. Get the data if we are reading + * 4. Reset the mdio master (even with error) + */ + mutex_lock(&mgmt_eth_data->mutex); + + /* Check if mgmt_master is operational */ + mgmt_master = priv->mgmt_master; + if (!mgmt_master) { + mutex_unlock(&mgmt_eth_data->mutex); + ret = -EINVAL; + goto err_mgmt_master; + } + + read_skb->dev = mgmt_master; + clear_skb->dev = mgmt_master; + write_skb->dev = mgmt_master; + + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the write pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(write_skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + ack = mgmt_eth_data->ack; + + if (ret <= 0) { + ret = -ETIMEDOUT; + kfree_skb(read_skb); + goto exit; + } + + if (!ack) { + ret = -EINVAL; + kfree_skb(read_skb); + goto exit; + } + + ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, + !(val & QCA8K_MDIO_MASTER_BUSY), 0, + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, + mgmt_eth_data, read_skb, &val); + + if (ret < 0 && ret1 < 0) { + ret = ret1; + goto exit; + } + + if (read) { + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the read pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(read_skb); + + ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + ack = mgmt_eth_data->ack; + + if (ret <= 0) { + ret = -ETIMEDOUT; + goto exit; + } + + if (!ack) { + ret = -EINVAL; + goto exit; + } + + ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; + } else { + kfree_skb(read_skb); + } +exit: + reinit_completion(&mgmt_eth_data->rw_done); + + /* Increment seq_num and set it in the clear pkt */ + mgmt_eth_data->seq++; + qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); + mgmt_eth_data->ack = false; + + dev_queue_xmit(clear_skb); + + wait_for_completion_timeout(&mgmt_eth_data->rw_done, + QCA8K_ETHERNET_TIMEOUT); + + mutex_unlock(&mgmt_eth_data->mutex); + + return ret; + + /* Error handling before lock */ +err_mgmt_master: + kfree_skb(read_skb); +err_read_skb: + kfree_skb(clear_skb); +err_clear_skb: + kfree_skb(write_skb); + + return ret; +} + static u32 qca8k_port_to_phy(int port) { @@ -704,8 +1181,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) } static int -qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) +qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) { + struct mii_bus *bus = priv->bus; u16 r1, r2, page; u32 val; int ret; @@ -722,18 +1200,18 @@ qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, QCA8K_MDIO_MASTER_BUSY); exit: /* even if the busy_wait timeouts try to clear the MASTER_EN */ - qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + qca8k_mii_write32(priv, 0x10 | r2, r1, 0); mutex_unlock(&bus->mdio_lock); @@ -741,8 +1219,9 @@ exit: } static int -qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) +qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) { + struct mii_bus *bus = priv->bus; u16 r1, r2, page; u32 val; int ret; @@ -758,11 +1237,11 @@ qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - ret = qca8k_set_page(bus, page); + ret = qca8k_set_page(priv, page); if (ret) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + qca8k_mii_write32(priv, 0x10 | r2, r1, val); ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, QCA8K_MDIO_MASTER_BUSY); @@ -773,7 +1252,7 @@ qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) exit: /* even if the busy_wait timeouts try to clear the MASTER_EN */ - qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + qca8k_mii_write32(priv, 0x10 | r2, r1, 0); mutex_unlock(&bus->mdio_lock); @@ -787,24 +1266,35 @@ static int qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) { struct qca8k_priv *priv = slave_bus->priv; - struct mii_bus *bus = priv->bus; + int ret; - return qca8k_mdio_write(bus, phy, regnum, data); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); + if (!ret) + return 0; + + return qca8k_mdio_write(priv, phy, regnum, data); } static int qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) { struct qca8k_priv *priv = slave_bus->priv; - struct mii_bus *bus = priv->bus; + int ret; + + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); + if (ret >= 0) + return ret; - return qca8k_mdio_read(bus, phy, regnum); + return qca8k_mdio_read(priv, phy, regnum); } static int qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) { struct qca8k_priv *priv = ds->priv; + int ret; /* Check if the legacy mapping should be used and the * port is not correctly mapped to the right PHY in the @@ -813,7 +1303,12 @@ qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) if (priv->legacy_phy_port_mapping) port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - return qca8k_mdio_write(priv->bus, port, regnum, data); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, false, port, regnum, 0); + if (!ret) + return ret; + + return qca8k_mdio_write(priv, port, regnum, data); } static int @@ -829,7 +1324,12 @@ qca8k_phy_read(struct dsa_switch *ds, int port, int regnum) if (priv->legacy_phy_port_mapping) port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - ret = qca8k_mdio_read(priv->bus, port, regnum); + /* Use mdio Ethernet when available, fallback to legacy one on error */ + ret = qca8k_phy_eth_command(priv, true, port, regnum, 0); + if (ret >= 0) + return ret; + + ret = qca8k_mdio_read(priv, port, regnum); if (ret < 0) return 0xffff; @@ -1132,220 +1632,6 @@ qca8k_parse_port_config(struct qca8k_priv *priv) return 0; } -static int -qca8k_setup(struct dsa_switch *ds) -{ - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - int cpu_port, ret, i; - u32 mask; - - cpu_port = qca8k_find_cpu_port(ds); - if (cpu_port < 0) { - dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); - return cpu_port; - } - - /* Parse CPU port config to be later used in phy_link mac_config */ - ret = qca8k_parse_port_config(priv); - if (ret) - return ret; - - ret = qca8k_setup_mdio_bus(priv); - if (ret) - return ret; - - ret = qca8k_setup_of_pws_reg(priv); - if (ret) - return ret; - - ret = qca8k_setup_mac_pwr_sel(priv); - if (ret) - return ret; - - /* Make sure MAC06 is disabled */ - ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, - QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); - if (ret) { - dev_err(priv->dev, "failed disabling MAC06 exchange"); - return ret; - } - - /* Enable CPU Port */ - ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, - QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); - if (ret) { - dev_err(priv->dev, "failed enabling CPU port"); - return ret; - } - - /* Enable MIB counters */ - ret = qca8k_mib_init(priv); - if (ret) - dev_warn(priv->dev, "mib init failed"); - - /* Initial setup of all ports */ - for (i = 0; i < QCA8K_NUM_PORTS; i++) { - /* Disable forwarding by default on all ports */ - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, 0); - if (ret) - return ret; - - /* Enable QCA header mode on all cpu ports */ - if (dsa_is_cpu_port(ds, i)) { - ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), - FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | - FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); - if (ret) { - dev_err(priv->dev, "failed enabling QCA header mode"); - return ret; - } - } - - /* Disable MAC by default on all user ports */ - if (dsa_is_user_port(ds, i)) - qca8k_port_set_status(priv, i, 0); - } - - /* Forward all unknown frames to CPU port for Linux processing - * Notice that in multi-cpu config only one port should be set - * for igmp, unknown, multicast and broadcast packet - */ - ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | - FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); - if (ret) - return ret; - - /* Setup connection between CPU port & user ports - * Configure specific switch configuration for ports - */ - for (i = 0; i < QCA8K_NUM_PORTS; i++) { - /* CPU port gets connected to all user ports of the switch */ - if (dsa_is_cpu_port(ds, i)) { - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); - if (ret) - return ret; - } - - /* Individual user ports get connected to CPU port only */ - if (dsa_is_user_port(ds, i)) { - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, - BIT(cpu_port)); - if (ret) - return ret; - - /* Enable ARP Auto-learning by default */ - ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_LEARN); - if (ret) - return ret; - - /* For port based vlans to work we need to set the - * default egress vid - */ - ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), - QCA8K_EGREES_VLAN_PORT_MASK(i), - QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); - if (ret) - return ret; - - ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), - QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | - QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); - if (ret) - return ret; - } - - /* The port 5 of the qca8337 have some problem in flood condition. The - * original legacy driver had some specific buffer and priority settings - * for the different port suggested by the QCA switch team. Add this - * missing settings to improve switch stability under load condition. - * This problem is limited to qca8337 and other qca8k switch are not affected. - */ - if (priv->switch_id == QCA8K_ID_QCA8337) { - switch (i) { - /* The 2 CPU port and port 5 requires some different - * priority than any other ports. - */ - case 0: - case 5: - case 6: - mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | - QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | - QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | - QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); - break; - default: - mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | - QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | - QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | - QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); - } - qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); - - mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN; - qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), - QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN, - mask); - } - - /* Set initial MTU for every port. - * We have only have a general MTU setting. So track - * every port and set the max across all port. - * Set per port MTU to 1500 as the MTU change function - * will add the overhead and if its set to 1518 then it - * will apply the overhead again and we will end up with - * MTU of 1536 instead of 1518 - */ - priv->port_mtu[i] = ETH_DATA_LEN; - } - - /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ - if (priv->switch_id == QCA8K_ID_QCA8327) { - mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | - QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); - qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, - QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | - QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, - mask); - } - - /* Setup our port MTUs to match power on defaults */ - ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); - if (ret) - dev_warn(priv->dev, "failed setting MTU settings"); - - /* Flush the FDB table */ - qca8k_fdb_flush(priv); - - /* We don't have interrupts for link changes, so we need to poll */ - ds->pcs_poll = true; - - /* Set min a max ageing value supported */ - ds->ageing_time_min = 7000; - ds->ageing_time_max = 458745000; - - /* Set max number of LAGs supported */ - ds->num_lag_ids = QCA8K_NUM_LAGS; - - return 0; -} - static void qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, u32 reg) @@ -1387,13 +1673,41 @@ qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_inde cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); } +static struct phylink_pcs * +qca8k_phylink_mac_select_pcs(struct dsa_switch *ds, int port, + phy_interface_t interface) +{ + struct qca8k_priv *priv = ds->priv; + struct phylink_pcs *pcs = NULL; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + switch (port) { + case 0: + pcs = &priv->pcs_port_0.pcs; + break; + + case 6: + pcs = &priv->pcs_port_6.pcs; + break; + } + break; + + default: + break; + } + + return pcs; +} + static void qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { struct qca8k_priv *priv = ds->priv; - int cpu_port_index, ret; - u32 reg, val; + int cpu_port_index; + u32 reg; switch (port) { case 0: /* 1st CPU port */ @@ -1459,70 +1773,6 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, case PHY_INTERFACE_MODE_1000BASEX: /* Enable SGMII on the port */ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); - - /* Enable/disable SerDes auto-negotiation as necessary */ - ret = qca8k_read(priv, QCA8K_REG_PWS, &val); - if (ret) - return; - if (phylink_autoneg_inband(mode)) - val &= ~QCA8K_PWS_SERDES_AEN_DIS; - else - val |= QCA8K_PWS_SERDES_AEN_DIS; - qca8k_write(priv, QCA8K_REG_PWS, val); - - /* Configure the SGMII parameters */ - ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); - if (ret) - return; - - val |= QCA8K_SGMII_EN_SD; - - if (priv->ports_config.sgmii_enable_pll) - val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | - QCA8K_SGMII_EN_TX; - - if (dsa_is_cpu_port(ds, port)) { - /* CPU port, we're talking to the CPU MAC, be a PHY */ - val &= ~QCA8K_SGMII_MODE_CTRL_MASK; - val |= QCA8K_SGMII_MODE_CTRL_PHY; - } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { - val &= ~QCA8K_SGMII_MODE_CTRL_MASK; - val |= QCA8K_SGMII_MODE_CTRL_MAC; - } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { - val &= ~QCA8K_SGMII_MODE_CTRL_MASK; - val |= QCA8K_SGMII_MODE_CTRL_BASEX; - } - - qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); - - /* From original code is reported port instability as SGMII also - * require delay set. Apply advised values here or take them from DT. - */ - if (state->interface == PHY_INTERFACE_MODE_SGMII) - qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); - - /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and - * falling edge is set writing in the PORT0 PAD reg - */ - if (priv->switch_id == QCA8K_ID_QCA8327 || - priv->switch_id == QCA8K_ID_QCA8337) - reg = QCA8K_REG_PORT0_PAD_CTRL; - - val = 0; - - /* SGMII Clock phase configuration */ - if (priv->ports_config.sgmii_rx_clk_falling_edge) - val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; - - if (priv->ports_config.sgmii_tx_clk_falling_edge) - val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; - - if (val) - ret = qca8k_rmw(priv, reg, - QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | - QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, - val); - break; default: dev_err(ds->dev, "xMII mode %s not supported for port %d\n", @@ -1531,109 +1781,41 @@ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, } } -static void -qca8k_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void qca8k_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - switch (port) { case 0: /* 1st CPU port */ - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != PHY_INTERFACE_MODE_RGMII && - state->interface != PHY_INTERFACE_MODE_RGMII_ID && - state->interface != PHY_INTERFACE_MODE_RGMII_TXID && - state->interface != PHY_INTERFACE_MODE_RGMII_RXID && - state->interface != PHY_INTERFACE_MODE_SGMII) - goto unsupported; + phy_interface_set_rgmii(config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); break; + case 1: case 2: case 3: case 4: case 5: /* Internal PHY */ - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != PHY_INTERFACE_MODE_GMII && - state->interface != PHY_INTERFACE_MODE_INTERNAL) - goto unsupported; - break; - case 6: /* 2nd CPU port / external PHY */ - if (state->interface != PHY_INTERFACE_MODE_NA && - state->interface != PHY_INTERFACE_MODE_RGMII && - state->interface != PHY_INTERFACE_MODE_RGMII_ID && - state->interface != PHY_INTERFACE_MODE_RGMII_TXID && - state->interface != PHY_INTERFACE_MODE_RGMII_RXID && - state->interface != PHY_INTERFACE_MODE_SGMII && - state->interface != PHY_INTERFACE_MODE_1000BASEX) - goto unsupported; + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); break; - default: -unsupported: - linkmode_zero(supported); - return; - } - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) - phylink_set(mask, 1000baseX_Full); - - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); -} - -static int -qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) -{ - struct qca8k_priv *priv = ds->priv; - u32 reg; - int ret; - - ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); - if (ret < 0) - return ret; - - state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); - state->an_complete = state->link; - state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); - state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : - DUPLEX_HALF; - - switch (reg & QCA8K_PORT_STATUS_SPEED) { - case QCA8K_PORT_STATUS_SPEED_10: - state->speed = SPEED_10; - break; - case QCA8K_PORT_STATUS_SPEED_100: - state->speed = SPEED_100; - break; - case QCA8K_PORT_STATUS_SPEED_1000: - state->speed = SPEED_1000; - break; - default: - state->speed = SPEED_UNKNOWN; + case 6: /* 2nd CPU port / external PHY */ + phy_interface_set_rgmii(config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); break; } - state->pause = MLO_PAUSE_NONE; - if (reg & QCA8K_PORT_STATUS_RXFLOW) - state->pause |= MLO_PAUSE_RX; - if (reg & QCA8K_PORT_STATUS_TXFLOW) - state->pause |= MLO_PAUSE_TX; + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; - return 1; + config->legacy_pre_march2020 = false; } static void @@ -1686,6 +1868,163 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); } +static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct qca8k_pcs, pcs); +} + +static void qca8k_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv; + int port = pcs_to_qca8k_pcs(pcs)->port; + u32 reg; + int ret; + + ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); + if (ret < 0) { + state->link = false; + return; + } + + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); + state->an_complete = state->link; + state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); + state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : + DUPLEX_HALF; + + switch (reg & QCA8K_PORT_STATUS_SPEED) { + case QCA8K_PORT_STATUS_SPEED_10: + state->speed = SPEED_10; + break; + case QCA8K_PORT_STATUS_SPEED_100: + state->speed = SPEED_100; + break; + case QCA8K_PORT_STATUS_SPEED_1000: + state->speed = SPEED_1000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + if (reg & QCA8K_PORT_STATUS_RXFLOW) + state->pause |= MLO_PAUSE_RX; + if (reg & QCA8K_PORT_STATUS_TXFLOW) + state->pause |= MLO_PAUSE_TX; +} + +static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv; + int cpu_port_index, ret, port; + u32 reg, val; + + port = pcs_to_qca8k_pcs(pcs)->port; + switch (port) { + case 0: + reg = QCA8K_REG_PORT0_PAD_CTRL; + cpu_port_index = QCA8K_CPU_PORT0; + break; + + case 6: + reg = QCA8K_REG_PORT6_PAD_CTRL; + cpu_port_index = QCA8K_CPU_PORT6; + break; + + default: + WARN_ON(1); + } + + /* Enable/disable SerDes auto-negotiation as necessary */ + ret = qca8k_read(priv, QCA8K_REG_PWS, &val); + if (ret) + return ret; + if (phylink_autoneg_inband(mode)) + val &= ~QCA8K_PWS_SERDES_AEN_DIS; + else + val |= QCA8K_PWS_SERDES_AEN_DIS; + qca8k_write(priv, QCA8K_REG_PWS, val); + + /* Configure the SGMII parameters */ + ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); + if (ret) + return ret; + + val |= QCA8K_SGMII_EN_SD; + + if (priv->ports_config.sgmii_enable_pll) + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX; + + if (dsa_is_cpu_port(priv->ds, port)) { + /* CPU port, we're talking to the CPU MAC, be a PHY */ + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_PHY; + } else if (interface == PHY_INTERFACE_MODE_SGMII) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_MAC; + } else if (interface == PHY_INTERFACE_MODE_1000BASEX) { + val &= ~QCA8K_SGMII_MODE_CTRL_MASK; + val |= QCA8K_SGMII_MODE_CTRL_BASEX; + } + + qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); + + /* From original code is reported port instability as SGMII also + * require delay set. Apply advised values here or take them from DT. + */ + if (interface == PHY_INTERFACE_MODE_SGMII) + qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); + /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and + * falling edge is set writing in the PORT0 PAD reg + */ + if (priv->switch_id == QCA8K_ID_QCA8327 || + priv->switch_id == QCA8K_ID_QCA8337) + reg = QCA8K_REG_PORT0_PAD_CTRL; + + val = 0; + + /* SGMII Clock phase configuration */ + if (priv->ports_config.sgmii_rx_clk_falling_edge) + val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; + + if (priv->ports_config.sgmii_tx_clk_falling_edge) + val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; + + if (val) + ret = qca8k_rmw(priv, reg, + QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | + QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, + val); + + return 0; +} + +static void qca8k_pcs_an_restart(struct phylink_pcs *pcs) +{ +} + +static const struct phylink_pcs_ops qca8k_pcs_ops = { + .pcs_get_state = qca8k_pcs_get_state, + .pcs_config = qca8k_pcs_config, + .pcs_an_restart = qca8k_pcs_an_restart, +}; + +static void qca8k_setup_pcs(struct qca8k_priv *priv, struct qca8k_pcs *qpcs, + int port) +{ + qpcs->pcs.ops = &qca8k_pcs_ops; + + /* We don't have interrupts for link changes, so we need to poll */ + qpcs->pcs.poll = true; + qpcs->priv = priv; + qpcs->port = port; +} + static void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) { @@ -1703,6 +2042,97 @@ qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) ETH_GSTRING_LEN); } +static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) +{ + const struct qca8k_match_data *match_data; + struct qca8k_mib_eth_data *mib_eth_data; + struct qca8k_priv *priv = ds->priv; + const struct qca8k_mib_desc *mib; + struct mib_ethhdr *mib_ethhdr; + int i, mib_len, offset = 0; + u64 *data; + u8 port; + + mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); + mib_eth_data = &priv->mib_eth_data; + + /* The switch autocast every port. Ignore other packet and + * parse only the requested one. + */ + port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); + if (port != mib_eth_data->req_port) + goto exit; + + match_data = device_get_match_data(priv->dev); + data = mib_eth_data->data; + + for (i = 0; i < match_data->mib_count; i++) { + mib = &ar8327_mib[i]; + + /* First 3 mib are present in the skb head */ + if (i < 3) { + data[i] = mib_ethhdr->data[i]; + continue; + } + + mib_len = sizeof(uint32_t); + + /* Some mib are 64 bit wide */ + if (mib->size == 2) + mib_len = sizeof(uint64_t); + + /* Copy the mib value from packet to the */ + memcpy(data + i, skb->data + offset, mib_len); + + /* Set the offset for the next mib */ + offset += mib_len; + } + +exit: + /* Complete on receiving all the mib packet */ + if (refcount_dec_and_test(&mib_eth_data->port_parsed)) + complete(&mib_eth_data->rw_done); +} + +static int +qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct qca8k_mib_eth_data *mib_eth_data; + struct qca8k_priv *priv = ds->priv; + int ret; + + mib_eth_data = &priv->mib_eth_data; + + mutex_lock(&mib_eth_data->mutex); + + reinit_completion(&mib_eth_data->rw_done); + + mib_eth_data->req_port = dp->index; + mib_eth_data->data = data; + refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); + + mutex_lock(&priv->reg_mutex); + + /* Send mib autocast request */ + ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, + QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, + FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | + QCA8K_MIB_BUSY); + + mutex_unlock(&priv->reg_mutex); + + if (ret) + goto exit; + + ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); + +exit: + mutex_unlock(&mib_eth_data->mutex); + + return ret; +} + static void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) @@ -1714,6 +2144,10 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, u32 hi = 0; int ret; + if (priv->mgmt_master && + qca8k_get_ethtool_stats_eth(ds, port, data) > 0) + return; + match_data = of_device_get_match_data(priv->dev); for (i = 0; i < match_data->mib_count; i++) { @@ -2383,6 +2817,260 @@ qca8k_port_lag_leave(struct dsa_switch *ds, int port, return qca8k_lag_refresh_portmap(ds, port, lag, true); } +static void +qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, + bool operational) +{ + struct dsa_port *dp = master->dsa_ptr; + struct qca8k_priv *priv = ds->priv; + + /* Ethernet MIB/MDIO is only supported for CPU port 0 */ + if (dp->index != 0) + return; + + mutex_lock(&priv->mgmt_eth_data.mutex); + mutex_lock(&priv->mib_eth_data.mutex); + + priv->mgmt_master = operational ? (struct net_device *)master : NULL; + + mutex_unlock(&priv->mib_eth_data.mutex); + mutex_unlock(&priv->mgmt_eth_data.mutex); +} + +static int qca8k_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct qca_tagger_data *tagger_data; + + switch (proto) { + case DSA_TAG_PROTO_QCA: + tagger_data = ds->tagger_data; + + tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; + tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; + + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int +qca8k_setup(struct dsa_switch *ds) +{ + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + int cpu_port, ret, i; + u32 mask; + + cpu_port = qca8k_find_cpu_port(ds); + if (cpu_port < 0) { + dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); + return cpu_port; + } + + /* Parse CPU port config to be later used in phy_link mac_config */ + ret = qca8k_parse_port_config(priv); + if (ret) + return ret; + + ret = qca8k_setup_mdio_bus(priv); + if (ret) + return ret; + + ret = qca8k_setup_of_pws_reg(priv); + if (ret) + return ret; + + ret = qca8k_setup_mac_pwr_sel(priv); + if (ret) + return ret; + + qca8k_setup_pcs(priv, &priv->pcs_port_0, 0); + qca8k_setup_pcs(priv, &priv->pcs_port_6, 6); + + /* Make sure MAC06 is disabled */ + ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, + QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); + if (ret) { + dev_err(priv->dev, "failed disabling MAC06 exchange"); + return ret; + } + + /* Enable CPU Port */ + ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); + if (ret) { + dev_err(priv->dev, "failed enabling CPU port"); + return ret; + } + + /* Enable MIB counters */ + ret = qca8k_mib_init(priv); + if (ret) + dev_warn(priv->dev, "mib init failed"); + + /* Initial setup of all ports */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* Disable forwarding by default on all ports */ + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, 0); + if (ret) + return ret; + + /* Enable QCA header mode on all cpu ports */ + if (dsa_is_cpu_port(ds, i)) { + ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), + FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | + FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); + if (ret) { + dev_err(priv->dev, "failed enabling QCA header mode"); + return ret; + } + } + + /* Disable MAC by default on all user ports */ + if (dsa_is_user_port(ds, i)) + qca8k_port_set_status(priv, i, 0); + } + + /* Forward all unknown frames to CPU port for Linux processing + * Notice that in multi-cpu config only one port should be set + * for igmp, unknown, multicast and broadcast packet + */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); + if (ret) + return ret; + + /* Setup connection between CPU port & user ports + * Configure specific switch configuration for ports + */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* CPU port gets connected to all user ports of the switch */ + if (dsa_is_cpu_port(ds, i)) { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); + if (ret) + return ret; + } + + /* Individual user ports get connected to CPU port only */ + if (dsa_is_user_port(ds, i)) { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, + BIT(cpu_port)); + if (ret) + return ret; + + /* Enable ARP Auto-learning by default */ + ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_LEARN); + if (ret) + return ret; + + /* For port based vlans to work we need to set the + * default egress vid + */ + ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), + QCA8K_EGREES_VLAN_PORT_MASK(i), + QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); + if (ret) + return ret; + + ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); + if (ret) + return ret; + } + + /* The port 5 of the qca8337 have some problem in flood condition. The + * original legacy driver had some specific buffer and priority settings + * for the different port suggested by the QCA switch team. Add this + * missing settings to improve switch stability under load condition. + * This problem is limited to qca8337 and other qca8k switch are not affected. + */ + if (priv->switch_id == QCA8K_ID_QCA8337) { + switch (i) { + /* The 2 CPU port and port 5 requires some different + * priority than any other ports. + */ + case 0: + case 5: + case 6: + mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | + QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | + QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | + QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); + break; + default: + mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | + QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | + QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | + QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); + } + qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); + + mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN; + qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), + QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN, + mask); + } + + /* Set initial MTU for every port. + * We have only have a general MTU setting. So track + * every port and set the max across all port. + * Set per port MTU to 1500 as the MTU change function + * will add the overhead and if its set to 1518 then it + * will apply the overhead again and we will end up with + * MTU of 1536 instead of 1518 + */ + priv->port_mtu[i] = ETH_DATA_LEN; + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ + if (priv->switch_id == QCA8K_ID_QCA8327) { + mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | + QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); + qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, + QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | + QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, + mask); + } + + /* Setup our port MTUs to match power on defaults */ + ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); + if (ret) + dev_warn(priv->dev, "failed setting MTU settings"); + + /* Flush the FDB table */ + qca8k_fdb_flush(priv); + + /* Set min a max ageing value supported */ + ds->ageing_time_min = 7000; + ds->ageing_time_max = 458745000; + + /* Set max number of LAGs supported */ + ds->num_lag_ids = QCA8K_NUM_LAGS; + + return 0; +} + static const struct dsa_switch_ops qca8k_switch_ops = { .get_tag_protocol = qca8k_get_tag_protocol, .setup = qca8k_setup, @@ -2410,14 +3098,16 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_vlan_filtering = qca8k_port_vlan_filtering, .port_vlan_add = qca8k_port_vlan_add, .port_vlan_del = qca8k_port_vlan_del, - .phylink_validate = qca8k_phylink_validate, - .phylink_mac_link_state = qca8k_phylink_mac_link_state, + .phylink_get_caps = qca8k_phylink_get_caps, + .phylink_mac_select_pcs = qca8k_phylink_mac_select_pcs, .phylink_mac_config = qca8k_phylink_mac_config, .phylink_mac_link_down = qca8k_phylink_mac_link_down, .phylink_mac_link_up = qca8k_phylink_mac_link_up, .get_phy_flags = qca8k_get_phy_flags, .port_lag_join = qca8k_port_lag_join, .port_lag_leave = qca8k_port_lag_leave, + .master_state_change = qca8k_master_change, + .connect_tag_protocol = qca8k_connect_tag_protocol, }; static int qca8k_read_switch_id(struct qca8k_priv *priv) @@ -2488,6 +3178,10 @@ qca8k_sw_probe(struct mdio_device *mdiodev) return PTR_ERR(priv->regmap); } + priv->mdio_cache.page = 0xffff; + priv->mdio_cache.lo = 0xffff; + priv->mdio_cache.hi = 0xffff; + /* Check the detected switch id */ ret = qca8k_read_switch_id(priv); if (ret) @@ -2497,6 +3191,12 @@ qca8k_sw_probe(struct mdio_device *mdiodev) if (!priv->ds) return -ENOMEM; + mutex_init(&priv->mgmt_eth_data.mutex); + init_completion(&priv->mgmt_eth_data.rw_done); + + mutex_init(&priv->mib_eth_data.mutex); + init_completion(&priv->mib_eth_data.rw_done); + priv->ds->dev = &mdiodev->dev; priv->ds->num_ports = QCA8K_NUM_PORTS; priv->ds->priv = priv; diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index ab4a417b25a9..f375627174c8 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -11,6 +11,11 @@ #include <linux/delay.h> #include <linux/regmap.h> #include <linux/gpio.h> +#include <linux/dsa/tag_qca.h> + +#define QCA8K_ETHERNET_MDIO_PRIORITY 7 +#define QCA8K_ETHERNET_PHY_PRIORITY 6 +#define QCA8K_ETHERNET_TIMEOUT 100 #define QCA8K_NUM_PORTS 7 #define QCA8K_NUM_CPU_PORTS 2 @@ -63,7 +68,7 @@ #define QCA8K_REG_MODULE_EN 0x030 #define QCA8K_MODULE_EN_MIB BIT(0) #define QCA8K_REG_MIB 0x034 -#define QCA8K_MIB_FLUSH BIT(24) +#define QCA8K_MIB_FUNC GENMASK(26, 24) #define QCA8K_MIB_CPU_KEEP BIT(20) #define QCA8K_MIB_BUSY BIT(17) #define QCA8K_MDIO_MASTER_CTRL 0x3c @@ -313,6 +318,12 @@ enum qca8k_vlan_cmd { QCA8K_VLAN_READ = 6, }; +enum qca8k_mid_cmd { + QCA8K_MIB_FLUSH = 1, + QCA8K_MIB_FLUSH_PORT = 2, + QCA8K_MIB_CAST = 3, +}; + struct ar8xxx_port_status { int enabled; }; @@ -328,6 +339,22 @@ enum { QCA8K_CPU_PORT6, }; +struct qca8k_mgmt_eth_data { + struct completion rw_done; + struct mutex mutex; /* Enforce one mdio read/write at time */ + bool ack; + u32 seq; + u32 data[4]; +}; + +struct qca8k_mib_eth_data { + struct completion rw_done; + struct mutex mutex; /* Process one command at time */ + refcount_t port_parsed; /* Counter to track parsed port */ + u8 req_port; + u64 *data; /* pointer to ethtool data */ +}; + struct qca8k_ports_config { bool sgmii_rx_clk_falling_edge; bool sgmii_tx_clk_falling_edge; @@ -336,6 +363,25 @@ struct qca8k_ports_config { u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ }; +struct qca8k_mdio_cache { +/* The 32bit switch registers are accessed indirectly. To achieve this we need + * to set the page of the register. Track the last page that was set to reduce + * mdio writes + */ + u16 page; +/* lo and hi can also be cached and from Documentation we can skip one + * extra mdio write if lo or hi is didn't change. + */ + u16 lo; + u16 hi; +}; + +struct qca8k_pcs { + struct phylink_pcs pcs; + struct qca8k_priv *priv; + int port; +}; + struct qca8k_priv { u8 switch_id; u8 switch_revision; @@ -353,6 +399,12 @@ struct qca8k_priv { struct dsa_switch_ops ops; struct gpio_desc *reset_gpio; unsigned int port_mtu[QCA8K_NUM_PORTS]; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; + struct qca8k_mdio_cache mdio_cache; + struct qca8k_pcs pcs_port_0; + struct qca8k_pcs pcs_port_6; }; struct qca8k_mib_desc { diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c deleted file mode 100644 index aae46ada8d83..000000000000 --- a/drivers/net/dsa/realtek-smi-core.c +++ /dev/null @@ -1,523 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* Realtek Simple Management Interface (SMI) driver - * It can be discussed how "simple" this interface is. - * - * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels - * but the protocol is not MDIO at all. Instead it is a Realtek - * pecularity that need to bit-bang the lines in a special way to - * communicate with the switch. - * - * ASICs we intend to support with this driver: - * - * RTL8366 - The original version, apparently - * RTL8369 - Similar enough to have the same datsheet as RTL8366 - * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite - * different register layout from the other two - * RTL8366S - Is this "RTL8366 super"? - * RTL8367 - Has an OpenWRT driver as well - * RTL8368S - Seems to be an alternative name for RTL8366RB - * RTL8370 - Also uses SMI - * - * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> - * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> - * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> - * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> - * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/spinlock.h> -#include <linux/skbuff.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_mdio.h> -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> -#include <linux/bitops.h> -#include <linux/if_bridge.h> - -#include "realtek-smi-core.h" - -#define REALTEK_SMI_ACK_RETRY_COUNT 5 -#define REALTEK_SMI_HW_STOP_DELAY 25 /* msecs */ -#define REALTEK_SMI_HW_START_DELAY 100 /* msecs */ - -static inline void realtek_smi_clk_delay(struct realtek_smi *smi) -{ - ndelay(smi->clk_delay); -} - -static void realtek_smi_start(struct realtek_smi *smi) -{ - /* Set GPIO pins to output mode, with initial state: - * SCK = 0, SDA = 1 - */ - gpiod_direction_output(smi->mdc, 0); - gpiod_direction_output(smi->mdio, 1); - realtek_smi_clk_delay(smi); - - /* CLK 1: 0 -> 1, 1 -> 0 */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - - /* CLK 2: */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 1); -} - -static void realtek_smi_stop(struct realtek_smi *smi) -{ - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 0); - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdio, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 1); - - /* Add a click */ - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 1); - - /* Set GPIO pins to input mode */ - gpiod_direction_input(smi->mdio); - gpiod_direction_input(smi->mdc); -} - -static void realtek_smi_write_bits(struct realtek_smi *smi, u32 data, u32 len) -{ - for (; len > 0; len--) { - realtek_smi_clk_delay(smi); - - /* Prepare data */ - gpiod_set_value(smi->mdio, !!(data & (1 << (len - 1)))); - realtek_smi_clk_delay(smi); - - /* Clocking */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - gpiod_set_value(smi->mdc, 0); - } -} - -static void realtek_smi_read_bits(struct realtek_smi *smi, u32 len, u32 *data) -{ - gpiod_direction_input(smi->mdio); - - for (*data = 0; len > 0; len--) { - u32 u; - - realtek_smi_clk_delay(smi); - - /* Clocking */ - gpiod_set_value(smi->mdc, 1); - realtek_smi_clk_delay(smi); - u = !!gpiod_get_value(smi->mdio); - gpiod_set_value(smi->mdc, 0); - - *data |= (u << (len - 1)); - } - - gpiod_direction_output(smi->mdio, 0); -} - -static int realtek_smi_wait_for_ack(struct realtek_smi *smi) -{ - int retry_cnt; - - retry_cnt = 0; - do { - u32 ack; - - realtek_smi_read_bits(smi, 1, &ack); - if (ack == 0) - break; - - if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { - dev_err(smi->dev, "ACK timeout\n"); - return -ETIMEDOUT; - } - } while (1); - - return 0; -} - -static int realtek_smi_write_byte(struct realtek_smi *smi, u8 data) -{ - realtek_smi_write_bits(smi, data, 8); - return realtek_smi_wait_for_ack(smi); -} - -static int realtek_smi_write_byte_noack(struct realtek_smi *smi, u8 data) -{ - realtek_smi_write_bits(smi, data, 8); - return 0; -} - -static int realtek_smi_read_byte0(struct realtek_smi *smi, u8 *data) -{ - u32 t; - - /* Read data */ - realtek_smi_read_bits(smi, 8, &t); - *data = (t & 0xff); - - /* Send an ACK */ - realtek_smi_write_bits(smi, 0x00, 1); - - return 0; -} - -static int realtek_smi_read_byte1(struct realtek_smi *smi, u8 *data) -{ - u32 t; - - /* Read data */ - realtek_smi_read_bits(smi, 8, &t); - *data = (t & 0xff); - - /* Send an ACK */ - realtek_smi_write_bits(smi, 0x01, 1); - - return 0; -} - -static int realtek_smi_read_reg(struct realtek_smi *smi, u32 addr, u32 *data) -{ - unsigned long flags; - u8 lo = 0; - u8 hi = 0; - int ret; - - spin_lock_irqsave(&smi->lock, flags); - - realtek_smi_start(smi); - - /* Send READ command */ - ret = realtek_smi_write_byte(smi, smi->cmd_read); - if (ret) - goto out; - - /* Set ADDR[7:0] */ - ret = realtek_smi_write_byte(smi, addr & 0xff); - if (ret) - goto out; - - /* Set ADDR[15:8] */ - ret = realtek_smi_write_byte(smi, addr >> 8); - if (ret) - goto out; - - /* Read DATA[7:0] */ - realtek_smi_read_byte0(smi, &lo); - /* Read DATA[15:8] */ - realtek_smi_read_byte1(smi, &hi); - - *data = ((u32)lo) | (((u32)hi) << 8); - - ret = 0; - - out: - realtek_smi_stop(smi); - spin_unlock_irqrestore(&smi->lock, flags); - - return ret; -} - -static int realtek_smi_write_reg(struct realtek_smi *smi, - u32 addr, u32 data, bool ack) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&smi->lock, flags); - - realtek_smi_start(smi); - - /* Send WRITE command */ - ret = realtek_smi_write_byte(smi, smi->cmd_write); - if (ret) - goto out; - - /* Set ADDR[7:0] */ - ret = realtek_smi_write_byte(smi, addr & 0xff); - if (ret) - goto out; - - /* Set ADDR[15:8] */ - ret = realtek_smi_write_byte(smi, addr >> 8); - if (ret) - goto out; - - /* Write DATA[7:0] */ - ret = realtek_smi_write_byte(smi, data & 0xff); - if (ret) - goto out; - - /* Write DATA[15:8] */ - if (ack) - ret = realtek_smi_write_byte(smi, data >> 8); - else - ret = realtek_smi_write_byte_noack(smi, data >> 8); - if (ret) - goto out; - - ret = 0; - - out: - realtek_smi_stop(smi); - spin_unlock_irqrestore(&smi->lock, flags); - - return ret; -} - -/* There is one single case when we need to use this accessor and that - * is when issueing soft reset. Since the device reset as soon as we write - * that bit, no ACK will come back for natural reasons. - */ -int realtek_smi_write_reg_noack(struct realtek_smi *smi, u32 addr, - u32 data) -{ - return realtek_smi_write_reg(smi, addr, data, false); -} -EXPORT_SYMBOL_GPL(realtek_smi_write_reg_noack); - -/* Regmap accessors */ - -static int realtek_smi_write(void *ctx, u32 reg, u32 val) -{ - struct realtek_smi *smi = ctx; - - return realtek_smi_write_reg(smi, reg, val, true); -} - -static int realtek_smi_read(void *ctx, u32 reg, u32 *val) -{ - struct realtek_smi *smi = ctx; - - return realtek_smi_read_reg(smi, reg, val); -} - -static const struct regmap_config realtek_smi_mdio_regmap_config = { - .reg_bits = 10, /* A4..A0 R4..R0 */ - .val_bits = 16, - .reg_stride = 1, - /* PHY regs are at 0x8000 */ - .max_register = 0xffff, - .reg_format_endian = REGMAP_ENDIAN_BIG, - .reg_read = realtek_smi_read, - .reg_write = realtek_smi_write, - .cache_type = REGCACHE_NONE, -}; - -static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) -{ - struct realtek_smi *smi = bus->priv; - - return smi->ops->phy_read(smi, addr, regnum); -} - -static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) -{ - struct realtek_smi *smi = bus->priv; - - return smi->ops->phy_write(smi, addr, regnum, val); -} - -int realtek_smi_setup_mdio(struct realtek_smi *smi) -{ - struct device_node *mdio_np; - int ret; - - mdio_np = of_get_compatible_child(smi->dev->of_node, "realtek,smi-mdio"); - if (!mdio_np) { - dev_err(smi->dev, "no MDIO bus node\n"); - return -ENODEV; - } - - smi->slave_mii_bus = devm_mdiobus_alloc(smi->dev); - if (!smi->slave_mii_bus) { - ret = -ENOMEM; - goto err_put_node; - } - smi->slave_mii_bus->priv = smi; - smi->slave_mii_bus->name = "SMI slave MII"; - smi->slave_mii_bus->read = realtek_smi_mdio_read; - smi->slave_mii_bus->write = realtek_smi_mdio_write; - snprintf(smi->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", - smi->ds->index); - smi->slave_mii_bus->dev.of_node = mdio_np; - smi->slave_mii_bus->parent = smi->dev; - smi->ds->slave_mii_bus = smi->slave_mii_bus; - - ret = devm_of_mdiobus_register(smi->dev, smi->slave_mii_bus, mdio_np); - if (ret) { - dev_err(smi->dev, "unable to register MDIO bus %s\n", - smi->slave_mii_bus->id); - goto err_put_node; - } - - return 0; - -err_put_node: - of_node_put(mdio_np); - - return ret; -} - -static int realtek_smi_probe(struct platform_device *pdev) -{ - const struct realtek_smi_variant *var; - struct device *dev = &pdev->dev; - struct realtek_smi *smi; - struct device_node *np; - int ret; - - var = of_device_get_match_data(dev); - np = dev->of_node; - - smi = devm_kzalloc(dev, sizeof(*smi) + var->chip_data_sz, GFP_KERNEL); - if (!smi) - return -ENOMEM; - smi->chip_data = (void *)smi + sizeof(*smi); - smi->map = devm_regmap_init(dev, NULL, smi, - &realtek_smi_mdio_regmap_config); - if (IS_ERR(smi->map)) { - ret = PTR_ERR(smi->map); - dev_err(dev, "regmap init failed: %d\n", ret); - return ret; - } - - /* Link forward and backward */ - smi->dev = dev; - smi->clk_delay = var->clk_delay; - smi->cmd_read = var->cmd_read; - smi->cmd_write = var->cmd_write; - smi->ops = var->ops; - - dev_set_drvdata(dev, smi); - spin_lock_init(&smi->lock); - - /* TODO: if power is software controlled, set up any regulators here */ - - /* Assert then deassert RESET */ - smi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(smi->reset)) { - dev_err(dev, "failed to get RESET GPIO\n"); - return PTR_ERR(smi->reset); - } - msleep(REALTEK_SMI_HW_STOP_DELAY); - gpiod_set_value(smi->reset, 0); - msleep(REALTEK_SMI_HW_START_DELAY); - dev_info(dev, "deasserted RESET\n"); - - /* Fetch MDIO pins */ - smi->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); - if (IS_ERR(smi->mdc)) - return PTR_ERR(smi->mdc); - smi->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); - if (IS_ERR(smi->mdio)) - return PTR_ERR(smi->mdio); - - smi->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); - - ret = smi->ops->detect(smi); - if (ret) { - dev_err(dev, "unable to detect switch\n"); - return ret; - } - - smi->ds = devm_kzalloc(dev, sizeof(*smi->ds), GFP_KERNEL); - if (!smi->ds) - return -ENOMEM; - - smi->ds->dev = dev; - smi->ds->num_ports = smi->num_ports; - smi->ds->priv = smi; - - smi->ds->ops = var->ds_ops; - ret = dsa_register_switch(smi->ds); - if (ret) { - dev_err_probe(dev, ret, "unable to register switch\n"); - return ret; - } - return 0; -} - -static int realtek_smi_remove(struct platform_device *pdev) -{ - struct realtek_smi *smi = platform_get_drvdata(pdev); - - if (!smi) - return 0; - - dsa_unregister_switch(smi->ds); - if (smi->slave_mii_bus) - of_node_put(smi->slave_mii_bus->dev.of_node); - gpiod_set_value(smi->reset, 1); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static void realtek_smi_shutdown(struct platform_device *pdev) -{ - struct realtek_smi *smi = platform_get_drvdata(pdev); - - if (!smi) - return; - - dsa_switch_shutdown(smi->ds); - - platform_set_drvdata(pdev, NULL); -} - -static const struct of_device_id realtek_smi_of_match[] = { - { - .compatible = "realtek,rtl8366rb", - .data = &rtl8366rb_variant, - }, - { - /* FIXME: add support for RTL8366S and more */ - .compatible = "realtek,rtl8366s", - .data = NULL, - }, - { - .compatible = "realtek,rtl8365mb", - .data = &rtl8365mb_variant, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, realtek_smi_of_match); - -static struct platform_driver realtek_smi_driver = { - .driver = { - .name = "realtek-smi", - .of_match_table = of_match_ptr(realtek_smi_of_match), - }, - .probe = realtek_smi_probe, - .remove = realtek_smi_remove, - .shutdown = realtek_smi_shutdown, -}; -module_platform_driver(realtek_smi_driver); - -MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig new file mode 100644 index 000000000000..b7427a8292b2 --- /dev/null +++ b/drivers/net/dsa/realtek/Kconfig @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig NET_DSA_REALTEK + tristate "Realtek Ethernet switch family support" + depends on NET_DSA + select FIXED_PHY + select IRQ_DOMAIN + select REALTEK_PHY + select REGMAP + help + Select to enable support for Realtek Ethernet switch chips. + +config NET_DSA_REALTEK_MDIO + tristate "Realtek MDIO connected switch driver" + depends on NET_DSA_REALTEK + help + Select to enable support for registering switches configured + through MDIO. + +config NET_DSA_REALTEK_SMI + tristate "Realtek SMI connected switch driver" + depends on NET_DSA_REALTEK + help + Select to enable support for registering switches connected + through SMI. + +config NET_DSA_REALTEK_RTL8365MB + tristate "Realtek RTL8365MB switch subdriver" + depends on NET_DSA_REALTEK + depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL8_4 + help + Select to enable support for Realtek RTL8365MB-VC and RTL8367S. + +config NET_DSA_REALTEK_RTL8366RB + tristate "Realtek RTL8366RB switch subdriver" + depends on NET_DSA_REALTEK + depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL4_A + help + Select to enable support for Realtek RTL8366RB diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile new file mode 100644 index 000000000000..0aab57252a7c --- /dev/null +++ b/drivers/net/dsa/realtek/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o +obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o +obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o +rtl8366-objs := rtl8366-core.o rtl8366rb.o +obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c new file mode 100644 index 000000000000..0308be95d00a --- /dev/null +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Realtek MDIO interface driver + * + * ASICs we intend to support with this driver: + * + * RTL8366 - The original version, apparently + * RTL8369 - Similar enough to have the same datsheet as RTL8366 + * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite + * different register layout from the other two + * RTL8366S - Is this "RTL8366 super"? + * RTL8367 - Has an OpenWRT driver as well + * RTL8368S - Seems to be an alternative name for RTL8366RB + * RTL8370 - Also uses SMI + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> + * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> + * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> + * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> + */ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#include "realtek.h" + +/* Read/write via mdiobus */ +#define REALTEK_MDIO_CTRL0_REG 31 +#define REALTEK_MDIO_START_REG 29 +#define REALTEK_MDIO_CTRL1_REG 21 +#define REALTEK_MDIO_ADDRESS_REG 23 +#define REALTEK_MDIO_DATA_WRITE_REG 24 +#define REALTEK_MDIO_DATA_READ_REG 25 + +#define REALTEK_MDIO_START_OP 0xFFFF +#define REALTEK_MDIO_ADDR_OP 0x000E +#define REALTEK_MDIO_READ_OP 0x0001 +#define REALTEK_MDIO_WRITE_OP 0x0003 + +static int realtek_mdio_write(void *ctx, u32 reg, u32 val) +{ + struct realtek_priv *priv = ctx; + struct mii_bus *bus = priv->bus; + int ret; + + mutex_lock(&bus->mdio_lock); + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, val); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_WRITE_OP); + +out_unlock: + mutex_unlock(&bus->mdio_lock); + + return ret; +} + +static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) +{ + struct realtek_priv *priv = ctx; + struct mii_bus *bus = priv->bus; + int ret; + + mutex_lock(&bus->mdio_lock); + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); + if (ret) + goto out_unlock; + + ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_READ_OP); + if (ret) + goto out_unlock; + + ret = bus->read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG); + if (ret >= 0) { + *val = ret; + ret = 0; + } + +out_unlock: + mutex_unlock(&bus->mdio_lock); + + return ret; +} + +static const struct regmap_config realtek_mdio_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = realtek_mdio_read, + .reg_write = realtek_mdio_write, + .cache_type = REGCACHE_NONE, +}; + +static int realtek_mdio_probe(struct mdio_device *mdiodev) +{ + struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; + const struct realtek_variant *var; + int ret; + struct device_node *np; + + var = of_device_get_match_data(dev); + if (!var) + return -EINVAL; + + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->map = devm_regmap_init(dev, NULL, priv, &realtek_mdio_regmap_config); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + priv->mdio_addr = mdiodev->addr; + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + priv->chip_data = (void *)priv + sizeof(*priv); + + priv->clk_delay = var->clk_delay; + priv->cmd_read = var->cmd_read; + priv->cmd_write = var->cmd_write; + priv->ops = var->ops; + + priv->write_reg_noack = realtek_mdio_write; + + np = dev->of_node; + + dev_set_drvdata(dev, priv); + + /* TODO: if power is software controlled, set up any regulators here */ + priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); + return PTR_ERR(priv->reset); + } + + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + ret = priv->ops->detect(priv); + if (ret) { + dev_err(dev, "unable to detect switch\n"); + return ret; + } + + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->num_ports = priv->num_ports; + priv->ds->priv = priv; + priv->ds->ops = var->ds_ops_mdio; + + ret = dsa_register_switch(priv->ds); + if (ret) { + dev_err(priv->dev, "unable to register switch ret = %d\n", ret); + return ret; + } + + return 0; +} + +static void realtek_mdio_remove(struct mdio_device *mdiodev) +{ + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + + if (!priv) + return; + + dsa_unregister_switch(priv->ds); + + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); + + dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void realtek_mdio_shutdown(struct mdio_device *mdiodev) +{ + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + + dev_set_drvdata(&mdiodev->dev, NULL); +} + +static const struct of_device_id realtek_mdio_of_match[] = { +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) + { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, +#endif +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) + { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, + { .compatible = "realtek,rtl8367s", .data = &rtl8365mb_variant, }, +#endif + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); + +static struct mdio_driver realtek_mdio_driver = { + .mdiodrv.driver = { + .name = "realtek-mdio", + .of_match_table = of_match_ptr(realtek_mdio_of_match), + }, + .probe = realtek_mdio_probe, + .remove = realtek_mdio_remove, + .shutdown = realtek_mdio_shutdown, +}; + +mdio_module_driver(realtek_mdio_driver); + +MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>"); +MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c new file mode 100644 index 000000000000..8806b74bd7a8 --- /dev/null +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Realtek Simple Management Interface (SMI) driver + * It can be discussed how "simple" this interface is. + * + * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels + * but the protocol is not MDIO at all. Instead it is a Realtek + * pecularity that need to bit-bang the lines in a special way to + * communicate with the switch. + * + * ASICs we intend to support with this driver: + * + * RTL8366 - The original version, apparently + * RTL8369 - Similar enough to have the same datsheet as RTL8366 + * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite + * different register layout from the other two + * RTL8366S - Is this "RTL8366 super"? + * RTL8367 - Has an OpenWRT driver as well + * RTL8368S - Seems to be an alternative name for RTL8366RB + * RTL8370 - Also uses SMI + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> + * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> + * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> + * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/skbuff.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_mdio.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/if_bridge.h> + +#include "realtek.h" + +#define REALTEK_SMI_ACK_RETRY_COUNT 5 + +static inline void realtek_smi_clk_delay(struct realtek_priv *priv) +{ + ndelay(priv->clk_delay); +} + +static void realtek_smi_start(struct realtek_priv *priv) +{ + /* Set GPIO pins to output mode, with initial state: + * SCK = 0, SDA = 1 + */ + gpiod_direction_output(priv->mdc, 0); + gpiod_direction_output(priv->mdio, 1); + realtek_smi_clk_delay(priv); + + /* CLK 1: 0 -> 1, 1 -> 0 */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + + /* CLK 2: */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 1); +} + +static void realtek_smi_stop(struct realtek_priv *priv) +{ + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 0); + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + + /* Add a click */ + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + + /* Set GPIO pins to input mode */ + gpiod_direction_input(priv->mdio); + gpiod_direction_input(priv->mdc); +} + +static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len) +{ + for (; len > 0; len--) { + realtek_smi_clk_delay(priv); + + /* Prepare data */ + gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1)))); + realtek_smi_clk_delay(priv); + + /* Clocking */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + } +} + +static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data) +{ + gpiod_direction_input(priv->mdio); + + for (*data = 0; len > 0; len--) { + u32 u; + + realtek_smi_clk_delay(priv); + + /* Clocking */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + u = !!gpiod_get_value(priv->mdio); + gpiod_set_value(priv->mdc, 0); + + *data |= (u << (len - 1)); + } + + gpiod_direction_output(priv->mdio, 0); +} + +static int realtek_smi_wait_for_ack(struct realtek_priv *priv) +{ + int retry_cnt; + + retry_cnt = 0; + do { + u32 ack; + + realtek_smi_read_bits(priv, 1, &ack); + if (ack == 0) + break; + + if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { + dev_err(priv->dev, "ACK timeout\n"); + return -ETIMEDOUT; + } + } while (1); + + return 0; +} + +static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) +{ + realtek_smi_write_bits(priv, data, 8); + return realtek_smi_wait_for_ack(priv); +} + +static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data) +{ + realtek_smi_write_bits(priv, data, 8); + return 0; +} + +static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) +{ + u32 t; + + /* Read data */ + realtek_smi_read_bits(priv, 8, &t); + *data = (t & 0xff); + + /* Send an ACK */ + realtek_smi_write_bits(priv, 0x00, 1); + + return 0; +} + +static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data) +{ + u32 t; + + /* Read data */ + realtek_smi_read_bits(priv, 8, &t); + *data = (t & 0xff); + + /* Send an ACK */ + realtek_smi_write_bits(priv, 0x01, 1); + + return 0; +} + +static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) +{ + unsigned long flags; + u8 lo = 0; + u8 hi = 0; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + realtek_smi_start(priv); + + /* Send READ command */ + ret = realtek_smi_write_byte(priv, priv->cmd_read); + if (ret) + goto out; + + /* Set ADDR[7:0] */ + ret = realtek_smi_write_byte(priv, addr & 0xff); + if (ret) + goto out; + + /* Set ADDR[15:8] */ + ret = realtek_smi_write_byte(priv, addr >> 8); + if (ret) + goto out; + + /* Read DATA[7:0] */ + realtek_smi_read_byte0(priv, &lo); + /* Read DATA[15:8] */ + realtek_smi_read_byte1(priv, &hi); + + *data = ((u32)lo) | (((u32)hi) << 8); + + ret = 0; + + out: + realtek_smi_stop(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int realtek_smi_write_reg(struct realtek_priv *priv, + u32 addr, u32 data, bool ack) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + realtek_smi_start(priv); + + /* Send WRITE command */ + ret = realtek_smi_write_byte(priv, priv->cmd_write); + if (ret) + goto out; + + /* Set ADDR[7:0] */ + ret = realtek_smi_write_byte(priv, addr & 0xff); + if (ret) + goto out; + + /* Set ADDR[15:8] */ + ret = realtek_smi_write_byte(priv, addr >> 8); + if (ret) + goto out; + + /* Write DATA[7:0] */ + ret = realtek_smi_write_byte(priv, data & 0xff); + if (ret) + goto out; + + /* Write DATA[15:8] */ + if (ack) + ret = realtek_smi_write_byte(priv, data >> 8); + else + ret = realtek_smi_write_byte_noack(priv, data >> 8); + if (ret) + goto out; + + ret = 0; + + out: + realtek_smi_stop(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +/* There is one single case when we need to use this accessor and that + * is when issueing soft reset. Since the device reset as soon as we write + * that bit, no ACK will come back for natural reasons. + */ +static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val) +{ + return realtek_smi_write_reg(ctx, reg, val, false); +} + +/* Regmap accessors */ + +static int realtek_smi_write(void *ctx, u32 reg, u32 val) +{ + struct realtek_priv *priv = ctx; + + return realtek_smi_write_reg(priv, reg, val, true); +} + +static int realtek_smi_read(void *ctx, u32 reg, u32 *val) +{ + struct realtek_priv *priv = ctx; + + return realtek_smi_read_reg(priv, reg, val); +} + +static const struct regmap_config realtek_smi_mdio_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = realtek_smi_read, + .reg_write = realtek_smi_write, + .cache_type = REGCACHE_NONE, +}; + +static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_read(priv, addr, regnum); +} + +static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_write(priv, addr, regnum, val); +} + +static int realtek_smi_setup_mdio(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + struct device_node *mdio_np; + int ret; + + mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); + if (!mdio_np) { + dev_err(priv->dev, "no MDIO bus node\n"); + return -ENODEV; + } + + priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev); + if (!priv->slave_mii_bus) { + ret = -ENOMEM; + goto err_put_node; + } + priv->slave_mii_bus->priv = priv; + priv->slave_mii_bus->name = "SMI slave MII"; + priv->slave_mii_bus->read = realtek_smi_mdio_read; + priv->slave_mii_bus->write = realtek_smi_mdio_write; + snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", + ds->index); + priv->slave_mii_bus->dev.of_node = mdio_np; + priv->slave_mii_bus->parent = priv->dev; + ds->slave_mii_bus = priv->slave_mii_bus; + + ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); + if (ret) { + dev_err(priv->dev, "unable to register MDIO bus %s\n", + priv->slave_mii_bus->id); + goto err_put_node; + } + + return 0; + +err_put_node: + of_node_put(mdio_np); + + return ret; +} + +static int realtek_smi_probe(struct platform_device *pdev) +{ + const struct realtek_variant *var; + struct device *dev = &pdev->dev; + struct realtek_priv *priv; + struct device_node *np; + int ret; + + var = of_device_get_match_data(dev); + np = dev->of_node; + + priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->chip_data = (void *)priv + sizeof(*priv); + priv->map = devm_regmap_init(dev, NULL, priv, + &realtek_smi_mdio_regmap_config); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + /* Link forward and backward */ + priv->dev = dev; + priv->clk_delay = var->clk_delay; + priv->cmd_read = var->cmd_read; + priv->cmd_write = var->cmd_write; + priv->ops = var->ops; + + priv->setup_interface = realtek_smi_setup_mdio; + priv->write_reg_noack = realtek_smi_write_reg_noack; + + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->lock); + + /* TODO: if power is software controlled, set up any regulators here */ + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); + return PTR_ERR(priv->reset); + } + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + /* Fetch MDIO pins */ + priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); + if (IS_ERR(priv->mdc)) + return PTR_ERR(priv->mdc); + priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); + if (IS_ERR(priv->mdio)) + return PTR_ERR(priv->mdio); + + priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); + + ret = priv->ops->detect(priv); + if (ret) { + dev_err(dev, "unable to detect switch\n"); + return ret; + } + + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->num_ports = priv->num_ports; + priv->ds->priv = priv; + + priv->ds->ops = var->ds_ops_smi; + ret = dsa_register_switch(priv->ds); + if (ret) { + dev_err_probe(dev, ret, "unable to register switch\n"); + return ret; + } + return 0; +} + +static int realtek_smi_remove(struct platform_device *pdev) +{ + struct realtek_priv *priv = platform_get_drvdata(pdev); + + if (!priv) + return 0; + + dsa_unregister_switch(priv->ds); + if (priv->slave_mii_bus) + of_node_put(priv->slave_mii_bus->dev.of_node); + + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void realtek_smi_shutdown(struct platform_device *pdev) +{ + struct realtek_priv *priv = platform_get_drvdata(pdev); + + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + + platform_set_drvdata(pdev, NULL); +} + +static const struct of_device_id realtek_smi_of_match[] = { +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) + { + .compatible = "realtek,rtl8366rb", + .data = &rtl8366rb_variant, + }, +#endif + { + /* FIXME: add support for RTL8366S and more */ + .compatible = "realtek,rtl8366s", + .data = NULL, + }, +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) + { + .compatible = "realtek,rtl8365mb", + .data = &rtl8365mb_variant, + }, + { + .compatible = "realtek,rtl8367s", + .data = &rtl8365mb_variant, + }, +#endif + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, realtek_smi_of_match); + +static struct platform_driver realtek_smi_driver = { + .driver = { + .name = "realtek-smi", + .of_match_table = of_match_ptr(realtek_smi_of_match), + }, + .probe = realtek_smi_probe, + .remove = realtek_smi_remove, + .shutdown = realtek_smi_shutdown, +}; +module_platform_driver(realtek_smi_driver); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/realtek-smi-core.h b/drivers/net/dsa/realtek/realtek.h index 5bfa53e2480a..e7d3e1bcf8b8 100644 --- a/drivers/net/dsa/realtek-smi-core.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -5,15 +5,18 @@ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> */ -#ifndef _REALTEK_SMI_H -#define _REALTEK_SMI_H +#ifndef _REALTEK_H +#define _REALTEK_H #include <linux/phy.h> #include <linux/platform_device.h> #include <linux/gpio/consumer.h> #include <net/dsa.h> -struct realtek_smi_ops; +#define REALTEK_HW_STOP_DELAY 25 /* msecs */ +#define REALTEK_HW_START_DELAY 100 /* msecs */ + +struct realtek_ops; struct dentry; struct inode; struct file; @@ -25,7 +28,7 @@ struct rtl8366_mib_counter { const char *name; }; -/** +/* * struct rtl8366_vlan_mc - Virtual LAN member configuration */ struct rtl8366_vlan_mc { @@ -43,13 +46,15 @@ struct rtl8366_vlan_4k { u8 fid; }; -struct realtek_smi { +struct realtek_priv { struct device *dev; struct gpio_desc *reset; struct gpio_desc *mdc; struct gpio_desc *mdio; struct regmap *map; struct mii_bus *slave_mii_bus; + struct mii_bus *bus; + int mdio_addr; unsigned int clk_delay; u8 cmd_read; @@ -65,7 +70,9 @@ struct realtek_smi { unsigned int num_mib_counters; struct rtl8366_mib_counter *mib_counters; - const struct realtek_smi_ops *ops; + const struct realtek_ops *ops; + int (*setup_interface)(struct dsa_switch *ds); + int (*write_reg_noack)(void *ctx, u32 addr, u32 data); int vlan_enabled; int vlan4k_enabled; @@ -74,61 +81,57 @@ struct realtek_smi { void *chip_data; /* Per-chip extra variant data */ }; -/** - * struct realtek_smi_ops - vtable for the per-SMI-chiptype operations +/* + * struct realtek_ops - vtable for the per-SMI-chiptype operations * @detect: detects the chiptype */ -struct realtek_smi_ops { - int (*detect)(struct realtek_smi *smi); - int (*reset_chip)(struct realtek_smi *smi); - int (*setup)(struct realtek_smi *smi); - void (*cleanup)(struct realtek_smi *smi); - int (*get_mib_counter)(struct realtek_smi *smi, +struct realtek_ops { + int (*detect)(struct realtek_priv *priv); + int (*reset_chip)(struct realtek_priv *priv); + int (*setup)(struct realtek_priv *priv); + void (*cleanup)(struct realtek_priv *priv); + int (*get_mib_counter)(struct realtek_priv *priv, int port, struct rtl8366_mib_counter *mib, u64 *mibvalue); - int (*get_vlan_mc)(struct realtek_smi *smi, u32 index, + int (*get_vlan_mc)(struct realtek_priv *priv, u32 index, struct rtl8366_vlan_mc *vlanmc); - int (*set_vlan_mc)(struct realtek_smi *smi, u32 index, + int (*set_vlan_mc)(struct realtek_priv *priv, u32 index, const struct rtl8366_vlan_mc *vlanmc); - int (*get_vlan_4k)(struct realtek_smi *smi, u32 vid, + int (*get_vlan_4k)(struct realtek_priv *priv, u32 vid, struct rtl8366_vlan_4k *vlan4k); - int (*set_vlan_4k)(struct realtek_smi *smi, + int (*set_vlan_4k)(struct realtek_priv *priv, const struct rtl8366_vlan_4k *vlan4k); - int (*get_mc_index)(struct realtek_smi *smi, int port, int *val); - int (*set_mc_index)(struct realtek_smi *smi, int port, int index); - bool (*is_vlan_valid)(struct realtek_smi *smi, unsigned int vlan); - int (*enable_vlan)(struct realtek_smi *smi, bool enable); - int (*enable_vlan4k)(struct realtek_smi *smi, bool enable); - int (*enable_port)(struct realtek_smi *smi, int port, bool enable); - int (*phy_read)(struct realtek_smi *smi, int phy, int regnum); - int (*phy_write)(struct realtek_smi *smi, int phy, int regnum, + int (*get_mc_index)(struct realtek_priv *priv, int port, int *val); + int (*set_mc_index)(struct realtek_priv *priv, int port, int index); + bool (*is_vlan_valid)(struct realtek_priv *priv, unsigned int vlan); + int (*enable_vlan)(struct realtek_priv *priv, bool enable); + int (*enable_vlan4k)(struct realtek_priv *priv, bool enable); + int (*enable_port)(struct realtek_priv *priv, int port, bool enable); + int (*phy_read)(struct realtek_priv *priv, int phy, int regnum); + int (*phy_write)(struct realtek_priv *priv, int phy, int regnum, u16 val); }; -struct realtek_smi_variant { - const struct dsa_switch_ops *ds_ops; - const struct realtek_smi_ops *ops; +struct realtek_variant { + const struct dsa_switch_ops *ds_ops_smi; + const struct dsa_switch_ops *ds_ops_mdio; + const struct realtek_ops *ops; unsigned int clk_delay; u8 cmd_read; u8 cmd_write; size_t chip_data_sz; }; -/* SMI core calls */ -int realtek_smi_write_reg_noack(struct realtek_smi *smi, u32 addr, - u32 data); -int realtek_smi_setup_mdio(struct realtek_smi *smi); - /* RTL8366 library helpers */ -int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used); -int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, +int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used); +int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member, u32 untag, u32 fid); -int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port, +int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, unsigned int vid); -int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable); -int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable); -int rtl8366_reset_vlan(struct realtek_smi *smi); +int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable); +int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable); +int rtl8366_reset_vlan(struct realtek_priv *priv); int rtl8366_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, struct netlink_ext_ack *extack); @@ -139,7 +142,7 @@ void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset); void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); -extern const struct realtek_smi_variant rtl8366rb_variant; -extern const struct realtek_smi_variant rtl8365mb_variant; +extern const struct realtek_variant rtl8366rb_variant; +extern const struct realtek_variant rtl8365mb_variant; -#endif /* _REALTEK_SMI_H */ +#endif /* _REALTEK_H */ diff --git a/drivers/net/dsa/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 3b729544798b..2ed592147c20 100644 --- a/drivers/net/dsa/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -99,18 +99,28 @@ #include <linux/regmap.h> #include <linux/if_bridge.h> -#include "realtek-smi-core.h" +#include "realtek.h" /* Chip-specific data and limits */ -#define RTL8365MB_CHIP_ID_8365MB_VC 0x6367 -#define RTL8365MB_CPU_PORT_NUM_8365MB_VC 6 -#define RTL8365MB_LEARN_LIMIT_MAX_8365MB_VC 2112 +#define RTL8365MB_CHIP_ID_8365MB_VC 0x6367 +#define RTL8365MB_CHIP_VER_8365MB_VC 0x0040 + +#define RTL8365MB_CHIP_ID_8367S 0x6367 +#define RTL8365MB_CHIP_VER_8367S 0x00A0 + +#define RTL8365MB_CHIP_ID_8367RB 0x6367 +#define RTL8365MB_CHIP_VER_8367RB 0x0020 /* Family-specific data and limits */ -#define RTL8365MB_PHYADDRMAX 7 -#define RTL8365MB_NUM_PHYREGS 32 -#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) -#define RTL8365MB_MAX_NUM_PORTS (RTL8365MB_CPU_PORT_NUM_8365MB_VC + 1) +#define RTL8365MB_PHYADDRMAX 7 +#define RTL8365MB_NUM_PHYREGS 32 +#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) +/* RTL8370MB and RTL8310SR, possibly suportable by this driver, have 10 ports */ +#define RTL8365MB_MAX_NUM_PORTS 10 +#define RTL8365MB_LEARN_LIMIT_MAX 2112 + +/* valid for all 6-port or less variants */ +static const int rtl8365mb_extint_port_map[] = { -1, -1, -1, -1, -1, -1, 1, 2, -1, -1}; /* Chip identification registers */ #define RTL8365MB_CHIP_ID_REG 0x1300 @@ -191,7 +201,7 @@ /* The PHY OCP addresses of PHY registers 0~31 start here */ #define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 -/* EXT port interface mode values - used in DIGITAL_INTERFACE_SELECT */ +/* EXT interface port mode values - used in DIGITAL_INTERFACE_SELECT */ #define RTL8365MB_EXT_PORT_MODE_DISABLE 0 #define RTL8365MB_EXT_PORT_MODE_RGMII 1 #define RTL8365MB_EXT_PORT_MODE_MII_MAC 2 @@ -207,39 +217,56 @@ #define RTL8365MB_EXT_PORT_MODE_1000X 12 #define RTL8365MB_EXT_PORT_MODE_100FX 13 -/* EXT port interface mode configuration registers 0~1 */ -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extport) \ - (RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 + \ - ((_extport) >> 1) * (0x13C3 - 0x1305)) -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extport) \ - (0xF << (((_extport) % 2))) -#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extport) \ - (((_extport) % 2) * 4) - -/* EXT port RGMII TX/RX delay configuration registers 1~2 */ -#define RTL8365MB_EXT_RGMXF_REG1 0x1307 -#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 -#define RTL8365MB_EXT_RGMXF_REG(_extport) \ - (RTL8365MB_EXT_RGMXF_REG1 + \ - (((_extport) >> 1) * (0x13C5 - 0x1307))) +/* Realtek docs and driver uses logic number as EXT_PORT0=16, EXT_PORT1=17, + * EXT_PORT2=18, to interact with switch ports. That logic number is internally + * converted to either a physical port number (0..9) or an external interface id (0..2), + * depending on which function was called. The external interface id is calculated as + * (ext_id=logic_port-15), while the logical to physical map depends on the chip id/version. + * + * EXT_PORT0 mentioned in datasheets and rtl8367c driver is used in this driver + * as extid==1, EXT_PORT2, mentioned in Realtek rtl8367c driver for 10-port switches, + * would have an ext_id of 3 (out of range for most extint macros) and ext_id 0 does + * not seem to be used as well for this family. + */ + +/* EXT interface mode configuration registers 0~1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ + ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ + 0x0) +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ + (0xF << (((_extint) % 2))) +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ + (((_extint) % 2) * 4) + +/* EXT interface RGMII TX/RX delay configuration registers 0~2 */ +#define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */ +#define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */ +#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */ +#define RTL8365MB_EXT_RGMXF_REG(_extint) \ + ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \ + (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ + (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ + 0x0) #define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 #define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 -/* External port speed values - used in DIGITAL_INTERFACE_FORCE */ +/* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ #define RTL8365MB_PORT_SPEED_10M 0 #define RTL8365MB_PORT_SPEED_100M 1 #define RTL8365MB_PORT_SPEED_1000M 2 -/* EXT port force configuration registers 0~2 */ -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extport) \ - (RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 + \ - ((_extport) & 0x1) + \ - ((((_extport) >> 1) & 0x1) * (0x13C4 - 0x1310))) +/* EXT interface force configuration registers 0~2 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \ + ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \ + (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ + 0x0) #define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 #define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 @@ -516,7 +543,7 @@ struct rtl8365mb_cpu { /** * struct rtl8365mb_port - private per-port data - * @smi: pointer to parent realtek_smi data + * @priv: pointer to parent realtek_priv data * @index: DSA port index, same as dsa_port::index * @stats: link statistics populated by rtl8365mb_stats_poll, ready for atomic * access via rtl8365mb_get_stats64 @@ -524,7 +551,7 @@ struct rtl8365mb_cpu { * @mib_work: delayed work for polling MIB counters */ struct rtl8365mb_port { - struct realtek_smi *smi; + struct realtek_priv *priv; unsigned int index; struct rtnl_link_stats64 stats; spinlock_t stats_lock; @@ -533,13 +560,12 @@ struct rtl8365mb_port { /** * struct rtl8365mb - private chip-specific driver data - * @smi: pointer to parent realtek_smi data + * @priv: pointer to parent realtek_priv data * @irq: registered IRQ or zero * @chip_id: chip identifier * @chip_ver: chip silicon revision * @port_mask: mask of all ports * @learn_limit_max: maximum number of L2 addresses the chip can learn - * @cpu: CPU tagging and CPU port configuration for this chip * @mib_lock: prevent concurrent reads of MIB counters * @ports: per-port data * @jam_table: chip-specific initialization jam table @@ -548,29 +574,28 @@ struct rtl8365mb_port { * Private data for this driver. */ struct rtl8365mb { - struct realtek_smi *smi; + struct realtek_priv *priv; int irq; u32 chip_id; u32 chip_ver; u32 port_mask; u32 learn_limit_max; - struct rtl8365mb_cpu cpu; struct mutex mib_lock; struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS]; const struct rtl8365mb_jam_tbl_entry *jam_table; size_t jam_size; }; -static int rtl8365mb_phy_poll_busy(struct realtek_smi *smi) +static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) { u32 val; - return regmap_read_poll_timeout(smi->map, + return regmap_read_poll_timeout(priv->map, RTL8365MB_INDIRECT_ACCESS_STATUS_REG, val, !val, 10, 100); } -static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, +static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, u32 ocp_addr) { u32 val; @@ -579,7 +604,7 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, /* Set OCP prefix */ val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); ret = regmap_update_bits( - smi->map, RTL8365MB_GPHY_OCP_MSB_0_REG, + priv->map, RTL8365MB_GPHY_OCP_MSB_0_REG, RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); if (ret) @@ -592,7 +617,7 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, ocp_addr >> 1); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, ocp_addr >> 6); - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, + ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) return ret; @@ -600,17 +625,17 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_smi *smi, int phy, return 0; } -static int rtl8365mb_phy_ocp_read(struct realtek_smi *smi, int phy, +static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, u32 ocp_addr, u16 *data) { u32 val; int ret; - ret = rtl8365mb_phy_poll_busy(smi); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) return ret; - ret = rtl8365mb_phy_ocp_prepare(smi, phy, ocp_addr); + ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) return ret; @@ -619,16 +644,16 @@ static int rtl8365mb_phy_ocp_read(struct realtek_smi *smi, int phy, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); + ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); if (ret) return ret; - ret = rtl8365mb_phy_poll_busy(smi); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) return ret; /* Get PHY register data */ - ret = regmap_read(smi->map, RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, + ret = regmap_read(priv->map, RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); if (ret) return ret; @@ -638,22 +663,22 @@ static int rtl8365mb_phy_ocp_read(struct realtek_smi *smi, int phy, return 0; } -static int rtl8365mb_phy_ocp_write(struct realtek_smi *smi, int phy, +static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, u32 ocp_addr, u16 data) { u32 val; int ret; - ret = rtl8365mb_phy_poll_busy(smi); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) return ret; - ret = rtl8365mb_phy_ocp_prepare(smi, phy, ocp_addr); + ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) return ret; /* Set PHY register data */ - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, + ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); if (ret) return ret; @@ -663,18 +688,18 @@ static int rtl8365mb_phy_ocp_write(struct realtek_smi *smi, int phy, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); - ret = regmap_write(smi->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); + ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); if (ret) return ret; - ret = rtl8365mb_phy_poll_busy(smi); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) return ret; return 0; } -static int rtl8365mb_phy_read(struct realtek_smi *smi, int phy, int regnum) +static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) { u32 ocp_addr; u16 val; @@ -688,21 +713,21 @@ static int rtl8365mb_phy_read(struct realtek_smi *smi, int phy, int regnum) ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; - ret = rtl8365mb_phy_ocp_read(smi, phy, ocp_addr, &val); + ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy, regnum, ocp_addr, ret); return ret; } - dev_dbg(smi->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", + dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", phy, regnum, ocp_addr, val); return val; } -static int rtl8365mb_phy_write(struct realtek_smi *smi, int phy, int regnum, +static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, u16 val) { u32 ocp_addr; @@ -716,20 +741,31 @@ static int rtl8365mb_phy_write(struct realtek_smi *smi, int phy, int regnum, ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; - ret = rtl8365mb_phy_ocp_write(smi, phy, ocp_addr, val); + ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy, regnum, ocp_addr, ret); return ret; } - dev_dbg(smi->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", + dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", phy, regnum, ocp_addr, val); return 0; } +static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +{ + return rtl8365mb_phy_read(ds->priv, phy, regnum); +} + +static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, + u16 val) +{ + return rtl8365mb_phy_write(ds->priv, phy, regnum, val); +} + static enum dsa_tag_protocol rtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) @@ -737,25 +773,25 @@ rtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port, return DSA_TAG_PROTO_RTL8_4; } -static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, +static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, phy_interface_t interface) { struct device_node *dn; struct dsa_port *dp; int tx_delay = 0; int rx_delay = 0; - int ext_port; + int ext_int; u32 val; int ret; - if (port == smi->cpu_port) { - ext_port = 1; - } else { - dev_err(smi->dev, "only one EXT port is currently supported\n"); + ext_int = rtl8365mb_extint_port_map[port]; + + if (ext_int <= 0) { + dev_err(priv->dev, "Port %d is not an external interface port\n", port); return -EINVAL; } - dp = dsa_to_port(smi->ds, port); + dp = dsa_to_port(priv->ds, port); dn = dp->dn; /* Set the RGMII TX/RX delay @@ -786,8 +822,8 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, if (val == 0 || val == 2) tx_delay = val / 2; else - dev_warn(smi->dev, - "EXT port TX delay must be 0 or 2 ns\n"); + dev_warn(priv->dev, + "EXT interface TX delay must be 0 or 2 ns\n"); } if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) { @@ -796,12 +832,12 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, if (val <= 7) rx_delay = val; else - dev_warn(smi->dev, - "EXT port RX delay must be 0 to 2.1 ns\n"); + dev_warn(priv->dev, + "EXT interface RX delay must be 0 to 2.1 ns\n"); } ret = regmap_update_bits( - smi->map, RTL8365MB_EXT_RGMXF_REG(ext_port), + priv->map, RTL8365MB_EXT_RGMXF_REG(ext_int), RTL8365MB_EXT_RGMXF_TXDELAY_MASK | RTL8365MB_EXT_RGMXF_RXDELAY_MASK, FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) | @@ -810,18 +846,18 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port, return ret; ret = regmap_update_bits( - smi->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(ext_port), - RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(ext_port), + priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(ext_int), + RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(ext_int), RTL8365MB_EXT_PORT_MODE_RGMII << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET( - ext_port)); + ext_int)); if (ret) return ret; return 0; } -static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, +static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port, bool link, int speed, int duplex, bool tx_pause, bool rx_pause) { @@ -830,14 +866,14 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, u32 r_duplex; u32 r_speed; u32 r_link; - int ext_port; + int ext_int; int val; int ret; - if (port == smi->cpu_port) { - ext_port = 1; - } else { - dev_err(smi->dev, "only one EXT port is currently supported\n"); + ext_int = rtl8365mb_extint_port_map[port]; + + if (ext_int <= 0) { + dev_err(priv->dev, "Port %d is not an external interface port\n", port); return -EINVAL; } @@ -854,7 +890,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, } else if (speed == SPEED_10) { r_speed = RTL8365MB_PORT_SPEED_10M; } else { - dev_err(smi->dev, "unsupported port speed %s\n", + dev_err(priv->dev, "unsupported port speed %s\n", phy_speed_to_str(speed)); return -EINVAL; } @@ -864,7 +900,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, } else if (duplex == DUPLEX_HALF) { r_duplex = 0; } else { - dev_err(smi->dev, "unsupported duplex %s\n", + dev_err(priv->dev, "unsupported duplex %s\n", phy_duplex_to_str(duplex)); return -EINVAL; } @@ -886,8 +922,8 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK, r_duplex) | FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed); - ret = regmap_write(smi->map, - RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(ext_port), + ret = regmap_write(priv->map, + RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(ext_int), val); if (ret) return ret; @@ -898,13 +934,17 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_smi *smi, int port, static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port, phy_interface_t interface) { - if (dsa_is_user_port(ds, port) && + int ext_int; + + ext_int = rtl8365mb_extint_port_map[port]; + + if (ext_int < 0 && (interface == PHY_INTERFACE_MODE_NA || interface == PHY_INTERFACE_MODE_INTERNAL || interface == PHY_INTERFACE_MODE_GMII)) /* Internal PHY */ return true; - else if (dsa_is_cpu_port(ds, port) && + else if ((ext_int >= 1) && phy_interface_mode_is_rgmii(interface)) /* Extension MAC */ return true; @@ -912,65 +952,43 @@ static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port, return false; } -static void rtl8365mb_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - struct realtek_smi *smi = ds->priv; - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0 }; - - /* include/linux/phylink.h says: - * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink - * expects the MAC driver to return all supported link modes. - */ - if (state->interface != PHY_INTERFACE_MODE_NA && - !rtl8365mb_phy_mode_supported(ds, port, state->interface)) { - dev_err(smi->dev, "phy mode %s is unsupported on port %d\n", - phy_modes(state->interface), port); - linkmode_zero(supported); - return; - } - - phylink_set_port_modes(mask); - - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 1000baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + if (dsa_is_user_port(ds, port)) + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + else if (dsa_is_cpu_port(ds, port)) + phy_interface_set_rgmii(config->supported_interfaces); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; } static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; if (!rtl8365mb_phy_mode_supported(ds, port, state->interface)) { - dev_err(smi->dev, "phy mode %s is unsupported on port %d\n", + dev_err(priv->dev, "phy mode %s is unsupported on port %d\n", phy_modes(state->interface), port); return; } if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) { - dev_err(smi->dev, + dev_err(priv->dev, "port %d supports only conventional PHY or fixed-link\n", port); return; } if (phy_interface_mode_is_rgmii(state->interface)) { - ret = rtl8365mb_ext_config_rgmii(smi, port, state->interface); + ret = rtl8365mb_ext_config_rgmii(priv, port, state->interface); if (ret) - dev_err(smi->dev, + dev_err(priv->dev, "failed to configure RGMII mode on port %d: %d\n", port, ret); return; @@ -985,20 +1003,20 @@ static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; int ret; - mb = smi->chip_data; + mb = priv->chip_data; p = &mb->ports[port]; cancel_delayed_work_sync(&p->mib_work); if (phy_interface_mode_is_rgmii(interface)) { - ret = rtl8365mb_ext_config_forcemode(smi, port, false, 0, 0, + ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0, false, false); if (ret) - dev_err(smi->dev, + dev_err(priv->dev, "failed to reset forced mode on port %d: %d\n", port, ret); @@ -1013,21 +1031,21 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, int duplex, bool tx_pause, bool rx_pause) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; int ret; - mb = smi->chip_data; + mb = priv->chip_data; p = &mb->ports[port]; schedule_delayed_work(&p->mib_work, 0); if (phy_interface_mode_is_rgmii(interface)) { - ret = rtl8365mb_ext_config_forcemode(smi, port, true, speed, + ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed, duplex, tx_pause, rx_pause); if (ret) - dev_err(smi->dev, + dev_err(priv->dev, "failed to force mode on port %d: %d\n", port, ret); @@ -1038,7 +1056,7 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; enum rtl8365mb_stp_state val; int msti = 0; @@ -1057,36 +1075,36 @@ static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, val = RTL8365MB_STP_STATE_FORWARDING; break; default: - dev_err(smi->dev, "invalid STP state: %u\n", state); + dev_err(priv->dev, "invalid STP state: %u\n", state); return; } - regmap_update_bits(smi->map, RTL8365MB_MSTI_CTRL_REG(msti, port), + regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port), RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port), val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); } -static int rtl8365mb_port_set_learning(struct realtek_smi *smi, int port, +static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, bool enable) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; /* Enable/disable learning by limiting the number of L2 addresses the * port can learn. Realtek documentation states that a limit of zero * disables learning. When enabling learning, set it to the chip's * maximum. */ - return regmap_write(smi->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), + return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), enable ? mb->learn_limit_max : 0); } -static int rtl8365mb_port_set_isolation(struct realtek_smi *smi, int port, +static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, u32 mask) { - return regmap_write(smi->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); + return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); } -static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, +static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, u32 offset, u32 length, u64 *mibvalue) { u64 tmpvalue = 0; @@ -1098,13 +1116,13 @@ static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, * and then poll the control register before reading the value from some * counter registers. */ - ret = regmap_write(smi->map, RTL8365MB_MIB_ADDRESS_REG, + ret = regmap_write(priv->map, RTL8365MB_MIB_ADDRESS_REG, RTL8365MB_MIB_ADDRESS(port, offset)); if (ret) return ret; /* Poll for completion */ - ret = regmap_read_poll_timeout(smi->map, RTL8365MB_MIB_CTRL0_REG, val, + ret = regmap_read_poll_timeout(priv->map, RTL8365MB_MIB_CTRL0_REG, val, !(val & RTL8365MB_MIB_CTRL0_BUSY_MASK), 10, 100); if (ret) @@ -1126,7 +1144,7 @@ static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, /* Read the MIB counter 16 bits at a time */ for (i = 0; i < length; i++) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8365MB_MIB_COUNTER_REG(offset - i), &val); if (ret) return ret; @@ -1142,21 +1160,21 @@ static int rtl8365mb_mib_counter_read(struct realtek_smi *smi, int port, static void rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb *mb; int ret; int i; - mb = smi->chip_data; + mb = priv->chip_data; mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; - ret = rtl8365mb_mib_counter_read(smi, port, mib->offset, + ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &data[i]); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to read port %d counters: %d\n", port, ret); break; @@ -1190,15 +1208,15 @@ static int rtl8365mb_get_sset_count(struct dsa_switch *ds, int port, int sset) static void rtl8365mb_get_phy_stats(struct dsa_switch *ds, int port, struct ethtool_eth_phy_stats *phy_stats) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_mib_counter *mib; struct rtl8365mb *mb; - mb = smi->chip_data; + mb = priv->chip_data; mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3StatsSymbolErrors]; mutex_lock(&mb->mib_lock); - rtl8365mb_mib_counter_read(smi, port, mib->offset, mib->length, + rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &phy_stats->SymbolErrorDuringCarrier); mutex_unlock(&mb->mib_lock); } @@ -1226,12 +1244,12 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, [RTL8365MB_MIB_dot3StatsExcessiveCollisions] = 1, }; - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb *mb; int ret; int i; - mb = smi->chip_data; + mb = priv->chip_data; mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { @@ -1241,7 +1259,7 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, if (!cnt[i]) continue; - ret = rtl8365mb_mib_counter_read(smi, port, mib->offset, + ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &cnt[i]); if (ret) break; @@ -1291,20 +1309,20 @@ static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, static void rtl8365mb_get_ctrl_stats(struct dsa_switch *ds, int port, struct ethtool_eth_ctrl_stats *ctrl_stats) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_mib_counter *mib; struct rtl8365mb *mb; - mb = smi->chip_data; + mb = priv->chip_data; mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3ControlInUnknownOpcodes]; mutex_lock(&mb->mib_lock); - rtl8365mb_mib_counter_read(smi, port, mib->offset, mib->length, + rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, &ctrl_stats->UnsupportedOpcodesReceived); mutex_unlock(&mb->mib_lock); } -static void rtl8365mb_stats_update(struct realtek_smi *smi, int port) +static void rtl8365mb_stats_update(struct realtek_priv *priv, int port) { u64 cnt[RTL8365MB_MIB_END] = { [RTL8365MB_MIB_ifOutOctets] = 1, @@ -1323,7 +1341,7 @@ static void rtl8365mb_stats_update(struct realtek_smi *smi, int port) [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, }; - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; struct rtnl_link_stats64 *stats; int ret; int i; @@ -1338,7 +1356,7 @@ static void rtl8365mb_stats_update(struct realtek_smi *smi, int port) if (!cnt[i]) continue; - ret = rtl8365mb_mib_counter_read(smi, port, c->offset, + ret = rtl8365mb_mib_counter_read(priv, port, c->offset, c->length, &cnt[i]); if (ret) break; @@ -1388,9 +1406,9 @@ static void rtl8365mb_stats_poll(struct work_struct *work) struct rtl8365mb_port *p = container_of(to_delayed_work(work), struct rtl8365mb_port, mib_work); - struct realtek_smi *smi = p->smi; + struct realtek_priv *priv = p->priv; - rtl8365mb_stats_update(smi, p->index); + rtl8365mb_stats_update(priv, p->index); schedule_delayed_work(&p->mib_work, RTL8365MB_STATS_INTERVAL_JIFFIES); } @@ -1398,11 +1416,11 @@ static void rtl8365mb_stats_poll(struct work_struct *work) static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, struct rtnl_link_stats64 *s) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8365mb_port *p; struct rtl8365mb *mb; - mb = smi->chip_data; + mb = priv->chip_data; p = &mb->ports[port]; spin_lock(&p->stats_lock); @@ -1410,9 +1428,9 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, spin_unlock(&p->stats_lock); } -static void rtl8365mb_stats_setup(struct realtek_smi *smi) +static void rtl8365mb_stats_setup(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int i; /* Per-chip global mutex to protect MIB counter access, since doing @@ -1420,10 +1438,10 @@ static void rtl8365mb_stats_setup(struct realtek_smi *smi) */ mutex_init(&mb->mib_lock); - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(smi->ds, i)) + if (dsa_is_unused_port(priv->ds, i)) continue; /* Per-port spinlock to protect the stats64 data */ @@ -1436,45 +1454,45 @@ static void rtl8365mb_stats_setup(struct realtek_smi *smi) } } -static void rtl8365mb_stats_teardown(struct realtek_smi *smi) +static void rtl8365mb_stats_teardown(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int i; - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(smi->ds, i)) + if (dsa_is_unused_port(priv->ds, i)) continue; cancel_delayed_work_sync(&p->mib_work); } } -static int rtl8365mb_get_and_clear_status_reg(struct realtek_smi *smi, u32 reg, +static int rtl8365mb_get_and_clear_status_reg(struct realtek_priv *priv, u32 reg, u32 *val) { int ret; - ret = regmap_read(smi->map, reg, val); + ret = regmap_read(priv->map, reg, val); if (ret) return ret; - return regmap_write(smi->map, reg, *val); + return regmap_write(priv->map, reg, *val); } static irqreturn_t rtl8365mb_irq(int irq, void *data) { - struct realtek_smi *smi = data; + struct realtek_priv *priv = data; unsigned long line_changes = 0; struct rtl8365mb *mb; u32 stat; int line; int ret; - mb = smi->chip_data; + mb = priv->chip_data; - ret = rtl8365mb_get_and_clear_status_reg(smi, RTL8365MB_INTR_STATUS_REG, + ret = rtl8365mb_get_and_clear_status_reg(priv, RTL8365MB_INTR_STATUS_REG, &stat); if (ret) goto out_error; @@ -1485,14 +1503,14 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) u32 val; ret = rtl8365mb_get_and_clear_status_reg( - smi, RTL8365MB_PORT_LINKUP_IND_REG, &val); + priv, RTL8365MB_PORT_LINKUP_IND_REG, &val); if (ret) goto out_error; linkup_ind = FIELD_GET(RTL8365MB_PORT_LINKUP_IND_MASK, val); ret = rtl8365mb_get_and_clear_status_reg( - smi, RTL8365MB_PORT_LINKDOWN_IND_REG, &val); + priv, RTL8365MB_PORT_LINKDOWN_IND_REG, &val); if (ret) goto out_error; @@ -1504,8 +1522,8 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) if (!line_changes) goto out_none; - for_each_set_bit(line, &line_changes, smi->num_ports) { - int child_irq = irq_find_mapping(smi->irqdomain, line); + for_each_set_bit(line, &line_changes, priv->num_ports) { + int child_irq = irq_find_mapping(priv->irqdomain, line); handle_nested_irq(child_irq); } @@ -1513,7 +1531,7 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) return IRQ_HANDLED; out_error: - dev_err(smi->dev, "failed to read interrupt status: %d\n", ret); + dev_err(priv->dev, "failed to read interrupt status: %d\n", ret); out_none: return IRQ_NONE; @@ -1548,27 +1566,27 @@ static const struct irq_domain_ops rtl8365mb_irqdomain_ops = { .xlate = irq_domain_xlate_onecell, }; -static int rtl8365mb_set_irq_enable(struct realtek_smi *smi, bool enable) +static int rtl8365mb_set_irq_enable(struct realtek_priv *priv, bool enable) { - return regmap_update_bits(smi->map, RTL8365MB_INTR_CTRL_REG, + return regmap_update_bits(priv->map, RTL8365MB_INTR_CTRL_REG, RTL8365MB_INTR_LINK_CHANGE_MASK, FIELD_PREP(RTL8365MB_INTR_LINK_CHANGE_MASK, enable ? 1 : 0)); } -static int rtl8365mb_irq_enable(struct realtek_smi *smi) +static int rtl8365mb_irq_enable(struct realtek_priv *priv) { - return rtl8365mb_set_irq_enable(smi, true); + return rtl8365mb_set_irq_enable(priv, true); } -static int rtl8365mb_irq_disable(struct realtek_smi *smi) +static int rtl8365mb_irq_disable(struct realtek_priv *priv) { - return rtl8365mb_set_irq_enable(smi, false); + return rtl8365mb_set_irq_enable(priv, false); } -static int rtl8365mb_irq_setup(struct realtek_smi *smi) +static int rtl8365mb_irq_setup(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; struct device_node *intc; u32 irq_trig; int virq; @@ -1577,9 +1595,9 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) int ret; int i; - intc = of_get_child_by_name(smi->dev->of_node, "interrupt-controller"); + intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); if (!intc) { - dev_err(smi->dev, "missing child interrupt-controller node\n"); + dev_err(priv->dev, "missing child interrupt-controller node\n"); return -EINVAL; } @@ -1587,24 +1605,24 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) irq = of_irq_get(intc, 0); if (irq <= 0) { if (irq != -EPROBE_DEFER) - dev_err(smi->dev, "failed to get parent irq: %d\n", + dev_err(priv->dev, "failed to get parent irq: %d\n", irq); ret = irq ? irq : -EINVAL; goto out_put_node; } - smi->irqdomain = irq_domain_add_linear(intc, smi->num_ports, - &rtl8365mb_irqdomain_ops, smi); - if (!smi->irqdomain) { - dev_err(smi->dev, "failed to add irq domain\n"); + priv->irqdomain = irq_domain_add_linear(intc, priv->num_ports, + &rtl8365mb_irqdomain_ops, priv); + if (!priv->irqdomain) { + dev_err(priv->dev, "failed to add irq domain\n"); ret = -ENOMEM; goto out_put_node; } - for (i = 0; i < smi->num_ports; i++) { - virq = irq_create_mapping(smi->irqdomain, i); + for (i = 0; i < priv->num_ports; i++) { + virq = irq_create_mapping(priv->irqdomain, i); if (!virq) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to create irq domain mapping\n"); ret = -EINVAL; goto out_remove_irqdomain; @@ -1625,40 +1643,40 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) val = RTL8365MB_INTR_POLARITY_LOW; break; default: - dev_err(smi->dev, "unsupported irq trigger type %u\n", + dev_err(priv->dev, "unsupported irq trigger type %u\n", irq_trig); ret = -EINVAL; goto out_remove_irqdomain; } - ret = regmap_update_bits(smi->map, RTL8365MB_INTR_POLARITY_REG, + ret = regmap_update_bits(priv->map, RTL8365MB_INTR_POLARITY_REG, RTL8365MB_INTR_POLARITY_MASK, FIELD_PREP(RTL8365MB_INTR_POLARITY_MASK, val)); if (ret) goto out_remove_irqdomain; /* Disable the interrupt in case the chip has it enabled on reset */ - ret = rtl8365mb_irq_disable(smi); + ret = rtl8365mb_irq_disable(priv); if (ret) goto out_remove_irqdomain; /* Clear the interrupt status register */ - ret = regmap_write(smi->map, RTL8365MB_INTR_STATUS_REG, + ret = regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG, RTL8365MB_INTR_ALL_MASK); if (ret) goto out_remove_irqdomain; ret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT, - "rtl8365mb", smi); + "rtl8365mb", priv); if (ret) { - dev_err(smi->dev, "failed to request irq: %d\n", ret); + dev_err(priv->dev, "failed to request irq: %d\n", ret); goto out_remove_irqdomain; } /* Store the irq so that we know to free it during teardown */ mb->irq = irq; - ret = rtl8365mb_irq_enable(smi); + ret = rtl8365mb_irq_enable(priv); if (ret) goto out_free_irq; @@ -1667,17 +1685,17 @@ static int rtl8365mb_irq_setup(struct realtek_smi *smi) return 0; out_free_irq: - free_irq(mb->irq, smi); + free_irq(mb->irq, priv); mb->irq = 0; out_remove_irqdomain: - for (i = 0; i < smi->num_ports; i++) { - virq = irq_find_mapping(smi->irqdomain, i); + for (i = 0; i < priv->num_ports; i++) { + virq = irq_find_mapping(priv->irqdomain, i); irq_dispose_mapping(virq); } - irq_domain_remove(smi->irqdomain); - smi->irqdomain = NULL; + irq_domain_remove(priv->irqdomain); + priv->irqdomain = NULL; out_put_node: of_node_put(intc); @@ -1685,36 +1703,34 @@ out_put_node: return ret; } -static void rtl8365mb_irq_teardown(struct realtek_smi *smi) +static void rtl8365mb_irq_teardown(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int virq; int i; if (mb->irq) { - free_irq(mb->irq, smi); + free_irq(mb->irq, priv); mb->irq = 0; } - if (smi->irqdomain) { - for (i = 0; i < smi->num_ports; i++) { - virq = irq_find_mapping(smi->irqdomain, i); + if (priv->irqdomain) { + for (i = 0; i < priv->num_ports; i++) { + virq = irq_find_mapping(priv->irqdomain, i); irq_dispose_mapping(virq); } - irq_domain_remove(smi->irqdomain); - smi->irqdomain = NULL; + irq_domain_remove(priv->irqdomain); + priv->irqdomain = NULL; } } -static int rtl8365mb_cpu_config(struct realtek_smi *smi) +static int rtl8365mb_cpu_config(struct realtek_priv *priv, const struct rtl8365mb_cpu *cpu) { - struct rtl8365mb *mb = smi->chip_data; - struct rtl8365mb_cpu *cpu = &mb->cpu; u32 val; int ret; - ret = regmap_update_bits(smi->map, RTL8365MB_CPU_PORT_MASK_REG, + ret = regmap_update_bits(priv->map, RTL8365MB_CPU_PORT_MASK_REG, RTL8365MB_CPU_PORT_MASK_MASK, FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK, cpu->mask)); @@ -1726,26 +1742,26 @@ static int rtl8365mb_cpu_config(struct realtek_smi *smi) FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu->position) | FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu->rx_length) | FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu->format) | - FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port) | + FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port & 0x7) | FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK, - cpu->trap_port >> 3); - ret = regmap_write(smi->map, RTL8365MB_CPU_CTRL_REG, val); + cpu->trap_port >> 3 & 0x1); + ret = regmap_write(priv->map, RTL8365MB_CPU_CTRL_REG, val); if (ret) return ret; return 0; } -static int rtl8365mb_switch_init(struct realtek_smi *smi) +static int rtl8365mb_switch_init(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; int ret; int i; /* Do any chip-specific init jam before getting to the common stuff */ if (mb->jam_table) { for (i = 0; i < mb->jam_size; i++) { - ret = regmap_write(smi->map, mb->jam_table[i].reg, + ret = regmap_write(priv->map, mb->jam_table[i].reg, mb->jam_table[i].val); if (ret) return ret; @@ -1754,7 +1770,7 @@ static int rtl8365mb_switch_init(struct realtek_smi *smi) /* Common init jam */ for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) { - ret = regmap_write(smi->map, rtl8365mb_init_jam_common[i].reg, + ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg, rtl8365mb_init_jam_common[i].val); if (ret) return ret; @@ -1763,75 +1779,86 @@ static int rtl8365mb_switch_init(struct realtek_smi *smi) return 0; } -static int rtl8365mb_reset_chip(struct realtek_smi *smi) +static int rtl8365mb_reset_chip(struct realtek_priv *priv) { u32 val; - realtek_smi_write_reg_noack(smi, RTL8365MB_CHIP_RESET_REG, - FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, - 1)); + priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, + FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); /* Realtek documentation says the chip needs 1 second to reset. Sleep * for 100 ms before accessing any registers to prevent ACK timeouts. */ msleep(100); - return regmap_read_poll_timeout(smi->map, RTL8365MB_CHIP_RESET_REG, val, + return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val, !(val & RTL8365MB_CHIP_RESET_HW_MASK), 20000, 1e6); } static int rtl8365mb_setup(struct dsa_switch *ds) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_cpu cpu = {0}; + struct dsa_port *cpu_dp; struct rtl8365mb *mb; int ret; int i; - mb = smi->chip_data; + mb = priv->chip_data; - ret = rtl8365mb_reset_chip(smi); + ret = rtl8365mb_reset_chip(priv); if (ret) { - dev_err(smi->dev, "failed to reset chip: %d\n", ret); + dev_err(priv->dev, "failed to reset chip: %d\n", ret); goto out_error; } /* Configure switch to vendor-defined initial state */ - ret = rtl8365mb_switch_init(smi); + ret = rtl8365mb_switch_init(priv); if (ret) { - dev_err(smi->dev, "failed to initialize switch: %d\n", ret); + dev_err(priv->dev, "failed to initialize switch: %d\n", ret); goto out_error; } /* Set up cascading IRQs */ - ret = rtl8365mb_irq_setup(smi); + ret = rtl8365mb_irq_setup(priv); if (ret == -EPROBE_DEFER) return ret; else if (ret) - dev_info(smi->dev, "no interrupt support\n"); + dev_info(priv->dev, "no interrupt support\n"); /* Configure CPU tagging */ - ret = rtl8365mb_cpu_config(smi); + cpu.trap_port = RTL8365MB_MAX_NUM_PORTS; + dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { + cpu.mask |= BIT(cpu_dp->index); + + if (cpu.trap_port == RTL8365MB_MAX_NUM_PORTS) + cpu.trap_port = cpu_dp->index; + } + + cpu.enable = cpu.mask > 0; + cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL; + cpu.position = RTL8365MB_CPU_POS_AFTER_SA; + cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES; + cpu.format = RTL8365MB_CPU_FORMAT_8BYTES; + + ret = rtl8365mb_cpu_config(priv, &cpu); if (ret) goto out_teardown_irq; /* Configure ports */ - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i]; - if (dsa_is_unused_port(smi->ds, i)) + if (dsa_is_unused_port(priv->ds, i)) continue; - /* Set up per-port private data */ - p->smi = smi; - p->index = i; - /* Forward only to the CPU */ - ret = rtl8365mb_port_set_isolation(smi, i, BIT(smi->cpu_port)); + ret = rtl8365mb_port_set_isolation(priv, i, cpu.mask); if (ret) goto out_teardown_irq; /* Disable learning */ - ret = rtl8365mb_port_set_learning(smi, i, false); + ret = rtl8365mb_port_set_learning(priv, i, false); if (ret) goto out_teardown_irq; @@ -1839,29 +1866,35 @@ static int rtl8365mb_setup(struct dsa_switch *ds) * ports will still forward frames to the CPU despite being * administratively down by default. */ - rtl8365mb_port_stp_state_set(smi->ds, i, BR_STATE_DISABLED); + rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); + + /* Set up per-port private data */ + p->priv = priv; + p->index = i; } /* Set maximum packet length to 1536 bytes */ - ret = regmap_update_bits(smi->map, RTL8365MB_CFG0_MAX_LEN_REG, + ret = regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, RTL8365MB_CFG0_MAX_LEN_MASK, FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536)); if (ret) goto out_teardown_irq; - ret = realtek_smi_setup_mdio(smi); - if (ret) { - dev_err(smi->dev, "could not set up MDIO bus\n"); - goto out_teardown_irq; + if (priv->setup_interface) { + ret = priv->setup_interface(ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + goto out_teardown_irq; + } } /* Start statistics counter polling */ - rtl8365mb_stats_setup(smi); + rtl8365mb_stats_setup(priv); return 0; out_teardown_irq: - rtl8365mb_irq_teardown(smi); + rtl8365mb_irq_teardown(priv); out_error: return ret; @@ -1869,10 +1902,10 @@ out_error: static void rtl8365mb_teardown(struct dsa_switch *ds) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; - rtl8365mb_stats_teardown(smi); - rtl8365mb_irq_teardown(smi); + rtl8365mb_stats_teardown(priv); + rtl8365mb_irq_teardown(priv); } static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) @@ -1902,48 +1935,57 @@ static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) return 0; } -static int rtl8365mb_detect(struct realtek_smi *smi) +static int rtl8365mb_detect(struct realtek_priv *priv) { - struct rtl8365mb *mb = smi->chip_data; + struct rtl8365mb *mb = priv->chip_data; u32 chip_id; u32 chip_ver; int ret; - ret = rtl8365mb_get_chip_id_and_ver(smi->map, &chip_id, &chip_ver); + ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); if (ret) { - dev_err(smi->dev, "failed to read chip id and version: %d\n", + dev_err(priv->dev, "failed to read chip id and version: %d\n", ret); return ret; } switch (chip_id) { case RTL8365MB_CHIP_ID_8365MB_VC: - dev_info(smi->dev, - "found an RTL8365MB-VC switch (ver=0x%04x)\n", - chip_ver); + switch (chip_ver) { + case RTL8365MB_CHIP_VER_8365MB_VC: + dev_info(priv->dev, + "found an RTL8365MB-VC switch (ver=0x%04x)\n", + chip_ver); + break; + case RTL8365MB_CHIP_VER_8367RB: + dev_info(priv->dev, + "found an RTL8367RB-VB switch (ver=0x%04x)\n", + chip_ver); + break; + case RTL8365MB_CHIP_VER_8367S: + dev_info(priv->dev, + "found an RTL8367S switch (ver=0x%04x)\n", + chip_ver); + break; + default: + dev_err(priv->dev, "unrecognized switch version (ver=0x%04x)", + chip_ver); + return -ENODEV; + } - smi->cpu_port = RTL8365MB_CPU_PORT_NUM_8365MB_VC; - smi->num_ports = smi->cpu_port + 1; + priv->num_ports = RTL8365MB_MAX_NUM_PORTS; - mb->smi = smi; + mb->priv = priv; mb->chip_id = chip_id; mb->chip_ver = chip_ver; - mb->port_mask = BIT(smi->num_ports) - 1; - mb->learn_limit_max = RTL8365MB_LEARN_LIMIT_MAX_8365MB_VC; + mb->port_mask = GENMASK(priv->num_ports - 1, 0); + mb->learn_limit_max = RTL8365MB_LEARN_LIMIT_MAX; mb->jam_table = rtl8365mb_init_jam_8365mb_vc; mb->jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc); - mb->cpu.enable = 1; - mb->cpu.mask = BIT(smi->cpu_port); - mb->cpu.trap_port = smi->cpu_port; - mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL; - mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA; - mb->cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES; - mb->cpu.format = RTL8365MB_CPU_FORMAT_8BYTES; - break; default: - dev_err(smi->dev, + dev_err(priv->dev, "found an unknown Realtek switch (id=0x%04x, ver=0x%04x)\n", chip_id, chip_ver); return -ENODEV; @@ -1952,14 +1994,34 @@ static int rtl8365mb_detect(struct realtek_smi *smi) return 0; } -static const struct dsa_switch_ops rtl8365mb_switch_ops = { +static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { + .get_tag_protocol = rtl8365mb_get_tag_protocol, + .setup = rtl8365mb_setup, + .teardown = rtl8365mb_teardown, + .phylink_get_caps = rtl8365mb_phylink_get_caps, + .phylink_mac_config = rtl8365mb_phylink_mac_config, + .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, + .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, + .port_stp_state_set = rtl8365mb_port_stp_state_set, + .get_strings = rtl8365mb_get_strings, + .get_ethtool_stats = rtl8365mb_get_ethtool_stats, + .get_sset_count = rtl8365mb_get_sset_count, + .get_eth_phy_stats = rtl8365mb_get_phy_stats, + .get_eth_mac_stats = rtl8365mb_get_mac_stats, + .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, + .get_stats64 = rtl8365mb_get_stats64, +}; + +static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { .get_tag_protocol = rtl8365mb_get_tag_protocol, .setup = rtl8365mb_setup, .teardown = rtl8365mb_teardown, - .phylink_validate = rtl8365mb_phylink_validate, + .phylink_get_caps = rtl8365mb_phylink_get_caps, .phylink_mac_config = rtl8365mb_phylink_mac_config, .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, + .phy_read = rtl8365mb_dsa_phy_read, + .phy_write = rtl8365mb_dsa_phy_write, .port_stp_state_set = rtl8365mb_port_stp_state_set, .get_strings = rtl8365mb_get_strings, .get_ethtool_stats = rtl8365mb_get_ethtool_stats, @@ -1970,18 +2032,23 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .get_stats64 = rtl8365mb_get_stats64, }; -static const struct realtek_smi_ops rtl8365mb_smi_ops = { +static const struct realtek_ops rtl8365mb_ops = { .detect = rtl8365mb_detect, .phy_read = rtl8365mb_phy_read, .phy_write = rtl8365mb_phy_write, }; -const struct realtek_smi_variant rtl8365mb_variant = { - .ds_ops = &rtl8365mb_switch_ops, - .ops = &rtl8365mb_smi_ops, +const struct realtek_variant rtl8365mb_variant = { + .ds_ops_smi = &rtl8365mb_switch_ops_smi, + .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, + .ops = &rtl8365mb_ops, .clk_delay = 10, .cmd_read = 0xb9, .cmd_write = 0xb8, .chip_data_sz = sizeof(struct rtl8365mb), }; EXPORT_SYMBOL_GPL(rtl8365mb_variant); + +MODULE_AUTHOR("Alvin Å ipraga <alsi@bang-olufsen.dk>"); +MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/rtl8366.c b/drivers/net/dsa/realtek/rtl8366-core.c index bdb8d8d34880..dc5f75be3017 100644 --- a/drivers/net/dsa/rtl8366.c +++ b/drivers/net/dsa/realtek/rtl8366-core.c @@ -11,18 +11,18 @@ #include <linux/if_bridge.h> #include <net/dsa.h> -#include "realtek-smi-core.h" +#include "realtek.h" -int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used) +int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used) { int ret; int i; *used = 0; - for (i = 0; i < smi->num_ports; i++) { + for (i = 0; i < priv->num_ports; i++) { int index = 0; - ret = smi->ops->get_mc_index(smi, i, &index); + ret = priv->ops->get_mc_index(priv, i, &index); if (ret) return ret; @@ -38,13 +38,13 @@ EXPORT_SYMBOL_GPL(rtl8366_mc_is_used); /** * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration - * @smi: the Realtek SMI device instance + * @priv: the Realtek SMI device instance * @vid: the VLAN ID to look up or allocate * @vlanmc: the pointer will be assigned to a pointer to a valid member config * if successful * @return: index of a new member config or negative error number */ -static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, +static int rtl8366_obtain_mc(struct realtek_priv *priv, int vid, struct rtl8366_vlan_mc *vlanmc) { struct rtl8366_vlan_4k vlan4k; @@ -52,10 +52,10 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, int i; /* Try to find an existing member config entry for this VID */ - for (i = 0; i < smi->num_vlan_mc; i++) { - ret = smi->ops->get_vlan_mc(smi, i, vlanmc); + for (i = 0; i < priv->num_vlan_mc; i++) { + ret = priv->ops->get_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", + dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n", i, vid); return ret; } @@ -65,19 +65,19 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, } /* We have no MC entry for this VID, try to find an empty one */ - for (i = 0; i < smi->num_vlan_mc; i++) { - ret = smi->ops->get_vlan_mc(smi, i, vlanmc); + for (i = 0; i < priv->num_vlan_mc; i++) { + ret = priv->ops->get_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", + dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n", i, vid); return ret; } if (vlanmc->vid == 0 && vlanmc->member == 0) { /* Update the entry from the 4K table */ - ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); + ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k); if (ret) { - dev_err(smi->dev, "error looking for 4K VLAN MC %d for VID %d\n", + dev_err(priv->dev, "error looking for 4K VLAN MC %d for VID %d\n", i, vid); return ret; } @@ -86,30 +86,30 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, vlanmc->member = vlan4k.member; vlanmc->untag = vlan4k.untag; vlanmc->fid = vlan4k.fid; - ret = smi->ops->set_vlan_mc(smi, i, vlanmc); + ret = priv->ops->set_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", + dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n", i, vid); return ret; } - dev_dbg(smi->dev, "created new MC at index %d for VID %d\n", + dev_dbg(priv->dev, "created new MC at index %d for VID %d\n", i, vid); return i; } } /* MC table is full, try to find an unused entry and replace it */ - for (i = 0; i < smi->num_vlan_mc; i++) { + for (i = 0; i < priv->num_vlan_mc; i++) { int used; - ret = rtl8366_mc_is_used(smi, i, &used); + ret = rtl8366_mc_is_used(priv, i, &used); if (ret) return ret; if (!used) { /* Update the entry from the 4K table */ - ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); + ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k); if (ret) return ret; @@ -117,23 +117,23 @@ static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, vlanmc->member = vlan4k.member; vlanmc->untag = vlan4k.untag; vlanmc->fid = vlan4k.fid; - ret = smi->ops->set_vlan_mc(smi, i, vlanmc); + ret = priv->ops->set_vlan_mc(priv, i, vlanmc); if (ret) { - dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", + dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n", i, vid); return ret; } - dev_dbg(smi->dev, "recycled MC at index %i for VID %d\n", + dev_dbg(priv->dev, "recycled MC at index %i for VID %d\n", i, vid); return i; } } - dev_err(smi->dev, "all VLAN member configurations are in use\n"); + dev_err(priv->dev, "all VLAN member configurations are in use\n"); return -ENOSPC; } -int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, +int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member, u32 untag, u32 fid) { struct rtl8366_vlan_mc vlanmc; @@ -141,31 +141,31 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, int mc; int ret; - if (!smi->ops->is_vlan_valid(smi, vid)) + if (!priv->ops->is_vlan_valid(priv, vid)) return -EINVAL; - dev_dbg(smi->dev, + dev_dbg(priv->dev, "setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", vid, member, untag); /* Update the 4K table */ - ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); + ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k); if (ret) return ret; vlan4k.member |= member; vlan4k.untag |= untag; vlan4k.fid = fid; - ret = smi->ops->set_vlan_4k(smi, &vlan4k); + ret = priv->ops->set_vlan_4k(priv, &vlan4k); if (ret) return ret; - dev_dbg(smi->dev, + dev_dbg(priv->dev, "resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", vid, vlan4k.member, vlan4k.untag); /* Find or allocate a member config for this VID */ - ret = rtl8366_obtain_mc(smi, vid, &vlanmc); + ret = rtl8366_obtain_mc(priv, vid, &vlanmc); if (ret < 0) return ret; mc = ret; @@ -176,12 +176,12 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, vlanmc.fid = fid; /* Commit updates to the MC entry */ - ret = smi->ops->set_vlan_mc(smi, mc, &vlanmc); + ret = priv->ops->set_vlan_mc(priv, mc, &vlanmc); if (ret) - dev_err(smi->dev, "failed to commit changes to VLAN MC index %d for VID %d\n", + dev_err(priv->dev, "failed to commit changes to VLAN MC index %d for VID %d\n", mc, vid); else - dev_dbg(smi->dev, + dev_dbg(priv->dev, "resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n", vid, vlanmc.member, vlanmc.untag); @@ -189,37 +189,37 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, } EXPORT_SYMBOL_GPL(rtl8366_set_vlan); -int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port, +int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, unsigned int vid) { struct rtl8366_vlan_mc vlanmc; int mc; int ret; - if (!smi->ops->is_vlan_valid(smi, vid)) + if (!priv->ops->is_vlan_valid(priv, vid)) return -EINVAL; /* Find or allocate a member config for this VID */ - ret = rtl8366_obtain_mc(smi, vid, &vlanmc); + ret = rtl8366_obtain_mc(priv, vid, &vlanmc); if (ret < 0) return ret; mc = ret; - ret = smi->ops->set_mc_index(smi, port, mc); + ret = priv->ops->set_mc_index(priv, port, mc); if (ret) { - dev_err(smi->dev, "set PVID: failed to set MC index %d for port %d\n", + dev_err(priv->dev, "set PVID: failed to set MC index %d for port %d\n", mc, port); return ret; } - dev_dbg(smi->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n", + dev_dbg(priv->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n", port, vid, mc); return 0; } EXPORT_SYMBOL_GPL(rtl8366_set_pvid); -int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable) +int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) { int ret; @@ -229,52 +229,52 @@ int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable) */ if (enable) { /* Make sure VLAN is ON */ - ret = smi->ops->enable_vlan(smi, true); + ret = priv->ops->enable_vlan(priv, true); if (ret) return ret; - smi->vlan_enabled = true; + priv->vlan_enabled = true; } - ret = smi->ops->enable_vlan4k(smi, enable); + ret = priv->ops->enable_vlan4k(priv, enable); if (ret) return ret; - smi->vlan4k_enabled = enable; + priv->vlan4k_enabled = enable; return 0; } EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k); -int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable) +int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) { int ret; - ret = smi->ops->enable_vlan(smi, enable); + ret = priv->ops->enable_vlan(priv, enable); if (ret) return ret; - smi->vlan_enabled = enable; + priv->vlan_enabled = enable; /* If we turn VLAN off, make sure that we turn off * 4k VLAN as well, if that happened to be on. */ if (!enable) { - smi->vlan4k_enabled = false; - ret = smi->ops->enable_vlan4k(smi, false); + priv->vlan4k_enabled = false; + ret = priv->ops->enable_vlan4k(priv, false); } return ret; } EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); -int rtl8366_reset_vlan(struct realtek_smi *smi) +int rtl8366_reset_vlan(struct realtek_priv *priv) { struct rtl8366_vlan_mc vlanmc; int ret; int i; - rtl8366_enable_vlan(smi, false); - rtl8366_enable_vlan4k(smi, false); + rtl8366_enable_vlan(priv, false); + rtl8366_enable_vlan4k(priv, false); /* Clear the 16 VLAN member configurations */ vlanmc.vid = 0; @@ -282,8 +282,8 @@ int rtl8366_reset_vlan(struct realtek_smi *smi) vlanmc.member = 0; vlanmc.untag = 0; vlanmc.fid = 0; - for (i = 0; i < smi->num_vlan_mc; i++) { - ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); + for (i = 0; i < priv->num_vlan_mc; i++) { + ret = priv->ops->set_vlan_mc(priv, i, &vlanmc); if (ret) return ret; } @@ -298,12 +298,12 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, { bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; u32 member = 0; u32 untag = 0; int ret; - if (!smi->ops->is_vlan_valid(smi, vlan->vid)) { + if (!priv->ops->is_vlan_valid(priv, vlan->vid)) { NL_SET_ERR_MSG_MOD(extack, "VLAN ID not valid"); return -EINVAL; } @@ -312,13 +312,13 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, * FIXME: what's with this 4k business? * Just rtl8366_enable_vlan() seems inconclusive. */ - ret = rtl8366_enable_vlan4k(smi, true); + ret = rtl8366_enable_vlan4k(priv, true); if (ret) { NL_SET_ERR_MSG_MOD(extack, "Failed to enable VLAN 4K"); return ret; } - dev_dbg(smi->dev, "add VLAN %d on port %d, %s, %s\n", + dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n", vlan->vid, port, untagged ? "untagged" : "tagged", pvid ? "PVID" : "no PVID"); @@ -327,18 +327,18 @@ int rtl8366_vlan_add(struct dsa_switch *ds, int port, if (untagged) untag |= BIT(port); - ret = rtl8366_set_vlan(smi, vlan->vid, member, untag, 0); + ret = rtl8366_set_vlan(priv, vlan->vid, member, untag, 0); if (ret) { - dev_err(smi->dev, "failed to set up VLAN %04x", vlan->vid); + dev_err(priv->dev, "failed to set up VLAN %04x", vlan->vid); return ret; } if (!pvid) return 0; - ret = rtl8366_set_pvid(smi, port, vlan->vid); + ret = rtl8366_set_pvid(priv, port, vlan->vid); if (ret) { - dev_err(smi->dev, "failed to set PVID on port %d to VLAN %04x", + dev_err(priv->dev, "failed to set PVID on port %d to VLAN %04x", port, vlan->vid); return ret; } @@ -350,15 +350,15 @@ EXPORT_SYMBOL_GPL(rtl8366_vlan_add); int rtl8366_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret, i; - dev_dbg(smi->dev, "del VLAN %d on port %d\n", vlan->vid, port); + dev_dbg(priv->dev, "del VLAN %d on port %d\n", vlan->vid, port); - for (i = 0; i < smi->num_vlan_mc; i++) { + for (i = 0; i < priv->num_vlan_mc; i++) { struct rtl8366_vlan_mc vlanmc; - ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); + ret = priv->ops->get_vlan_mc(priv, i, &vlanmc); if (ret) return ret; @@ -376,9 +376,9 @@ int rtl8366_vlan_del(struct dsa_switch *ds, int port, vlanmc.priority = 0; vlanmc.fid = 0; } - ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); + ret = priv->ops->set_vlan_mc(priv, i, &vlanmc); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to remove VLAN %04x\n", vlan->vid); return ret; @@ -394,15 +394,15 @@ EXPORT_SYMBOL_GPL(rtl8366_vlan_del); void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8366_mib_counter *mib; int i; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return; - for (i = 0; i < smi->num_mib_counters; i++) { - mib = &smi->mib_counters[i]; + for (i = 0; i < priv->num_mib_counters; i++) { + mib = &priv->mib_counters[i]; strncpy(data + i * ETH_GSTRING_LEN, mib->name, ETH_GSTRING_LEN); } @@ -411,35 +411,35 @@ EXPORT_SYMBOL_GPL(rtl8366_get_strings); int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; /* We only support SS_STATS */ if (sset != ETH_SS_STATS) return 0; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return -EINVAL; - return smi->num_mib_counters; + return priv->num_mib_counters; } EXPORT_SYMBOL_GPL(rtl8366_get_sset_count); void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int i; int ret; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return; - for (i = 0; i < smi->num_mib_counters; i++) { + for (i = 0; i < priv->num_mib_counters; i++) { struct rtl8366_mib_counter *mib; u64 mibvalue = 0; - mib = &smi->mib_counters[i]; - ret = smi->ops->get_mib_counter(smi, port, mib, &mibvalue); + mib = &priv->mib_counters[i]; + ret = priv->ops->get_mib_counter(priv, port, mib, &mibvalue); if (ret) { - dev_err(smi->dev, "error reading MIB counter %s\n", + dev_err(priv->dev, "error reading MIB counter %s\n", mib->name); } data[i] = mibvalue; diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index ecc19bd5115f..fb6565e68401 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -21,7 +21,7 @@ #include <linux/of_irq.h> #include <linux/regmap.h> -#include "realtek-smi-core.h" +#include "realtek.h" #define RTL8366RB_PORT_NUM_CPU 5 #define RTL8366RB_NUM_PORTS 6 @@ -396,7 +396,7 @@ static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { { 0, 70, 2, "IfOutBroadcastPkts" }, }; -static int rtl8366rb_get_mib_counter(struct realtek_smi *smi, +static int rtl8366rb_get_mib_counter(struct realtek_priv *priv, int port, struct rtl8366_mib_counter *mib, u64 *mibvalue) @@ -412,12 +412,12 @@ static int rtl8366rb_get_mib_counter(struct realtek_smi *smi, /* Writing access counter address first * then ASIC will prepare 64bits counter wait for being retrived */ - ret = regmap_write(smi->map, addr, 0); /* Write whatever */ + ret = regmap_write(priv->map, addr, 0); /* Write whatever */ if (ret) return ret; /* Read MIB control register */ - ret = regmap_read(smi->map, RTL8366RB_MIB_CTRL_REG, &val); + ret = regmap_read(priv->map, RTL8366RB_MIB_CTRL_REG, &val); if (ret) return -EIO; @@ -430,7 +430,7 @@ static int rtl8366rb_get_mib_counter(struct realtek_smi *smi, /* Read each individual MIB 16 bits at the time */ *mibvalue = 0; for (i = mib->length; i > 0; i--) { - ret = regmap_read(smi->map, addr + (i - 1), &val); + ret = regmap_read(priv->map, addr + (i - 1), &val); if (ret) return ret; *mibvalue = (*mibvalue << 16) | (val & 0xFFFF); @@ -455,38 +455,38 @@ static u32 rtl8366rb_get_irqmask(struct irq_data *d) static void rtl8366rb_mask_irq(struct irq_data *d) { - struct realtek_smi *smi = irq_data_get_irq_chip_data(d); + struct realtek_priv *priv = irq_data_get_irq_chip_data(d); int ret; - ret = regmap_update_bits(smi->map, RTL8366RB_INTERRUPT_MASK_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_MASK_REG, rtl8366rb_get_irqmask(d), 0); if (ret) - dev_err(smi->dev, "could not mask IRQ\n"); + dev_err(priv->dev, "could not mask IRQ\n"); } static void rtl8366rb_unmask_irq(struct irq_data *d) { - struct realtek_smi *smi = irq_data_get_irq_chip_data(d); + struct realtek_priv *priv = irq_data_get_irq_chip_data(d); int ret; - ret = regmap_update_bits(smi->map, RTL8366RB_INTERRUPT_MASK_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_MASK_REG, rtl8366rb_get_irqmask(d), rtl8366rb_get_irqmask(d)); if (ret) - dev_err(smi->dev, "could not unmask IRQ\n"); + dev_err(priv->dev, "could not unmask IRQ\n"); } static irqreturn_t rtl8366rb_irq(int irq, void *data) { - struct realtek_smi *smi = data; + struct realtek_priv *priv = data; u32 stat; int ret; /* This clears the IRQ status register */ - ret = regmap_read(smi->map, RTL8366RB_INTERRUPT_STATUS_REG, + ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, &stat); if (ret) { - dev_err(smi->dev, "can't read interrupt status\n"); + dev_err(priv->dev, "can't read interrupt status\n"); return IRQ_NONE; } stat &= RTL8366RB_INTERRUPT_VALID; @@ -502,7 +502,7 @@ static irqreturn_t rtl8366rb_irq(int irq, void *data) */ if (line < 12 && line > 5) line -= 5; - child_irq = irq_find_mapping(smi->irqdomain, line); + child_irq = irq_find_mapping(priv->irqdomain, line); handle_nested_irq(child_irq); } return IRQ_HANDLED; @@ -538,7 +538,7 @@ static const struct irq_domain_ops rtl8366rb_irqdomain_ops = { .xlate = irq_domain_xlate_onecell, }; -static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) +static int rtl8366rb_setup_cascaded_irq(struct realtek_priv *priv) { struct device_node *intc; unsigned long irq_trig; @@ -547,24 +547,24 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) u32 val; int i; - intc = of_get_child_by_name(smi->dev->of_node, "interrupt-controller"); + intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); if (!intc) { - dev_err(smi->dev, "missing child interrupt-controller node\n"); + dev_err(priv->dev, "missing child interrupt-controller node\n"); return -EINVAL; } /* RB8366RB IRQs cascade off this one */ irq = of_irq_get(intc, 0); if (irq <= 0) { - dev_err(smi->dev, "failed to get parent IRQ\n"); + dev_err(priv->dev, "failed to get parent IRQ\n"); ret = irq ? irq : -EINVAL; goto out_put_node; } /* This clears the IRQ status register */ - ret = regmap_read(smi->map, RTL8366RB_INTERRUPT_STATUS_REG, + ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, &val); if (ret) { - dev_err(smi->dev, "can't read interrupt status\n"); + dev_err(priv->dev, "can't read interrupt status\n"); goto out_put_node; } @@ -573,48 +573,48 @@ static int rtl8366rb_setup_cascaded_irq(struct realtek_smi *smi) switch (irq_trig) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH: - dev_info(smi->dev, "active high/rising IRQ\n"); + dev_info(priv->dev, "active high/rising IRQ\n"); val = 0; break; case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW: - dev_info(smi->dev, "active low/falling IRQ\n"); + dev_info(priv->dev, "active low/falling IRQ\n"); val = RTL8366RB_INTERRUPT_POLARITY; break; } - ret = regmap_update_bits(smi->map, RTL8366RB_INTERRUPT_CONTROL_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, RTL8366RB_INTERRUPT_POLARITY, val); if (ret) { - dev_err(smi->dev, "could not configure IRQ polarity\n"); + dev_err(priv->dev, "could not configure IRQ polarity\n"); goto out_put_node; } - ret = devm_request_threaded_irq(smi->dev, irq, NULL, + ret = devm_request_threaded_irq(priv->dev, irq, NULL, rtl8366rb_irq, IRQF_ONESHOT, - "RTL8366RB", smi); + "RTL8366RB", priv); if (ret) { - dev_err(smi->dev, "unable to request irq: %d\n", ret); + dev_err(priv->dev, "unable to request irq: %d\n", ret); goto out_put_node; } - smi->irqdomain = irq_domain_add_linear(intc, - RTL8366RB_NUM_INTERRUPT, - &rtl8366rb_irqdomain_ops, - smi); - if (!smi->irqdomain) { - dev_err(smi->dev, "failed to create IRQ domain\n"); + priv->irqdomain = irq_domain_add_linear(intc, + RTL8366RB_NUM_INTERRUPT, + &rtl8366rb_irqdomain_ops, + priv); + if (!priv->irqdomain) { + dev_err(priv->dev, "failed to create IRQ domain\n"); ret = -EINVAL; goto out_put_node; } - for (i = 0; i < smi->num_ports; i++) - irq_set_parent(irq_create_mapping(smi->irqdomain, i), irq); + for (i = 0; i < priv->num_ports; i++) + irq_set_parent(irq_create_mapping(priv->irqdomain, i), irq); out_put_node: of_node_put(intc); return ret; } -static int rtl8366rb_set_addr(struct realtek_smi *smi) +static int rtl8366rb_set_addr(struct realtek_priv *priv) { u8 addr[ETH_ALEN]; u16 val; @@ -622,18 +622,18 @@ static int rtl8366rb_set_addr(struct realtek_smi *smi) eth_random_addr(addr); - dev_info(smi->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + dev_info(priv->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); val = addr[0] << 8 | addr[1]; - ret = regmap_write(smi->map, RTL8366RB_SMAR0, val); + ret = regmap_write(priv->map, RTL8366RB_SMAR0, val); if (ret) return ret; val = addr[2] << 8 | addr[3]; - ret = regmap_write(smi->map, RTL8366RB_SMAR1, val); + ret = regmap_write(priv->map, RTL8366RB_SMAR1, val); if (ret) return ret; val = addr[4] << 8 | addr[5]; - ret = regmap_write(smi->map, RTL8366RB_SMAR2, val); + ret = regmap_write(priv->map, RTL8366RB_SMAR2, val); if (ret) return ret; @@ -765,7 +765,7 @@ static const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = { /* Function that jams the tables in the proper registers */ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, - int jam_size, struct realtek_smi *smi, + int jam_size, struct realtek_priv *priv, bool write_dbg) { u32 val; @@ -774,24 +774,24 @@ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, for (i = 0; i < jam_size; i++) { if ((jam_table[i].reg & 0xBE00) == 0xBE00) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8366RB_PHY_ACCESS_BUSY_REG, &val); if (ret) return ret; if (!(val & RTL8366RB_PHY_INT_BUSY)) { - ret = regmap_write(smi->map, - RTL8366RB_PHY_ACCESS_CTRL_REG, - RTL8366RB_PHY_CTRL_WRITE); + ret = regmap_write(priv->map, + RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); if (ret) return ret; } } if (write_dbg) - dev_dbg(smi->dev, "jam %04x into register %04x\n", + dev_dbg(priv->dev, "jam %04x into register %04x\n", jam_table[i].val, jam_table[i].reg); - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, jam_table[i].reg, jam_table[i].val); if (ret) @@ -802,7 +802,7 @@ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, static int rtl8366rb_setup(struct dsa_switch *ds) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; const struct rtl8366rb_jam_tbl_entry *jam_table; struct rtl8366rb *rb; u32 chip_ver = 0; @@ -812,11 +812,11 @@ static int rtl8366rb_setup(struct dsa_switch *ds) int ret; int i; - rb = smi->chip_data; + rb = priv->chip_data; - ret = regmap_read(smi->map, RTL8366RB_CHIP_ID_REG, &chip_id); + ret = regmap_read(priv->map, RTL8366RB_CHIP_ID_REG, &chip_id); if (ret) { - dev_err(smi->dev, "unable to read chip id\n"); + dev_err(priv->dev, "unable to read chip id\n"); return ret; } @@ -824,18 +824,18 @@ static int rtl8366rb_setup(struct dsa_switch *ds) case RTL8366RB_CHIP_ID_8366: break; default: - dev_err(smi->dev, "unknown chip id (%04x)\n", chip_id); + dev_err(priv->dev, "unknown chip id (%04x)\n", chip_id); return -ENODEV; } - ret = regmap_read(smi->map, RTL8366RB_CHIP_VERSION_CTRL_REG, + ret = regmap_read(priv->map, RTL8366RB_CHIP_VERSION_CTRL_REG, &chip_ver); if (ret) { - dev_err(smi->dev, "unable to read chip version\n"); + dev_err(priv->dev, "unable to read chip version\n"); return ret; } - dev_info(smi->dev, "RTL%04x ver %u chip found\n", + dev_info(priv->dev, "RTL%04x ver %u chip found\n", chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK); /* Do the init dance using the right jam table */ @@ -872,20 +872,20 @@ static int rtl8366rb_setup(struct dsa_switch *ds) jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500); } - ret = rtl8366rb_jam_table(jam_table, jam_size, smi, true); + ret = rtl8366rb_jam_table(jam_table, jam_size, priv, true); if (ret) return ret; /* Isolate all user ports so they can only send packets to itself and the CPU port */ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { - ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(i), + ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(i), RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) | RTL8366RB_PORT_ISO_EN); if (ret) return ret; } /* CPU port can send packets to all ports */ - ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), + ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(ds)) | RTL8366RB_PORT_ISO_EN); if (ret) @@ -893,26 +893,26 @@ static int rtl8366rb_setup(struct dsa_switch *ds) /* Set up the "green ethernet" feature */ ret = rtl8366rb_jam_table(rtl8366rb_green_jam, - ARRAY_SIZE(rtl8366rb_green_jam), smi, false); + ARRAY_SIZE(rtl8366rb_green_jam), priv, false); if (ret) return ret; - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, RTL8366RB_GREEN_FEATURE_REG, (chip_ver == 1) ? 0x0007 : 0x0003); if (ret) return ret; /* Vendor driver sets 0x240 in registers 0xc and 0xd (undocumented) */ - ret = regmap_write(smi->map, 0x0c, 0x240); + ret = regmap_write(priv->map, 0x0c, 0x240); if (ret) return ret; - ret = regmap_write(smi->map, 0x0d, 0x240); + ret = regmap_write(priv->map, 0x0d, 0x240); if (ret) return ret; /* Set some random MAC address */ - ret = rtl8366rb_set_addr(smi); + ret = rtl8366rb_set_addr(priv); if (ret) return ret; @@ -921,21 +921,21 @@ static int rtl8366rb_setup(struct dsa_switch *ds) * If you set RTL8368RB_CPU_NO_TAG (bit 15) in this registers * the custom tag is turned off. */ - ret = regmap_update_bits(smi->map, RTL8368RB_CPU_CTRL_REG, + ret = regmap_update_bits(priv->map, RTL8368RB_CPU_CTRL_REG, 0xFFFF, - BIT(smi->cpu_port)); + BIT(priv->cpu_port)); if (ret) return ret; /* Make sure we default-enable the fixed CPU port */ - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, - BIT(smi->cpu_port), + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, + BIT(priv->cpu_port), 0); if (ret) return ret; /* Set maximum packet length to 1536 bytes */ - ret = regmap_update_bits(smi->map, RTL8366RB_SGCR, + ret = regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK, RTL8366RB_SGCR_MAX_LENGTH_1536); if (ret) @@ -945,13 +945,13 @@ static int rtl8366rb_setup(struct dsa_switch *ds) rb->max_mtu[i] = 1532; /* Disable learning for all ports */ - ret = regmap_write(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL, + ret = regmap_write(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, RTL8366RB_PORT_ALL); if (ret) return ret; /* Enable auto ageing for all ports */ - ret = regmap_write(smi->map, RTL8366RB_SECURITY_CTRL, 0); + ret = regmap_write(priv->map, RTL8366RB_SECURITY_CTRL, 0); if (ret) return ret; @@ -962,30 +962,30 @@ static int rtl8366rb_setup(struct dsa_switch *ds) * connected to something exotic such as fiber, then this might * be worth experimenting with. */ - ret = regmap_update_bits(smi->map, RTL8366RB_PMC0, + ret = regmap_update_bits(priv->map, RTL8366RB_PMC0, RTL8366RB_PMC0_P4_IOMODE_MASK, 0 << RTL8366RB_PMC0_P4_IOMODE_SHIFT); if (ret) return ret; /* Accept all packets by default, we enable filtering on-demand */ - ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, + ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, 0); if (ret) return ret; - ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, + ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, 0); if (ret) return ret; /* Don't drop packets whose DA has not been learned */ - ret = regmap_update_bits(smi->map, RTL8366RB_SSCR2, + ret = regmap_update_bits(priv->map, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0); if (ret) return ret; /* Set blinking, TODO: make this configurable */ - ret = regmap_update_bits(smi->map, RTL8366RB_LED_BLINKRATE_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, RTL8366RB_LED_BLINKRATE_MASK, RTL8366RB_LED_BLINKRATE_56MS); if (ret) @@ -996,15 +996,15 @@ static int rtl8366rb_setup(struct dsa_switch *ds) * behaviour (no individual config) but we can set up each * LED separately. */ - if (smi->leds_disabled) { + if (priv->leds_disabled) { /* Turn everything off */ - regmap_update_bits(smi->map, + regmap_update_bits(priv->map, RTL8366RB_LED_0_1_CTRL_REG, 0x0FFF, 0); - regmap_update_bits(smi->map, + regmap_update_bits(priv->map, RTL8366RB_LED_2_3_CTRL_REG, 0x0FFF, 0); - regmap_update_bits(smi->map, + regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, RTL8366RB_P4_RGMII_LED, 0); @@ -1014,7 +1014,7 @@ static int rtl8366rb_setup(struct dsa_switch *ds) val = RTL8366RB_LED_FORCE; } for (i = 0; i < 4; i++) { - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_CTRL_REG, 0xf << (i * 4), val << (i * 4)); @@ -1022,18 +1022,20 @@ static int rtl8366rb_setup(struct dsa_switch *ds) return ret; } - ret = rtl8366_reset_vlan(smi); + ret = rtl8366_reset_vlan(priv); if (ret) return ret; - ret = rtl8366rb_setup_cascaded_irq(smi); + ret = rtl8366rb_setup_cascaded_irq(priv); if (ret) - dev_info(smi->dev, "no interrupt support\n"); + dev_info(priv->dev, "no interrupt support\n"); - ret = realtek_smi_setup_mdio(smi); - if (ret) { - dev_info(smi->dev, "could not set up MDIO bus\n"); - return -ENODEV; + if (priv->setup_interface) { + ret = priv->setup_interface(ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + return -ENODEV; + } } return 0; @@ -1052,35 +1054,35 @@ rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, struct phy_device *phydev, int speed, int duplex, bool tx_pause, bool rx_pause) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - if (port != smi->cpu_port) + if (port != priv->cpu_port) return; - dev_dbg(smi->dev, "MAC link up on CPU port (%d)\n", port); + dev_dbg(priv->dev, "MAC link up on CPU port (%d)\n", port); /* Force the fixed CPU port into 1Gbit mode, no autonegotiation */ - ret = regmap_update_bits(smi->map, RTL8366RB_MAC_FORCE_CTRL_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_MAC_FORCE_CTRL_REG, BIT(port), BIT(port)); if (ret) { - dev_err(smi->dev, "failed to force 1Gbit on CPU port\n"); + dev_err(priv->dev, "failed to force 1Gbit on CPU port\n"); return; } - ret = regmap_update_bits(smi->map, RTL8366RB_PAACR2, + ret = regmap_update_bits(priv->map, RTL8366RB_PAACR2, 0xFF00U, RTL8366RB_PAACR_CPU_PORT << 8); if (ret) { - dev_err(smi->dev, "failed to set PAACR on CPU port\n"); + dev_err(priv->dev, "failed to set PAACR on CPU port\n"); return; } /* Enable the CPU port */ - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 0); if (ret) { - dev_err(smi->dev, "failed to enable the CPU port\n"); + dev_err(priv->dev, "failed to enable the CPU port\n"); return; } } @@ -1089,99 +1091,99 @@ static void rtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - if (port != smi->cpu_port) + if (port != priv->cpu_port) return; - dev_dbg(smi->dev, "MAC link down on CPU port (%d)\n", port); + dev_dbg(priv->dev, "MAC link down on CPU port (%d)\n", port); /* Disable the CPU port */ - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), BIT(port)); if (ret) { - dev_err(smi->dev, "failed to disable the CPU port\n"); + dev_err(priv->dev, "failed to disable the CPU port\n"); return; } } -static void rb8366rb_set_port_led(struct realtek_smi *smi, +static void rb8366rb_set_port_led(struct realtek_priv *priv, int port, bool enable) { u16 val = enable ? 0x3f : 0; int ret; - if (smi->leds_disabled) + if (priv->leds_disabled) return; switch (port) { case 0: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_0_1_CTRL_REG, 0x3F, val); break; case 1: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_0_1_CTRL_REG, 0x3F << RTL8366RB_LED_1_OFFSET, val << RTL8366RB_LED_1_OFFSET); break; case 2: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_2_3_CTRL_REG, 0x3F, val); break; case 3: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_LED_2_3_CTRL_REG, 0x3F << RTL8366RB_LED_3_OFFSET, val << RTL8366RB_LED_3_OFFSET); break; case 4: - ret = regmap_update_bits(smi->map, + ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, RTL8366RB_P4_RGMII_LED, enable ? RTL8366RB_P4_RGMII_LED : 0); break; default: - dev_err(smi->dev, "no LED for port %d\n", port); + dev_err(priv->dev, "no LED for port %d\n", port); return; } if (ret) - dev_err(smi->dev, "error updating LED on port %d\n", port); + dev_err(priv->dev, "error updating LED on port %d\n", port); } static int rtl8366rb_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - dev_dbg(smi->dev, "enable port %d\n", port); - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + dev_dbg(priv->dev, "enable port %d\n", port); + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 0); if (ret) return ret; - rb8366rb_set_port_led(smi, port, true); + rb8366rb_set_port_led(priv, port, true); return 0; } static void rtl8366rb_port_disable(struct dsa_switch *ds, int port) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; - dev_dbg(smi->dev, "disable port %d\n", port); - ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), + dev_dbg(priv->dev, "disable port %d\n", port); + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), BIT(port)); if (ret) return; - rb8366rb_set_port_led(smi, port, false); + rb8366rb_set_port_led(priv, port, false); } static int @@ -1189,7 +1191,7 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, bool *tx_fwd_offload) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; unsigned int port_bitmap = 0; int ret, i; @@ -1202,17 +1204,17 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) continue; /* Join this port to each other port on the bridge */ - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i), RTL8366RB_PORT_ISO_PORTS(BIT(port)), RTL8366RB_PORT_ISO_PORTS(BIT(port))); if (ret) - dev_err(smi->dev, "failed to join port %d\n", port); + dev_err(priv->dev, "failed to join port %d\n", port); port_bitmap |= BIT(i); } /* Set the bits for the ports we can access */ - return regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port), + return regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port), RTL8366RB_PORT_ISO_PORTS(port_bitmap), RTL8366RB_PORT_ISO_PORTS(port_bitmap)); } @@ -1221,7 +1223,7 @@ static void rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; unsigned int port_bitmap = 0; int ret, i; @@ -1234,28 +1236,30 @@ rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) continue; /* Remove this port from any other port on the bridge */ - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i), RTL8366RB_PORT_ISO_PORTS(BIT(port)), 0); if (ret) - dev_err(smi->dev, "failed to leave port %d\n", port); + dev_err(priv->dev, "failed to leave port %d\n", port); port_bitmap |= BIT(i); } /* Clear the bits for the ports we can not access, leave ourselves */ - regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port), + regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port), RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0); } /** * rtl8366rb_drop_untagged() - make the switch drop untagged and C-tagged frames - * @smi: SMI state container + * @priv: SMI state container * @port: the port to drop untagged and C-tagged frames on * @drop: whether to drop or pass untagged and C-tagged frames + * + * Return: zero for success, a negative number on error. */ -static int rtl8366rb_drop_untagged(struct realtek_smi *smi, int port, bool drop) +static int rtl8366rb_drop_untagged(struct realtek_priv *priv, int port, bool drop) { - return regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, + return regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port), drop ? RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) : 0); } @@ -1264,17 +1268,17 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, struct netlink_ext_ack *extack) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8366rb *rb; int ret; - rb = smi->chip_data; + rb = priv->chip_data; - dev_dbg(smi->dev, "port %d: %s VLAN filtering\n", port, + dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, vlan_filtering ? "enable" : "disable"); /* If the port is not in the member set, the frame will be dropped */ - ret = regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, + ret = regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, BIT(port), vlan_filtering ? BIT(port) : 0); if (ret) return ret; @@ -1284,9 +1288,9 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, * filtering on a port, we need to accept any frames. */ if (vlan_filtering) - ret = rtl8366rb_drop_untagged(smi, port, !rb->pvid_enabled[port]); + ret = rtl8366rb_drop_untagged(priv, port, !rb->pvid_enabled[port]); else - ret = rtl8366rb_drop_untagged(smi, port, false); + ret = rtl8366rb_drop_untagged(priv, port, false); return ret; } @@ -1308,11 +1312,11 @@ rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; int ret; if (flags.mask & BR_LEARNING) { - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL, + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, BIT(port), (flags.val & BR_LEARNING) ? 0 : BIT(port)); if (ret) @@ -1325,7 +1329,7 @@ rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port, static void rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; u32 val; int i; @@ -1344,13 +1348,13 @@ rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) val = RTL8366RB_STP_STATE_FORWARDING; break; default: - dev_err(smi->dev, "unknown bridge state requested\n"); + dev_err(priv->dev, "unknown bridge state requested\n"); return; } /* Set the same status for the port on all the FIDs */ for (i = 0; i < RTL8366RB_NUM_FIDS; i++) { - regmap_update_bits(smi->map, RTL8366RB_STP_STATE_BASE + i, + regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i, RTL8366RB_STP_STATE_MASK(port), RTL8366RB_STP_STATE(port, val)); } @@ -1359,26 +1363,26 @@ rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) static void rtl8366rb_port_fast_age(struct dsa_switch *ds, int port) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; /* This will age out any learned L2 entries */ - regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL, + regmap_update_bits(priv->map, RTL8366RB_SECURITY_CTRL, BIT(port), BIT(port)); /* Restore the normal state of things */ - regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL, + regmap_update_bits(priv->map, RTL8366RB_SECURITY_CTRL, BIT(port), 0); } static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { - struct realtek_smi *smi = ds->priv; + struct realtek_priv *priv = ds->priv; struct rtl8366rb *rb; unsigned int max_mtu; u32 len; int i; /* Cache the per-port MTU setting */ - rb = smi->chip_data; + rb = priv->chip_data; rb->max_mtu[port] = new_mtu; /* Roof out the MTU for the entire switch to the greatest @@ -1406,7 +1410,7 @@ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) else len = RTL8366RB_SGCR_MAX_LENGTH_16000; - return regmap_update_bits(smi->map, RTL8366RB_SGCR, + return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK, len); } @@ -1419,7 +1423,7 @@ static int rtl8366rb_max_mtu(struct dsa_switch *ds, int port) return 15996; } -static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid, +static int rtl8366rb_get_vlan_4k(struct realtek_priv *priv, u32 vid, struct rtl8366_vlan_4k *vlan4k) { u32 data[3]; @@ -1432,19 +1436,19 @@ static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid, return -EINVAL; /* write VID */ - ret = regmap_write(smi->map, RTL8366RB_VLAN_TABLE_WRITE_BASE, + ret = regmap_write(priv->map, RTL8366RB_VLAN_TABLE_WRITE_BASE, vid & RTL8366RB_VLAN_VID_MASK); if (ret) return ret; /* write table access control word */ - ret = regmap_write(smi->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, RTL8366RB_TABLE_VLAN_READ_CTRL); if (ret) return ret; for (i = 0; i < 3; i++) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8366RB_VLAN_TABLE_READ_BASE + i, &data[i]); if (ret) @@ -1460,7 +1464,7 @@ static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid, return 0; } -static int rtl8366rb_set_vlan_4k(struct realtek_smi *smi, +static int rtl8366rb_set_vlan_4k(struct realtek_priv *priv, const struct rtl8366_vlan_4k *vlan4k) { u32 data[3]; @@ -1480,7 +1484,7 @@ static int rtl8366rb_set_vlan_4k(struct realtek_smi *smi, data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK; for (i = 0; i < 3; i++) { - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, RTL8366RB_VLAN_TABLE_WRITE_BASE + i, data[i]); if (ret) @@ -1488,13 +1492,13 @@ static int rtl8366rb_set_vlan_4k(struct realtek_smi *smi, } /* write table access control word */ - ret = regmap_write(smi->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, RTL8366RB_TABLE_VLAN_WRITE_CTRL); return ret; } -static int rtl8366rb_get_vlan_mc(struct realtek_smi *smi, u32 index, +static int rtl8366rb_get_vlan_mc(struct realtek_priv *priv, u32 index, struct rtl8366_vlan_mc *vlanmc) { u32 data[3]; @@ -1507,7 +1511,7 @@ static int rtl8366rb_get_vlan_mc(struct realtek_smi *smi, u32 index, return -EINVAL; for (i = 0; i < 3; i++) { - ret = regmap_read(smi->map, + ret = regmap_read(priv->map, RTL8366RB_VLAN_MC_BASE(index) + i, &data[i]); if (ret) @@ -1525,7 +1529,7 @@ static int rtl8366rb_get_vlan_mc(struct realtek_smi *smi, u32 index, return 0; } -static int rtl8366rb_set_vlan_mc(struct realtek_smi *smi, u32 index, +static int rtl8366rb_set_vlan_mc(struct realtek_priv *priv, u32 index, const struct rtl8366_vlan_mc *vlanmc) { u32 data[3]; @@ -1549,7 +1553,7 @@ static int rtl8366rb_set_vlan_mc(struct realtek_smi *smi, u32 index, data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK; for (i = 0; i < 3; i++) { - ret = regmap_write(smi->map, + ret = regmap_write(priv->map, RTL8366RB_VLAN_MC_BASE(index) + i, data[i]); if (ret) @@ -1559,15 +1563,15 @@ static int rtl8366rb_set_vlan_mc(struct realtek_smi *smi, u32 index, return 0; } -static int rtl8366rb_get_mc_index(struct realtek_smi *smi, int port, int *val) +static int rtl8366rb_get_mc_index(struct realtek_priv *priv, int port, int *val) { u32 data; int ret; - if (port >= smi->num_ports) + if (port >= priv->num_ports) return -EINVAL; - ret = regmap_read(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), + ret = regmap_read(priv->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), &data); if (ret) return ret; @@ -1578,22 +1582,22 @@ static int rtl8366rb_get_mc_index(struct realtek_smi *smi, int port, int *val) return 0; } -static int rtl8366rb_set_mc_index(struct realtek_smi *smi, int port, int index) +static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index) { struct rtl8366rb *rb; bool pvid_enabled; int ret; - rb = smi->chip_data; + rb = priv->chip_data; pvid_enabled = !!index; - if (port >= smi->num_ports || index >= RTL8366RB_NUM_VLANS) + if (port >= priv->num_ports || index >= RTL8366RB_NUM_VLANS) return -EINVAL; - ret = regmap_update_bits(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), - RTL8366RB_PORT_VLAN_CTRL_MASK << + ret = regmap_update_bits(priv->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), + RTL8366RB_PORT_VLAN_CTRL_MASK << RTL8366RB_PORT_VLAN_CTRL_SHIFT(port), - (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << + (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)); if (ret) return ret; @@ -1604,17 +1608,17 @@ static int rtl8366rb_set_mc_index(struct realtek_smi *smi, int port, int index) * not drop any untagged or C-tagged frames. Make sure to update the * filtering setting. */ - if (dsa_port_is_vlan_filtering(dsa_to_port(smi->ds, port))) - ret = rtl8366rb_drop_untagged(smi, port, !pvid_enabled); + if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port))) + ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled); return ret; } -static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan) +static bool rtl8366rb_is_vlan_valid(struct realtek_priv *priv, unsigned int vlan) { unsigned int max = RTL8366RB_NUM_VLANS - 1; - if (smi->vlan4k_enabled) + if (priv->vlan4k_enabled) max = RTL8366RB_NUM_VIDS - 1; if (vlan > max) @@ -1623,23 +1627,23 @@ static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan) return true; } -static int rtl8366rb_enable_vlan(struct realtek_smi *smi, bool enable) +static int rtl8366rb_enable_vlan(struct realtek_priv *priv, bool enable) { - dev_dbg(smi->dev, "%s VLAN\n", enable ? "enable" : "disable"); - return regmap_update_bits(smi->map, + dev_dbg(priv->dev, "%s VLAN\n", enable ? "enable" : "disable"); + return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN, enable ? RTL8366RB_SGCR_EN_VLAN : 0); } -static int rtl8366rb_enable_vlan4k(struct realtek_smi *smi, bool enable) +static int rtl8366rb_enable_vlan4k(struct realtek_priv *priv, bool enable) { - dev_dbg(smi->dev, "%s VLAN 4k\n", enable ? "enable" : "disable"); - return regmap_update_bits(smi->map, RTL8366RB_SGCR, + dev_dbg(priv->dev, "%s VLAN 4k\n", enable ? "enable" : "disable"); + return regmap_update_bits(priv->map, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN_4KTB, enable ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0); } -static int rtl8366rb_phy_read(struct realtek_smi *smi, int phy, int regnum) +static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) { u32 val; u32 reg; @@ -1648,32 +1652,32 @@ static int rtl8366rb_phy_read(struct realtek_smi *smi, int phy, int regnum) if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - ret = regmap_write(smi->map, RTL8366RB_PHY_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_READ); if (ret) return ret; reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; - ret = regmap_write(smi->map, reg, 0); + ret = regmap_write(priv->map, reg, 0); if (ret) { - dev_err(smi->dev, + dev_err(priv->dev, "failed to write PHY%d reg %04x @ %04x, ret %d\n", phy, regnum, reg, ret); return ret; } - ret = regmap_read(smi->map, RTL8366RB_PHY_ACCESS_DATA_REG, &val); + ret = regmap_read(priv->map, RTL8366RB_PHY_ACCESS_DATA_REG, &val); if (ret) return ret; - dev_dbg(smi->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", + dev_dbg(priv->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", phy, regnum, reg, val); return val; } -static int rtl8366rb_phy_write(struct realtek_smi *smi, int phy, int regnum, +static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, u16 val) { u32 reg; @@ -1682,34 +1686,45 @@ static int rtl8366rb_phy_write(struct realtek_smi *smi, int phy, int regnum, if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - ret = regmap_write(smi->map, RTL8366RB_PHY_ACCESS_CTRL_REG, + ret = regmap_write(priv->map, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_WRITE); if (ret) return ret; reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; - dev_dbg(smi->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", + dev_dbg(priv->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", phy, regnum, reg, val); - ret = regmap_write(smi->map, reg, val); + ret = regmap_write(priv->map, reg, val); if (ret) return ret; return 0; } -static int rtl8366rb_reset_chip(struct realtek_smi *smi) +static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +{ + return rtl8366rb_phy_read(ds->priv, phy, regnum); +} + +static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, + u16 val) +{ + return rtl8366rb_phy_write(ds->priv, phy, regnum, val); +} + +static int rtl8366rb_reset_chip(struct realtek_priv *priv) { int timeout = 10; u32 val; int ret; - realtek_smi_write_reg_noack(smi, RTL8366RB_RESET_CTRL_REG, - RTL8366RB_CHIP_CTRL_RESET_HW); + priv->write_reg_noack(priv, RTL8366RB_RESET_CTRL_REG, + RTL8366RB_CHIP_CTRL_RESET_HW); do { usleep_range(20000, 25000); - ret = regmap_read(smi->map, RTL8366RB_RESET_CTRL_REG, &val); + ret = regmap_read(priv->map, RTL8366RB_RESET_CTRL_REG, &val); if (ret) return ret; @@ -1718,21 +1733,21 @@ static int rtl8366rb_reset_chip(struct realtek_smi *smi) } while (--timeout); if (!timeout) { - dev_err(smi->dev, "timeout waiting for the switch to reset\n"); + dev_err(priv->dev, "timeout waiting for the switch to reset\n"); return -EIO; } return 0; } -static int rtl8366rb_detect(struct realtek_smi *smi) +static int rtl8366rb_detect(struct realtek_priv *priv) { - struct device *dev = smi->dev; + struct device *dev = priv->dev; int ret; u32 val; /* Detect device */ - ret = regmap_read(smi->map, 0x5c, &val); + ret = regmap_read(priv->map, 0x5c, &val); if (ret) { dev_err(dev, "can't get chip ID (%d)\n", ret); return ret; @@ -1745,11 +1760,11 @@ static int rtl8366rb_detect(struct realtek_smi *smi) return -ENODEV; case 0x5937: dev_info(dev, "found an RTL8366RB switch\n"); - smi->cpu_port = RTL8366RB_PORT_NUM_CPU; - smi->num_ports = RTL8366RB_NUM_PORTS; - smi->num_vlan_mc = RTL8366RB_NUM_VLANS; - smi->mib_counters = rtl8366rb_mib_counters; - smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters); + priv->cpu_port = RTL8366RB_PORT_NUM_CPU; + priv->num_ports = RTL8366RB_NUM_PORTS; + priv->num_vlan_mc = RTL8366RB_NUM_VLANS; + priv->mib_counters = rtl8366rb_mib_counters; + priv->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters); break; default: dev_info(dev, "found an Unknown Realtek switch (id=0x%04x)\n", @@ -1757,14 +1772,14 @@ static int rtl8366rb_detect(struct realtek_smi *smi) break; } - ret = rtl8366rb_reset_chip(smi); + ret = rtl8366rb_reset_chip(priv); if (ret) return ret; return 0; } -static const struct dsa_switch_ops rtl8366rb_switch_ops = { +static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { .get_tag_protocol = rtl8366_get_tag_protocol, .setup = rtl8366rb_setup, .phylink_mac_link_up = rtl8366rb_mac_link_up, @@ -1787,7 +1802,32 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = { .port_max_mtu = rtl8366rb_max_mtu, }; -static const struct realtek_smi_ops rtl8366rb_smi_ops = { +static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { + .get_tag_protocol = rtl8366_get_tag_protocol, + .setup = rtl8366rb_setup, + .phy_read = rtl8366rb_dsa_phy_read, + .phy_write = rtl8366rb_dsa_phy_write, + .phylink_mac_link_up = rtl8366rb_mac_link_up, + .phylink_mac_link_down = rtl8366rb_mac_link_down, + .get_strings = rtl8366_get_strings, + .get_ethtool_stats = rtl8366_get_ethtool_stats, + .get_sset_count = rtl8366_get_sset_count, + .port_bridge_join = rtl8366rb_port_bridge_join, + .port_bridge_leave = rtl8366rb_port_bridge_leave, + .port_vlan_filtering = rtl8366rb_vlan_filtering, + .port_vlan_add = rtl8366_vlan_add, + .port_vlan_del = rtl8366_vlan_del, + .port_enable = rtl8366rb_port_enable, + .port_disable = rtl8366rb_port_disable, + .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, + .port_bridge_flags = rtl8366rb_port_bridge_flags, + .port_stp_state_set = rtl8366rb_port_stp_state_set, + .port_fast_age = rtl8366rb_port_fast_age, + .port_change_mtu = rtl8366rb_change_mtu, + .port_max_mtu = rtl8366rb_max_mtu, +}; + +static const struct realtek_ops rtl8366rb_ops = { .detect = rtl8366rb_detect, .get_vlan_mc = rtl8366rb_get_vlan_mc, .set_vlan_mc = rtl8366rb_set_vlan_mc, @@ -1803,12 +1843,17 @@ static const struct realtek_smi_ops rtl8366rb_smi_ops = { .phy_write = rtl8366rb_phy_write, }; -const struct realtek_smi_variant rtl8366rb_variant = { - .ds_ops = &rtl8366rb_switch_ops, - .ops = &rtl8366rb_smi_ops, +const struct realtek_variant rtl8366rb_variant = { + .ds_ops_smi = &rtl8366rb_switch_ops_smi, + .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, + .ops = &rtl8366rb_ops, .clk_delay = 10, .cmd_read = 0xa9, .cmd_write = 0xa8, .chip_data_sz = sizeof(struct rtl8366rb), }; EXPORT_SYMBOL_GPL(rtl8366rb_variant); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c index 0730352cdd57..bc06fe6bac6b 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.c +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -442,34 +442,27 @@ static void xrs700x_teardown(struct dsa_switch *ds) cancel_delayed_work_sync(&priv->mib_work); } -static void xrs700x_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void xrs700x_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - switch (port) { case 0: + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + config->mac_capabilities = MAC_10FD | MAC_100FD; break; + case 1: case 2: case 3: - phylink_set(mask, 1000baseT_Full); + phy_interface_set_rgmii(config->supported_interfaces); + config->mac_capabilities = MAC_10FD | MAC_100FD | MAC_1000FD; break; + default: - linkmode_zero(supported); dev_err(ds->dev, "Unsupported port: %i\n", port); - return; + break; } - - phylink_set_port_modes(mask); - - /* The switch only supports full duplex. */ - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); } static void xrs700x_mac_link_up(struct dsa_switch *ds, int port, @@ -703,7 +696,7 @@ static const struct dsa_switch_ops xrs700x_ops = { .setup = xrs700x_setup, .teardown = xrs700x_teardown, .port_stp_state_set = xrs700x_port_stp_state_set, - .phylink_validate = xrs700x_phylink_validate, + .phylink_get_caps = xrs700x_phylink_get_caps, .phylink_mac_link_up = xrs700x_mac_link_up, .get_strings = xrs700x_get_strings, .get_sset_count = xrs700x_get_sset_count, |