diff options
Diffstat (limited to 'drivers/net/dsa')
24 files changed, 1472 insertions, 1343 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 37a3dabdce31..6d1fcb08bba1 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -72,7 +72,6 @@ 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 @@ -82,6 +81,7 @@ config NET_DSA_SMSC_LAN9303 config NET_DSA_SMSC_LAN9303_I2C tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in I2C managed mode" depends on I2C + depends on VLAN_8021Q || VLAN_8021Q=n select NET_DSA_SMSC_LAN9303 select REGMAP_I2C help @@ -91,6 +91,7 @@ config NET_DSA_SMSC_LAN9303_I2C config NET_DSA_SMSC_LAN9303_MDIO tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in MDIO managed mode" select NET_DSA_SMSC_LAN9303 + depends on VLAN_8021Q || VLAN_8021Q=n help Enable access functions if the SMSC/Microchip LAN9303 is configured for MDIO managed mode. diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 12c15da55664..8af4def38a98 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1360,7 +1360,7 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port, struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port)); struct gswip_priv *priv = ds->priv; struct gswip_pce_table_entry mac_bridge = {0,}; - unsigned int cpu_port = priv->hw_info->cpu_port; + unsigned int max_ports = priv->hw_info->max_ports; int fid = -1; int i; int err; @@ -1368,7 +1368,7 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port, if (!bridge) return -EINVAL; - for (i = cpu_port; i < ARRAY_SIZE(priv->vlans); i++) { + for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) { if (priv->vlans[i].bridge == bridge) { fid = priv->vlans[i].fid; break; @@ -1426,8 +1426,9 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port, err = gswip_pce_table_entry_read(priv, &mac_bridge); if (err) { - dev_err(priv->dev, "failed to write mac bridge: %d\n", - err); + dev_err(priv->dev, + "failed to read mac bridge entry %d: %d\n", + i, err); return err; } diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index b2752978cb09..12a599d5e61a 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -126,86 +126,6 @@ static u8 ksz8863_shifts[] = { [DYNAMIC_MAC_SRC_PORT] = 20, }; -struct mib_names { - char string[ETH_GSTRING_LEN]; -}; - -static const struct mib_names ksz87xx_mib_names[] = { - { "rx_hi" }, - { "rx_undersize" }, - { "rx_fragments" }, - { "rx_oversize" }, - { "rx_jabbers" }, - { "rx_symbol_err" }, - { "rx_crc_err" }, - { "rx_align_err" }, - { "rx_mac_ctrl" }, - { "rx_pause" }, - { "rx_bcast" }, - { "rx_mcast" }, - { "rx_ucast" }, - { "rx_64_or_less" }, - { "rx_65_127" }, - { "rx_128_255" }, - { "rx_256_511" }, - { "rx_512_1023" }, - { "rx_1024_1522" }, - { "rx_1523_2000" }, - { "rx_2001" }, - { "tx_hi" }, - { "tx_late_col" }, - { "tx_pause" }, - { "tx_bcast" }, - { "tx_mcast" }, - { "tx_ucast" }, - { "tx_deferred" }, - { "tx_total_col" }, - { "tx_exc_col" }, - { "tx_single_col" }, - { "tx_mult_col" }, - { "rx_total" }, - { "tx_total" }, - { "rx_discards" }, - { "tx_discards" }, -}; - -static const struct mib_names ksz88xx_mib_names[] = { - { "rx" }, - { "rx_hi" }, - { "rx_undersize" }, - { "rx_fragments" }, - { "rx_oversize" }, - { "rx_jabbers" }, - { "rx_symbol_err" }, - { "rx_crc_err" }, - { "rx_align_err" }, - { "rx_mac_ctrl" }, - { "rx_pause" }, - { "rx_bcast" }, - { "rx_mcast" }, - { "rx_ucast" }, - { "rx_64_or_less" }, - { "rx_65_127" }, - { "rx_128_255" }, - { "rx_256_511" }, - { "rx_512_1023" }, - { "rx_1024_1522" }, - { "tx" }, - { "tx_hi" }, - { "tx_late_col" }, - { "tx_pause" }, - { "tx_bcast" }, - { "tx_mcast" }, - { "tx_ucast" }, - { "tx_deferred" }, - { "tx_total_col" }, - { "tx_exc_col" }, - { "tx_single_col" }, - { "tx_mult_col" }, - { "rx_discards" }, - { "tx_discards" }, -}; - static bool ksz_is_ksz88x3(struct ksz_device *dev) { return dev->chip_id == 0x8830; @@ -306,7 +226,7 @@ static void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) masks = ksz8->masks; regs = ksz8->regs; - ctrl_addr = addr + dev->reg_mib_cnt * port; + ctrl_addr = addr + dev->info->reg_mib_cnt * port; ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); mutex_lock(&dev->alu_mutex); @@ -343,7 +263,7 @@ static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, masks = ksz8->masks; regs = ksz8->regs; - addr -= dev->reg_mib_cnt; + addr -= dev->info->reg_mib_cnt; ctrl_addr = (KSZ8795_MIB_TOTAL_RX_1 - KSZ8795_MIB_TOTAL_RX_0) * port; ctrl_addr += addr + KSZ8795_MIB_TOTAL_RX_0; ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); @@ -392,7 +312,7 @@ static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u32 data; u32 cur; - addr -= dev->reg_mib_cnt; + addr -= dev->info->reg_mib_cnt; ctrl_addr = addr ? KSZ8863_MIB_PACKET_DROPPED_TX_0 : KSZ8863_MIB_PACKET_DROPPED_RX_0; ctrl_addr += port; @@ -453,23 +373,21 @@ static void ksz8_port_init_cnt(struct ksz_device *dev, int port) mib->cnt_ptr = 0; /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->reg_mib_cnt) { + while (mib->cnt_ptr < dev->info->reg_mib_cnt) { dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; } /* last one in storage */ - dropped = &mib->counters[dev->mib_cnt]; + dropped = &mib->counters[dev->info->mib_cnt]; /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->mib_cnt) { + while (mib->cnt_ptr < dev->info->mib_cnt) { dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, dropped, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; } - mib->cnt_ptr = 0; - memset(mib->counters, 0, dev->mib_cnt * sizeof(u64)); } static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) @@ -1003,18 +921,6 @@ static u32 ksz8_sw_get_phy_flags(struct dsa_switch *ds, int port) return 0; } -static void ksz8_get_strings(struct dsa_switch *ds, int port, - u32 stringset, uint8_t *buf) -{ - struct ksz_device *dev = ds->priv; - int i; - - for (i = 0; i < dev->mib_cnt; i++) { - memcpy(buf + i * ETH_GSTRING_LEN, - dev->mib_names[i].string, ETH_GSTRING_LEN); - } -} - static void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) { u8 data; @@ -1027,40 +933,7 @@ static void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) static void ksz8_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - struct ksz_device *dev = ds->priv; - struct ksz_port *p; - u8 data; - - ksz_pread8(dev, port, P_STP_CTRL, &data); - data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); - - switch (state) { - case BR_STATE_DISABLED: - data |= PORT_LEARN_DISABLE; - break; - case BR_STATE_LISTENING: - data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); - break; - case BR_STATE_LEARNING: - data |= PORT_RX_ENABLE; - break; - case BR_STATE_FORWARDING: - data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); - break; - case BR_STATE_BLOCKING: - data |= PORT_LEARN_DISABLE; - break; - default: - dev_err(ds->dev, "invalid STP state: %d\n", state); - return; - } - - ksz_pwrite8(dev, port, P_STP_CTRL, data); - - p = &dev->ports[port]; - p->stp_state = state; - - ksz_update_port_member(dev, port); + ksz_port_stp_state_set(ds, port, state, P_STP_CTRL); } static void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) @@ -1069,13 +942,13 @@ static void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) int first, index, cnt; struct ksz_port *p; - if ((uint)port < dev->port_cnt) { + if ((uint)port < dev->info->port_cnt) { first = port; cnt = port + 1; } else { /* Flush all ports. */ first = 0; - cnt = dev->port_cnt; + cnt = dev->info->port_cnt; } for (index = first; index < cnt; index++) { p = &dev->ports[index]; @@ -1151,7 +1024,7 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, * Remove Tag flag to be changed, unless there are no * other VLANs currently configured. */ - for (vid = 1; vid < dev->num_vlans; ++vid) { + for (vid = 1; vid < dev->info->num_vlans; ++vid) { /* Skip the VID we are going to add or reconfigure */ if (vid == vlan->vid) continue; @@ -1422,7 +1295,7 @@ static int ksz8_handle_global_errata(struct dsa_switch *ds) * KSZ879x/KSZ877x/KSZ876x and some EEE link partners may result in * the link dropping. */ - if (dev->ksz87xx_eee_link_erratum) + if (dev->info->ksz87xx_eee_link_erratum) ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0); return ret; @@ -1435,7 +1308,7 @@ static int ksz8_setup(struct dsa_switch *ds) int i, ret = 0; dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), - dev->num_vlans, GFP_KERNEL); + dev->info->num_vlans, GFP_KERNEL); if (!dev->vlan_cache) return -ENOMEM; @@ -1479,7 +1352,7 @@ static int ksz8_setup(struct dsa_switch *ds) (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100); - for (i = 0; i < (dev->num_vlans / 4); i++) + for (i = 0; i < (dev->info->num_vlans / 4); i++) ksz8_r_vlan_entries(dev, i); /* Setup STP address for STP operation. */ @@ -1487,7 +1360,7 @@ static int ksz8_setup(struct dsa_switch *ds) ether_addr_copy(alu.mac, eth_stp_addr); alu.is_static = true; alu.is_override = true; - alu.port_forward = dev->host_mask; + alu.port_forward = dev->info->cpu_ports; ksz8_w_sta_mac_table(dev, 0, &alu); @@ -1503,15 +1376,7 @@ static void ksz8_get_caps(struct dsa_switch *ds, int port, { struct ksz_device *dev = ds->priv; - if (port == dev->cpu_port) { - __set_bit(PHY_INTERFACE_MODE_RMII, - config->supported_interfaces); - __set_bit(PHY_INTERFACE_MODE_MII, - config->supported_interfaces); - } else { - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - config->supported_interfaces); - } + ksz_phylink_get_caps(ds, port, config); config->mac_capabilities = MAC_10 | MAC_100; @@ -1537,7 +1402,7 @@ static const struct dsa_switch_ops ksz8_switch_ops = { .phylink_get_caps = ksz8_get_caps, .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_port, - .get_strings = ksz8_get_strings, + .get_strings = ksz_get_strings, .get_ethtool_stats = ksz_get_ethtool_stats, .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, @@ -1604,140 +1469,26 @@ static int ksz8_switch_detect(struct ksz_device *dev) return 0; } -struct ksz_chip_data { - u16 chip_id; - const char *dev_name; - int num_vlans; - int num_alus; - int num_statics; - int cpu_ports; - int port_cnt; - bool ksz87xx_eee_link_erratum; -}; - -static const struct ksz_chip_data ksz8_switch_chips[] = { - { - .chip_id = 0x8795, - .dev_name = "KSZ8795", - .num_vlans = 4096, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x10, /* can be configured as cpu port */ - .port_cnt = 5, /* total cpu and user ports */ - .ksz87xx_eee_link_erratum = true, - }, - { - /* - * WARNING - * ======= - * KSZ8794 is similar to KSZ8795, except the port map - * contains a gap between external and CPU ports, the - * port map is NOT continuous. The per-port register - * map is shifted accordingly too, i.e. registers at - * offset 0x40 are NOT used on KSZ8794 and they ARE - * used on KSZ8795 for external port 3. - * external cpu - * KSZ8794 0,1,2 4 - * KSZ8795 0,1,2,3 4 - * KSZ8765 0,1,2,3 4 - */ - .chip_id = 0x8794, - .dev_name = "KSZ8794", - .num_vlans = 4096, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x10, /* can be configured as cpu port */ - .port_cnt = 4, /* total cpu and user ports */ - .ksz87xx_eee_link_erratum = true, - }, - { - .chip_id = 0x8765, - .dev_name = "KSZ8765", - .num_vlans = 4096, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x10, /* can be configured as cpu port */ - .port_cnt = 5, /* total cpu and user ports */ - .ksz87xx_eee_link_erratum = true, - }, - { - .chip_id = 0x8830, - .dev_name = "KSZ8863/KSZ8873", - .num_vlans = 16, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x4, /* can be configured as cpu port */ - .port_cnt = 3, - }, -}; - static int ksz8_switch_init(struct ksz_device *dev) { struct ksz8 *ksz8 = dev->priv; - int i; dev->ds->ops = &ksz8_switch_ops; - for (i = 0; i < ARRAY_SIZE(ksz8_switch_chips); i++) { - const struct ksz_chip_data *chip = &ksz8_switch_chips[i]; - - if (dev->chip_id == chip->chip_id) { - dev->name = chip->dev_name; - dev->num_vlans = chip->num_vlans; - dev->num_alus = chip->num_alus; - dev->num_statics = chip->num_statics; - dev->port_cnt = fls(chip->cpu_ports); - dev->cpu_port = fls(chip->cpu_ports) - 1; - dev->phy_port_cnt = dev->port_cnt - 1; - dev->cpu_ports = chip->cpu_ports; - dev->host_mask = chip->cpu_ports; - dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | - chip->cpu_ports; - dev->ksz87xx_eee_link_erratum = - chip->ksz87xx_eee_link_erratum; - break; - } - } - - /* no switch found */ - if (!dev->cpu_ports) - return -ENODEV; + dev->cpu_port = fls(dev->info->cpu_ports) - 1; + dev->phy_port_cnt = dev->info->port_cnt - 1; + dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | dev->info->cpu_ports; if (ksz_is_ksz88x3(dev)) { ksz8->regs = ksz8863_regs; ksz8->masks = ksz8863_masks; ksz8->shifts = ksz8863_shifts; - dev->mib_cnt = ARRAY_SIZE(ksz88xx_mib_names); - dev->mib_names = ksz88xx_mib_names; } else { ksz8->regs = ksz8795_regs; ksz8->masks = ksz8795_masks; ksz8->shifts = ksz8795_shifts; - dev->mib_cnt = ARRAY_SIZE(ksz87xx_mib_names); - dev->mib_names = ksz87xx_mib_names; - } - - dev->reg_mib_cnt = MIB_COUNTER_NUM; - - dev->ports = devm_kzalloc(dev->dev, - dev->port_cnt * sizeof(struct ksz_port), - GFP_KERNEL); - if (!dev->ports) - return -ENOMEM; - for (i = 0; i < dev->port_cnt; i++) { - mutex_init(&dev->ports[i].mib.cnt_mutex); - dev->ports[i].mib.counters = - devm_kzalloc(dev->dev, - sizeof(u64) * - (dev->mib_cnt + 1), - GFP_KERNEL); - if (!dev->ports[i].mib.counters) - return -ENOMEM; } - /* set the real number of ports */ - dev->ds->num_ports = dev->port_cnt; - /* We rely on software untagging on the CPU port, so that we * can support both tagged and untagged VLANs */ diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index d74defcd86b4..4109433b6b6c 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -160,9 +160,6 @@ #define PORT_DISCARD_NON_VID BIT(5) #define PORT_FORCE_FLOW_CTRL BIT(4) #define PORT_BACK_PRESSURE BIT(3) -#define PORT_TX_ENABLE BIT(2) -#define PORT_RX_ENABLE BIT(1) -#define PORT_LEARN_DISABLE BIT(0) #define REG_PORT_1_CTRL_3 0x13 #define REG_PORT_2_CTRL_3 0x23 diff --git a/drivers/net/dsa/microchip/ksz8795_spi.c b/drivers/net/dsa/microchip/ksz8795_spi.c index 5f8d94aee774..961a74c359a8 100644 --- a/drivers/net/dsa/microchip/ksz8795_spi.c +++ b/drivers/net/dsa/microchip/ksz8795_spi.c @@ -34,6 +34,7 @@ KSZ_REGMAP_TABLE(ksz8863, 16, KSZ8863_SPI_ADDR_SHIFT, static int ksz8795_spi_probe(struct spi_device *spi) { const struct regmap_config *regmap_config; + const struct ksz_chip_data *chip; struct device *ddev = &spi->dev; struct regmap_config rc; struct ksz_device *dev; @@ -50,10 +51,15 @@ static int ksz8795_spi_probe(struct spi_device *spi) if (!dev) return -ENOMEM; - regmap_config = device_get_match_data(ddev); - if (!regmap_config) + chip = device_get_match_data(ddev); + if (!chip) return -EINVAL; + if (chip->chip_id == KSZ8830_CHIP_ID) + regmap_config = ksz8863_regmap_config; + else + regmap_config = ksz8795_regmap_config; + for (i = 0; i < ARRAY_SIZE(ksz8795_regmap_config); i++) { rc = regmap_config[i]; rc.lock_arg = &dev->regmap_mutex; @@ -113,11 +119,26 @@ static void ksz8795_spi_shutdown(struct spi_device *spi) } static const struct of_device_id ksz8795_dt_ids[] = { - { .compatible = "microchip,ksz8765", .data = &ksz8795_regmap_config }, - { .compatible = "microchip,ksz8794", .data = &ksz8795_regmap_config }, - { .compatible = "microchip,ksz8795", .data = &ksz8795_regmap_config }, - { .compatible = "microchip,ksz8863", .data = &ksz8863_regmap_config }, - { .compatible = "microchip,ksz8873", .data = &ksz8863_regmap_config }, + { + .compatible = "microchip,ksz8765", + .data = &ksz_switch_chips[KSZ8765] + }, + { + .compatible = "microchip,ksz8794", + .data = &ksz_switch_chips[KSZ8794] + }, + { + .compatible = "microchip,ksz8795", + .data = &ksz_switch_chips[KSZ8795] + }, + { + .compatible = "microchip,ksz8863", + .data = &ksz_switch_chips[KSZ8830] + }, + { + .compatible = "microchip,ksz8873", + .data = &ksz_switch_chips[KSZ8830] + }, {}, }; MODULE_DEVICE_TABLE(of, ksz8795_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index 5883fa7edda2..b6f99e641dca 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -206,8 +206,14 @@ static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) } static const struct of_device_id ksz8863_dt_ids[] = { - { .compatible = "microchip,ksz8863" }, - { .compatible = "microchip,ksz8873" }, + { + .compatible = "microchip,ksz8863", + .data = &ksz_switch_chips[KSZ8830] + }, + { + .compatible = "microchip,ksz8873", + .data = &ksz_switch_chips[KSZ8830] + }, { }, }; MODULE_DEVICE_TABLE(of, ksz8863_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 7310d19d1f06..ab40b700cf1a 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -23,142 +23,6 @@ #define NEW_XMII BIT(1) #define IS_9893 BIT(2) -static const struct { - int index; - char string[ETH_GSTRING_LEN]; -} ksz9477_mib_names[TOTAL_SWITCH_COUNTER_NUM] = { - { 0x00, "rx_hi" }, - { 0x01, "rx_undersize" }, - { 0x02, "rx_fragments" }, - { 0x03, "rx_oversize" }, - { 0x04, "rx_jabbers" }, - { 0x05, "rx_symbol_err" }, - { 0x06, "rx_crc_err" }, - { 0x07, "rx_align_err" }, - { 0x08, "rx_mac_ctrl" }, - { 0x09, "rx_pause" }, - { 0x0A, "rx_bcast" }, - { 0x0B, "rx_mcast" }, - { 0x0C, "rx_ucast" }, - { 0x0D, "rx_64_or_less" }, - { 0x0E, "rx_65_127" }, - { 0x0F, "rx_128_255" }, - { 0x10, "rx_256_511" }, - { 0x11, "rx_512_1023" }, - { 0x12, "rx_1024_1522" }, - { 0x13, "rx_1523_2000" }, - { 0x14, "rx_2001" }, - { 0x15, "tx_hi" }, - { 0x16, "tx_late_col" }, - { 0x17, "tx_pause" }, - { 0x18, "tx_bcast" }, - { 0x19, "tx_mcast" }, - { 0x1A, "tx_ucast" }, - { 0x1B, "tx_deferred" }, - { 0x1C, "tx_total_col" }, - { 0x1D, "tx_exc_col" }, - { 0x1E, "tx_single_col" }, - { 0x1F, "tx_mult_col" }, - { 0x80, "rx_total" }, - { 0x81, "tx_total" }, - { 0x82, "rx_discards" }, - { 0x83, "tx_discards" }, -}; - -struct ksz9477_stats_raw { - u64 rx_hi; - u64 rx_undersize; - u64 rx_fragments; - u64 rx_oversize; - u64 rx_jabbers; - u64 rx_symbol_err; - u64 rx_crc_err; - u64 rx_align_err; - u64 rx_mac_ctrl; - u64 rx_pause; - u64 rx_bcast; - u64 rx_mcast; - u64 rx_ucast; - u64 rx_64_or_less; - u64 rx_65_127; - u64 rx_128_255; - u64 rx_256_511; - u64 rx_512_1023; - u64 rx_1024_1522; - u64 rx_1523_2000; - u64 rx_2001; - u64 tx_hi; - u64 tx_late_col; - u64 tx_pause; - u64 tx_bcast; - u64 tx_mcast; - u64 tx_ucast; - u64 tx_deferred; - u64 tx_total_col; - u64 tx_exc_col; - u64 tx_single_col; - u64 tx_mult_col; - u64 rx_total; - u64 tx_total; - u64 rx_discards; - u64 tx_discards; -}; - -static void ksz9477_r_mib_stats64(struct ksz_device *dev, int port) -{ - struct rtnl_link_stats64 *stats; - struct ksz9477_stats_raw *raw; - struct ksz_port_mib *mib; - - mib = &dev->ports[port].mib; - stats = &mib->stats64; - raw = (struct ksz9477_stats_raw *)mib->counters; - - spin_lock(&mib->stats64_lock); - - stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast; - stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast; - - /* HW counters are counting bytes + FCS which is not acceptable - * for rtnl_link_stats64 interface - */ - stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN; - stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN; - - stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + - raw->rx_oversize; - - stats->rx_crc_errors = raw->rx_crc_err; - stats->rx_frame_errors = raw->rx_align_err; - stats->rx_dropped = raw->rx_discards; - stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + - stats->rx_frame_errors + stats->rx_dropped; - - stats->tx_window_errors = raw->tx_late_col; - stats->tx_fifo_errors = raw->tx_discards; - stats->tx_aborted_errors = raw->tx_exc_col; - stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + - stats->tx_aborted_errors; - - stats->multicast = raw->rx_mcast; - stats->collisions = raw->tx_total_col; - - spin_unlock(&mib->stats64_lock); -} - -static void ksz9477_get_stats64(struct dsa_switch *ds, int port, - struct rtnl_link_stats64 *s) -{ - struct ksz_device *dev = ds->priv; - struct ksz_port_mib *mib; - - mib = &dev->ports[port].mib; - - spin_lock(&mib->stats64_lock); - memcpy(s, &mib->stats64, sizeof(*s)); - spin_unlock(&mib->stats64_lock); -} - static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); @@ -194,7 +58,7 @@ static int ksz9477_change_mtu(struct dsa_switch *ds, int port, int mtu) /* Cache the per-port MTU setting */ dev->ports[port].max_frame = frame_size; - for (i = 0; i < dev->port_cnt; i++) + for (i = 0; i < dev->info->port_cnt; i++) max_frame = max(max_frame, dev->ports[i].max_frame); return regmap_update_bits(dev->regmap[1], REG_SW_MTU__2, @@ -381,7 +245,7 @@ static void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, static void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { - addr = ksz9477_mib_names[addr].index; + addr = dev->info->mib_names[addr].index; ksz9477_r_mib_cnt(dev, port, addr, cnt); } @@ -410,9 +274,6 @@ static void ksz9477_port_init_cnt(struct ksz_device *dev, int port) ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH); ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0); mutex_unlock(&mib->cnt_mutex); - - mib->cnt_ptr = 0; - memset(mib->counters, 0, dev->mib_cnt * sizeof(u64)); } static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds, @@ -494,20 +355,6 @@ static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg, return 0; } -static void ksz9477_get_strings(struct dsa_switch *ds, int port, - u32 stringset, uint8_t *buf) -{ - int i; - - if (stringset != ETH_SS_STATS) - return; - - for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) { - memcpy(buf + i * ETH_GSTRING_LEN, ksz9477_mib_names[i].string, - ETH_GSTRING_LEN); - } -} - static void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member) { @@ -517,38 +364,7 @@ static void ksz9477_cfg_port_member(struct ksz_device *dev, int port, static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - struct ksz_device *dev = ds->priv; - struct ksz_port *p = &dev->ports[port]; - u8 data; - - ksz_pread8(dev, port, P_STP_CTRL, &data); - data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); - - switch (state) { - case BR_STATE_DISABLED: - data |= PORT_LEARN_DISABLE; - break; - case BR_STATE_LISTENING: - data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); - break; - case BR_STATE_LEARNING: - data |= PORT_RX_ENABLE; - break; - case BR_STATE_FORWARDING: - data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); - break; - case BR_STATE_BLOCKING: - data |= PORT_LEARN_DISABLE; - break; - default: - dev_err(ds->dev, "invalid STP state: %d\n", state); - return; - } - - ksz_pwrite8(dev, port, P_STP_CTRL, data); - p->stp_state = state; - - ksz_update_port_member(dev, port); + ksz_port_stp_state_set(ds, port, state, P_STP_CTRL); } static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) @@ -559,7 +375,7 @@ static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) SW_FLUSH_OPTION_M << SW_FLUSH_OPTION_S, SW_FLUSH_OPTION_DYN_MAC << SW_FLUSH_OPTION_S); - if (port < dev->port_cnt) { + if (port < dev->info->port_cnt) { /* flush individual port */ ksz_pread8(dev, port, P_STP_CTRL, &data); if (!(data & PORT_LEARN_DISABLE)) @@ -881,7 +697,7 @@ static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, mutex_lock(&dev->alu_mutex); - for (index = 0; index < dev->num_statics; index++) { + for (index = 0; index < dev->info->num_statics; index++) { /* find empty slot first */ data = (index << ALU_STAT_INDEX_S) | ALU_STAT_READ | ALU_STAT_START; @@ -912,7 +728,7 @@ static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics) { + if (index == dev->info->num_statics) { err = -ENOSPC; goto exit; } @@ -957,7 +773,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, mutex_lock(&dev->alu_mutex); - for (index = 0; index < dev->num_statics; index++) { + for (index = 0; index < dev->info->num_statics; index++) { /* find empty slot first */ data = (index << ALU_STAT_INDEX_S) | ALU_STAT_READ | ALU_STAT_START; @@ -986,7 +802,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics) + if (index == dev->info->num_statics) goto exit; /* clear port */ @@ -1028,7 +844,7 @@ static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, * Check if any of the port is already set for sniffing * If yes, instruct the user to remove the previous entry & exit */ - for (p = 0; p < dev->port_cnt; p++) { + for (p = 0; p < dev->info->port_cnt; p++) { /* Skip the current sniffing port */ if (p == mirror->to_local_port) continue; @@ -1071,7 +887,7 @@ static void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, /* Check if any of the port is still referring to sniffer port */ - for (p = 0; p < dev->port_cnt; p++) { + for (p = 0; p < dev->info->port_cnt; p++) { ksz_pread8(dev, p, P_MIRROR_CTRL, &data); if ((data & (PORT_MIRROR_RX | PORT_MIRROR_TX))) { @@ -1281,6 +1097,15 @@ static void ksz9477_phy_errata_setup(struct ksz_device *dev, int port) ksz9477_port_mmd_write(dev, port, 0x1c, 0x20, 0xeeee); } +static void ksz9477_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + ksz_phylink_get_caps(ds, port, config); + + config->mac_capabilities = MAC_10 | MAC_100 | MAC_1000FD | + MAC_ASYM_PAUSE | MAC_SYM_PAUSE; +} + static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) { struct ksz_port *p = &dev->ports[port]; @@ -1319,7 +1144,7 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, false); - if (dev->phy_errata_9477) + if (dev->info->phy_errata_9477) ksz9477_phy_errata_setup(dev, port); } else { /* force flow control */ @@ -1384,8 +1209,9 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds) struct ksz_port *p; int i; - for (i = 0; i < dev->port_cnt; i++) { - if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) { + for (i = 0; i < dev->info->port_cnt; i++) { + if (dsa_is_cpu_port(ds, i) && + (dev->info->cpu_ports & (1 << i))) { phy_interface_t interface; const char *prev_msg; const char *prev_mode; @@ -1429,7 +1255,7 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds) } } - for (i = 0; i < dev->port_cnt; i++) { + for (i = 0; i < dev->info->port_cnt; i++) { if (i == dev->cpu_port) continue; p = &dev->ports[i]; @@ -1453,7 +1279,7 @@ static int ksz9477_setup(struct dsa_switch *ds) int ret = 0; dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), - dev->num_vlans, GFP_KERNEL); + dev->info->num_vlans, GFP_KERNEL); if (!dev->vlan_cache) return -ENOMEM; @@ -1505,8 +1331,9 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .phy_read = ksz9477_phy_read16, .phy_write = ksz9477_phy_write16, .phylink_mac_link_down = ksz_mac_link_down, + .phylink_get_caps = ksz9477_get_caps, .port_enable = ksz_enable_port, - .get_strings = ksz9477_get_strings, + .get_strings = ksz_get_strings, .get_ethtool_stats = ksz_get_ethtool_stats, .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, @@ -1523,7 +1350,7 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .port_mdb_del = ksz9477_port_mdb_del, .port_mirror_add = ksz9477_port_mirror_add, .port_mirror_del = ksz9477_port_mirror_del, - .get_stats64 = ksz9477_get_stats64, + .get_stats64 = ksz_get_stats64, .port_change_mtu = ksz9477_change_mtu, .port_max_mtu = ksz9477_max_mtu, }; @@ -1595,109 +1422,11 @@ static int ksz9477_switch_detect(struct ksz_device *dev) return 0; } -struct ksz_chip_data { - u32 chip_id; - const char *dev_name; - int num_vlans; - int num_alus; - int num_statics; - int cpu_ports; - int port_cnt; - bool phy_errata_9477; -}; - -static const struct ksz_chip_data ksz9477_switch_chips[] = { - { - .chip_id = 0x00947700, - .dev_name = "KSZ9477", - .num_vlans = 4096, - .num_alus = 4096, - .num_statics = 16, - .cpu_ports = 0x7F, /* can be configured as cpu port */ - .port_cnt = 7, /* total physical port count */ - .phy_errata_9477 = true, - }, - { - .chip_id = 0x00989700, - .dev_name = "KSZ9897", - .num_vlans = 4096, - .num_alus = 4096, - .num_statics = 16, - .cpu_ports = 0x7F, /* can be configured as cpu port */ - .port_cnt = 7, /* total physical port count */ - .phy_errata_9477 = true, - }, - { - .chip_id = 0x00989300, - .dev_name = "KSZ9893", - .num_vlans = 4096, - .num_alus = 4096, - .num_statics = 16, - .cpu_ports = 0x07, /* can be configured as cpu port */ - .port_cnt = 3, /* total port count */ - }, - { - .chip_id = 0x00956700, - .dev_name = "KSZ9567", - .num_vlans = 4096, - .num_alus = 4096, - .num_statics = 16, - .cpu_ports = 0x7F, /* can be configured as cpu port */ - .port_cnt = 7, /* total physical port count */ - .phy_errata_9477 = true, - }, -}; - static int ksz9477_switch_init(struct ksz_device *dev) { - int i; - dev->ds->ops = &ksz9477_switch_ops; - for (i = 0; i < ARRAY_SIZE(ksz9477_switch_chips); i++) { - const struct ksz_chip_data *chip = &ksz9477_switch_chips[i]; - - if (dev->chip_id == chip->chip_id) { - dev->name = chip->dev_name; - dev->num_vlans = chip->num_vlans; - dev->num_alus = chip->num_alus; - dev->num_statics = chip->num_statics; - dev->port_cnt = chip->port_cnt; - dev->cpu_ports = chip->cpu_ports; - dev->phy_errata_9477 = chip->phy_errata_9477; - - break; - } - } - - /* no switch found */ - if (!dev->port_cnt) - return -ENODEV; - - dev->port_mask = (1 << dev->port_cnt) - 1; - - dev->reg_mib_cnt = SWITCH_COUNTER_NUM; - dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM; - - dev->ports = devm_kzalloc(dev->dev, - dev->port_cnt * sizeof(struct ksz_port), - GFP_KERNEL); - if (!dev->ports) - return -ENOMEM; - for (i = 0; i < dev->port_cnt; i++) { - spin_lock_init(&dev->ports[i].mib.stats64_lock); - mutex_init(&dev->ports[i].mib.cnt_mutex); - dev->ports[i].mib.counters = - devm_kzalloc(dev->dev, - sizeof(u64) * - (TOTAL_SWITCH_COUNTER_NUM + 1), - GFP_KERNEL); - if (!dev->ports[i].mib.counters) - return -ENOMEM; - } - - /* set the real number of ports */ - dev->ds->num_ports = dev->port_cnt; + dev->port_mask = (1 << dev->info->port_cnt) - 1; return 0; } @@ -1714,7 +1443,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .port_setup = ksz9477_port_setup, .r_mib_cnt = ksz9477_r_mib_cnt, .r_mib_pkt = ksz9477_r_mib_pkt, - .r_mib_stat64 = ksz9477_r_mib_stats64, + .r_mib_stat64 = ksz_r_mib_stats64, .freeze_mib = ksz9477_freeze_mib, .port_init_cnt = ksz9477_port_init_cnt, .shutdown = ksz9477_reset_switch, diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index cbc0b20e7e1b..faa3163c86b0 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -87,12 +87,30 @@ static const struct i2c_device_id ksz9477_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id); static const struct of_device_id ksz9477_dt_ids[] = { - { .compatible = "microchip,ksz9477" }, - { .compatible = "microchip,ksz9897" }, - { .compatible = "microchip,ksz9893" }, - { .compatible = "microchip,ksz9563" }, - { .compatible = "microchip,ksz9567" }, - { .compatible = "microchip,ksz8563" }, + { + .compatible = "microchip,ksz9477", + .data = &ksz_switch_chips[KSZ9477] + }, + { + .compatible = "microchip,ksz9897", + .data = &ksz_switch_chips[KSZ9897] + }, + { + .compatible = "microchip,ksz9893", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz8563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9567", + .data = &ksz_switch_chips[KSZ9567] + }, {}, }; MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h index 0bd58467181f..7a2c8d4767af 100644 --- a/drivers/net/dsa/microchip/ksz9477_reg.h +++ b/drivers/net/dsa/microchip/ksz9477_reg.h @@ -1586,10 +1586,6 @@ #define REG_PORT_LUE_MSTP_STATE 0x0B04 -#define PORT_TX_ENABLE BIT(2) -#define PORT_RX_ENABLE BIT(1) -#define PORT_LEARN_DISABLE BIT(0) - /* C - PTP */ #define REG_PTP_PORT_RX_DELAY__2 0x0C00 diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index 87ca464dad32..1bc8b0cbe458 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -86,12 +86,30 @@ static void ksz9477_spi_shutdown(struct spi_device *spi) } static const struct of_device_id ksz9477_dt_ids[] = { - { .compatible = "microchip,ksz9477" }, - { .compatible = "microchip,ksz9897" }, - { .compatible = "microchip,ksz9893" }, - { .compatible = "microchip,ksz9563" }, - { .compatible = "microchip,ksz8563" }, - { .compatible = "microchip,ksz9567" }, + { + .compatible = "microchip,ksz9477", + .data = &ksz_switch_chips[KSZ9477] + }, + { + .compatible = "microchip,ksz9897", + .data = &ksz_switch_chips[KSZ9897] + }, + { + .compatible = "microchip,ksz9893", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz8563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9567", + .data = &ksz_switch_chips[KSZ9567] + }, {}, }; MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 8014b18d9391..9ca8c8d7740f 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -14,12 +14,524 @@ #include <linux/phy.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> +#include <linux/of_device.h> #include <linux/of_net.h> #include <net/dsa.h> #include <net/switchdev.h> #include "ksz_common.h" +#define MIB_COUNTER_NUM 0x20 + +struct ksz_stats_raw { + u64 rx_hi; + u64 rx_undersize; + u64 rx_fragments; + u64 rx_oversize; + u64 rx_jabbers; + u64 rx_symbol_err; + u64 rx_crc_err; + u64 rx_align_err; + u64 rx_mac_ctrl; + u64 rx_pause; + u64 rx_bcast; + u64 rx_mcast; + u64 rx_ucast; + u64 rx_64_or_less; + u64 rx_65_127; + u64 rx_128_255; + u64 rx_256_511; + u64 rx_512_1023; + u64 rx_1024_1522; + u64 rx_1523_2000; + u64 rx_2001; + u64 tx_hi; + u64 tx_late_col; + u64 tx_pause; + u64 tx_bcast; + u64 tx_mcast; + u64 tx_ucast; + u64 tx_deferred; + u64 tx_total_col; + u64 tx_exc_col; + u64 tx_single_col; + u64 tx_mult_col; + u64 rx_total; + u64 tx_total; + u64 rx_discards; + u64 tx_discards; +}; + +static const struct ksz_mib_names ksz88xx_mib_names[] = { + { 0x00, "rx" }, + { 0x01, "rx_hi" }, + { 0x02, "rx_undersize" }, + { 0x03, "rx_fragments" }, + { 0x04, "rx_oversize" }, + { 0x05, "rx_jabbers" }, + { 0x06, "rx_symbol_err" }, + { 0x07, "rx_crc_err" }, + { 0x08, "rx_align_err" }, + { 0x09, "rx_mac_ctrl" }, + { 0x0a, "rx_pause" }, + { 0x0b, "rx_bcast" }, + { 0x0c, "rx_mcast" }, + { 0x0d, "rx_ucast" }, + { 0x0e, "rx_64_or_less" }, + { 0x0f, "rx_65_127" }, + { 0x10, "rx_128_255" }, + { 0x11, "rx_256_511" }, + { 0x12, "rx_512_1023" }, + { 0x13, "rx_1024_1522" }, + { 0x14, "tx" }, + { 0x15, "tx_hi" }, + { 0x16, "tx_late_col" }, + { 0x17, "tx_pause" }, + { 0x18, "tx_bcast" }, + { 0x19, "tx_mcast" }, + { 0x1a, "tx_ucast" }, + { 0x1b, "tx_deferred" }, + { 0x1c, "tx_total_col" }, + { 0x1d, "tx_exc_col" }, + { 0x1e, "tx_single_col" }, + { 0x1f, "tx_mult_col" }, + { 0x100, "rx_discards" }, + { 0x101, "tx_discards" }, +}; + +static const struct ksz_mib_names ksz9477_mib_names[] = { + { 0x00, "rx_hi" }, + { 0x01, "rx_undersize" }, + { 0x02, "rx_fragments" }, + { 0x03, "rx_oversize" }, + { 0x04, "rx_jabbers" }, + { 0x05, "rx_symbol_err" }, + { 0x06, "rx_crc_err" }, + { 0x07, "rx_align_err" }, + { 0x08, "rx_mac_ctrl" }, + { 0x09, "rx_pause" }, + { 0x0A, "rx_bcast" }, + { 0x0B, "rx_mcast" }, + { 0x0C, "rx_ucast" }, + { 0x0D, "rx_64_or_less" }, + { 0x0E, "rx_65_127" }, + { 0x0F, "rx_128_255" }, + { 0x10, "rx_256_511" }, + { 0x11, "rx_512_1023" }, + { 0x12, "rx_1024_1522" }, + { 0x13, "rx_1523_2000" }, + { 0x14, "rx_2001" }, + { 0x15, "tx_hi" }, + { 0x16, "tx_late_col" }, + { 0x17, "tx_pause" }, + { 0x18, "tx_bcast" }, + { 0x19, "tx_mcast" }, + { 0x1A, "tx_ucast" }, + { 0x1B, "tx_deferred" }, + { 0x1C, "tx_total_col" }, + { 0x1D, "tx_exc_col" }, + { 0x1E, "tx_single_col" }, + { 0x1F, "tx_mult_col" }, + { 0x80, "rx_total" }, + { 0x81, "tx_total" }, + { 0x82, "rx_discards" }, + { 0x83, "tx_discards" }, +}; + +const struct ksz_chip_data ksz_switch_chips[] = { + [KSZ8795] = { + .chip_id = KSZ8795_CHIP_ID, + .dev_name = "KSZ8795", + .num_vlans = 4096, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .ksz87xx_eee_link_erratum = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, true, false}, + }, + + [KSZ8794] = { + /* WARNING + * ======= + * KSZ8794 is similar to KSZ8795, except the port map + * contains a gap between external and CPU ports, the + * port map is NOT continuous. The per-port register + * map is shifted accordingly too, i.e. registers at + * offset 0x40 are NOT used on KSZ8794 and they ARE + * used on KSZ8795 for external port 3. + * external cpu + * KSZ8794 0,1,2 4 + * KSZ8795 0,1,2,3 4 + * KSZ8765 0,1,2,3 4 + * port_cnt is configured as 5, even though it is 4 + */ + .chip_id = KSZ8794_CHIP_ID, + .dev_name = "KSZ8794", + .num_vlans = 4096, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .ksz87xx_eee_link_erratum = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, false, false}, + }, + + [KSZ8765] = { + .chip_id = KSZ8765_CHIP_ID, + .dev_name = "KSZ8765", + .num_vlans = 4096, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .ksz87xx_eee_link_erratum = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, true, false}, + }, + + [KSZ8830] = { + .chip_id = KSZ8830_CHIP_ID, + .dev_name = "KSZ8863/KSZ8873", + .num_vlans = 16, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x4, /* can be configured as cpu port */ + .port_cnt = 3, + .mib_names = ksz88xx_mib_names, + .mib_cnt = ARRAY_SIZE(ksz88xx_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .internal_phy = {true, true, false}, + }, + + [KSZ9477] = { + .chip_id = KSZ9477_CHIP_ID, + .dev_name = "KSZ9477", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x7F, /* can be configured as cpu port */ + .port_cnt = 7, /* total physical port count */ + .phy_errata_9477 = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, + false, true, false}, + .supports_rmii = {false, false, false, false, + false, true, false}, + .supports_rgmii = {false, false, false, false, + false, true, false}, + .internal_phy = {true, true, true, true, + true, false, false}, + }, + + [KSZ9897] = { + .chip_id = KSZ9897_CHIP_ID, + .dev_name = "KSZ9897", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x7F, /* can be configured as cpu port */ + .port_cnt = 7, /* total physical port count */ + .phy_errata_9477 = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, + false, true, true}, + .supports_rmii = {false, false, false, false, + false, true, true}, + .supports_rgmii = {false, false, false, false, + false, true, true}, + .internal_phy = {true, true, true, true, + true, false, false}, + }, + + [KSZ9893] = { + .chip_id = KSZ9893_CHIP_ID, + .dev_name = "KSZ9893", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x07, /* can be configured as cpu port */ + .port_cnt = 3, /* total port count */ + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .supports_rgmii = {false, false, true}, + .internal_phy = {true, true, false}, + }, + + [KSZ9567] = { + .chip_id = KSZ9567_CHIP_ID, + .dev_name = "KSZ9567", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x7F, /* can be configured as cpu port */ + .port_cnt = 7, /* total physical port count */ + .phy_errata_9477 = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, + false, true, true}, + .supports_rmii = {false, false, false, false, + false, true, true}, + .supports_rgmii = {false, false, false, false, + false, true, true}, + .internal_phy = {true, true, true, true, + true, false, false}, + }, + + [LAN9370] = { + .chip_id = LAN9370_CHIP_ID, + .dev_name = "LAN9370", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total physical port count */ + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, true, false}, + }, + + [LAN9371] = { + .chip_id = LAN9371_CHIP_ID, + .dev_name = "LAN9371", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x30, /* can be configured as cpu port */ + .port_cnt = 6, /* total physical port count */ + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, true, true}, + .supports_rmii = {false, false, false, false, true, true}, + .supports_rgmii = {false, false, false, false, true, true}, + .internal_phy = {true, true, true, true, false, false}, + }, + + [LAN9372] = { + .chip_id = LAN9372_CHIP_ID, + .dev_name = "LAN9372", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x30, /* can be configured as cpu port */ + .port_cnt = 8, /* total physical port count */ + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, + true, true, false, false}, + .supports_rmii = {false, false, false, false, + true, true, false, false}, + .supports_rgmii = {false, false, false, false, + true, true, false, false}, + .internal_phy = {true, true, true, true, + false, false, true, true}, + }, + + [LAN9373] = { + .chip_id = LAN9373_CHIP_ID, + .dev_name = "LAN9373", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x38, /* can be configured as cpu port */ + .port_cnt = 5, /* total physical port count */ + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, + true, true, false, false}, + .supports_rmii = {false, false, false, false, + true, true, false, false}, + .supports_rgmii = {false, false, false, false, + true, true, false, false}, + .internal_phy = {true, true, true, false, + false, false, true, true}, + }, + + [LAN9374] = { + .chip_id = LAN9374_CHIP_ID, + .dev_name = "LAN9374", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x30, /* can be configured as cpu port */ + .port_cnt = 8, /* total physical port count */ + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .supports_mii = {false, false, false, false, + true, true, false, false}, + .supports_rmii = {false, false, false, false, + true, true, false, false}, + .supports_rgmii = {false, false, false, false, + true, true, false, false}, + .internal_phy = {true, true, true, true, + false, false, true, true}, + }, +}; +EXPORT_SYMBOL_GPL(ksz_switch_chips); + +static const struct ksz_chip_data *ksz_lookup_info(unsigned int prod_num) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksz_switch_chips); i++) { + const struct ksz_chip_data *chip = &ksz_switch_chips[i]; + + if (chip->chip_id == prod_num) + return chip; + } + + return NULL; +} + +static int ksz_check_device_id(struct ksz_device *dev) +{ + const struct ksz_chip_data *dt_chip_data; + + dt_chip_data = of_device_get_match_data(dev->dev); + + /* Check for Device Tree and Chip ID */ + if (dt_chip_data->chip_id != dev->chip_id) { + dev_err(dev->dev, + "Device tree specifies chip %s but found %s, please fix it!\n", + dt_chip_data->dev_name, dev->info->dev_name); + return -ENODEV; + } + + return 0; +} + +void ksz_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct ksz_device *dev = ds->priv; + + config->legacy_pre_march2020 = false; + + if (dev->info->supports_mii[port]) + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + + if (dev->info->supports_rmii[port]) + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + + if (dev->info->supports_rgmii[port]) + phy_interface_set_rgmii(config->supported_interfaces); + + if (dev->info->internal_phy[port]) + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); +} +EXPORT_SYMBOL_GPL(ksz_phylink_get_caps); + +void ksz_r_mib_stats64(struct ksz_device *dev, int port) +{ + struct rtnl_link_stats64 *stats; + struct ksz_stats_raw *raw; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + stats = &mib->stats64; + raw = (struct ksz_stats_raw *)mib->counters; + + spin_lock(&mib->stats64_lock); + + stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast; + stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast; + + /* HW counters are counting bytes + FCS which is not acceptable + * for rtnl_link_stats64 interface + */ + stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN; + stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN; + + stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + + raw->rx_oversize; + + stats->rx_crc_errors = raw->rx_crc_err; + stats->rx_frame_errors = raw->rx_align_err; + stats->rx_dropped = raw->rx_discards; + stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + + stats->rx_frame_errors + stats->rx_dropped; + + stats->tx_window_errors = raw->tx_late_col; + stats->tx_fifo_errors = raw->tx_discards; + stats->tx_aborted_errors = raw->tx_exc_col; + stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + + stats->tx_aborted_errors; + + stats->multicast = raw->rx_mcast; + stats->collisions = raw->tx_total_col; + + spin_unlock(&mib->stats64_lock); +} +EXPORT_SYMBOL_GPL(ksz_r_mib_stats64); + +void ksz_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + + spin_lock(&mib->stats64_lock); + memcpy(s, &mib->stats64, sizeof(*s)); + spin_unlock(&mib->stats64_lock); +} +EXPORT_SYMBOL_GPL(ksz_get_stats64); + +void ksz_get_strings(struct dsa_switch *ds, int port, + u32 stringset, uint8_t *buf) +{ + struct ksz_device *dev = ds->priv; + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < dev->info->mib_cnt; i++) { + memcpy(buf + i * ETH_GSTRING_LEN, + dev->info->mib_names[i].string, ETH_GSTRING_LEN); + } +} +EXPORT_SYMBOL_GPL(ksz_get_strings); + void ksz_update_port_member(struct ksz_device *dev, int port) { struct ksz_port *p = &dev->ports[port]; @@ -85,17 +597,17 @@ static void port_r_cnt(struct ksz_device *dev, int port) u64 *dropped; /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->reg_mib_cnt) { + while (mib->cnt_ptr < dev->info->reg_mib_cnt) { dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; } /* last one in storage */ - dropped = &mib->counters[dev->mib_cnt]; + dropped = &mib->counters[dev->info->mib_cnt]; /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->mib_cnt) { + while (mib->cnt_ptr < dev->info->mib_cnt) { dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, dropped, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; @@ -111,7 +623,7 @@ static void ksz_mib_read_work(struct work_struct *work) struct ksz_port *p; int i; - for (i = 0; i < dev->port_cnt; i++) { + for (i = 0; i < dev->info->port_cnt; i++) { if (dsa_is_unused_port(dev->ds, i)) continue; @@ -126,7 +638,7 @@ static void ksz_mib_read_work(struct work_struct *work) const struct dsa_port *dp = dsa_to_port(dev->ds, i); if (!netif_carrier_ok(dp->slave)) - mib->cnt_ptr = dev->reg_mib_cnt; + mib->cnt_ptr = dev->info->reg_mib_cnt; } port_r_cnt(dev, i); p->read = false; @@ -146,8 +658,14 @@ void ksz_init_mib_timer(struct ksz_device *dev) INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); - for (i = 0; i < dev->port_cnt; i++) + for (i = 0; i < dev->info->port_cnt; i++) { + struct ksz_port_mib *mib = &dev->ports[i].mib; + dev->dev_ops->port_init_cnt(dev, i); + + mib->cnt_ptr = 0; + memset(mib->counters, 0, dev->info->mib_cnt * sizeof(u64)); + } } EXPORT_SYMBOL_GPL(ksz_init_mib_timer); @@ -193,7 +711,7 @@ int ksz_sset_count(struct dsa_switch *ds, int port, int sset) if (sset != ETH_SS_STATS) return 0; - return dev->mib_cnt; + return dev->info->mib_cnt; } EXPORT_SYMBOL_GPL(ksz_sset_count); @@ -208,9 +726,9 @@ void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf) /* Only read dropped counters if no link. */ if (!netif_carrier_ok(dp->slave)) - mib->cnt_ptr = dev->reg_mib_cnt; + mib->cnt_ptr = dev->info->reg_mib_cnt; port_r_cnt(dev, port); - memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64)); + memcpy(buf, mib->counters, dev->info->mib_cnt * sizeof(u64)); mutex_unlock(&mib->cnt_mutex); } EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats); @@ -286,7 +804,7 @@ int ksz_port_mdb_add(struct dsa_switch *ds, int port, int empty = 0; alu.port_forward = 0; - for (index = 0; index < dev->num_statics; index++) { + for (index = 0; index < dev->info->num_statics; index++) { if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { /* Found one already in static MAC table. */ if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && @@ -299,11 +817,11 @@ int ksz_port_mdb_add(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics && !empty) + if (index == dev->info->num_statics && !empty) return -ENOSPC; /* add entry */ - if (index == dev->num_statics) { + if (index == dev->info->num_statics) { index = empty - 1; memset(&alu, 0, sizeof(alu)); memcpy(alu.mac, mdb->addr, ETH_ALEN); @@ -330,7 +848,7 @@ int ksz_port_mdb_del(struct dsa_switch *ds, int port, struct alu_struct alu; int index; - for (index = 0; index < dev->num_statics; index++) { + for (index = 0; index < dev->info->num_statics; index++) { if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { /* Found one already in static MAC table. */ if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && @@ -340,7 +858,7 @@ int ksz_port_mdb_del(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics) + if (index == dev->info->num_statics) goto exit; /* clear port */ @@ -372,6 +890,46 @@ int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) } EXPORT_SYMBOL_GPL(ksz_enable_port); +void ksz_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state, int reg) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port *p; + u8 data; + + ksz_pread8(dev, port, reg, &data); + data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); + + switch (state) { + case BR_STATE_DISABLED: + data |= PORT_LEARN_DISABLE; + break; + case BR_STATE_LISTENING: + data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); + break; + case BR_STATE_LEARNING: + data |= PORT_RX_ENABLE; + break; + case BR_STATE_FORWARDING: + data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); + break; + case BR_STATE_BLOCKING: + data |= PORT_LEARN_DISABLE; + break; + default: + dev_err(ds->dev, "invalid STP state: %d\n", state); + return; + } + + ksz_pwrite8(dev, port, reg, data); + + p = &dev->ports[port]; + p->stp_state = state; + + ksz_update_port_member(dev, port); +} +EXPORT_SYMBOL_GPL(ksz_port_stp_state_set); + struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) { struct dsa_switch *ds; @@ -401,10 +959,12 @@ EXPORT_SYMBOL(ksz_switch_alloc); int ksz_switch_register(struct ksz_device *dev, const struct ksz_dev_ops *ops) { + const struct ksz_chip_data *info; struct device_node *port, *ports; phy_interface_t interface; unsigned int port_num; int ret; + int i; if (dev->pdata) dev->chip_id = dev->pdata->chip_id; @@ -431,14 +991,45 @@ int ksz_switch_register(struct ksz_device *dev, if (dev->dev_ops->detect(dev)) return -EINVAL; + info = ksz_lookup_info(dev->chip_id); + if (!info) + return -ENODEV; + + /* Update the compatible info with the probed one */ + dev->info = info; + + ret = ksz_check_device_id(dev); + if (ret) + return ret; + ret = dev->dev_ops->init(dev); if (ret) return ret; + dev->ports = devm_kzalloc(dev->dev, + dev->info->port_cnt * sizeof(struct ksz_port), + GFP_KERNEL); + if (!dev->ports) + return -ENOMEM; + + for (i = 0; i < dev->info->port_cnt; i++) { + spin_lock_init(&dev->ports[i].mib.stats64_lock); + mutex_init(&dev->ports[i].mib.cnt_mutex); + dev->ports[i].mib.counters = + devm_kzalloc(dev->dev, + sizeof(u64) * (dev->info->mib_cnt + 1), + GFP_KERNEL); + if (!dev->ports[i].mib.counters) + return -ENOMEM; + } + + /* set the real number of ports */ + dev->ds->num_ports = dev->info->port_cnt; + /* Host port interface will be self detected, or specifically set in * device tree. */ - for (port_num = 0; port_num < dev->port_cnt; ++port_num) + for (port_num = 0; port_num < dev->info->port_cnt; ++port_num) dev->ports[port_num].interface = PHY_INTERFACE_MODE_NA; if (dev->dev->of_node) { ret = of_get_phy_mode(dev->dev->of_node, &interface); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 485d4a948c38..8500eaedad67 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -14,6 +14,8 @@ #include <linux/regmap.h> #include <net/dsa.h> +#define KSZ_MAX_NUM_PORTS 8 + struct vlan_table { u32 table[3]; }; @@ -26,6 +28,30 @@ struct ksz_port_mib { struct spinlock stats64_lock; }; +struct ksz_mib_names { + int index; + char string[ETH_GSTRING_LEN]; +}; + +struct ksz_chip_data { + u32 chip_id; + const char *dev_name; + int num_vlans; + int num_alus; + int num_statics; + int cpu_ports; + int port_cnt; + bool phy_errata_9477; + bool ksz87xx_eee_link_erratum; + const struct ksz_mib_names *mib_names; + int mib_cnt; + u8 reg_mib_cnt; + bool supports_mii[KSZ_MAX_NUM_PORTS]; + bool supports_rmii[KSZ_MAX_NUM_PORTS]; + bool supports_rgmii[KSZ_MAX_NUM_PORTS]; + bool internal_phy[KSZ_MAX_NUM_PORTS]; +}; + struct ksz_port { bool remove_tag; /* Remove Tag flag set, for ksz8795 only */ int stp_state; @@ -47,7 +73,7 @@ struct ksz_port { struct ksz_device { struct dsa_switch *ds; struct ksz_platform_data *pdata; - const char *name; + const struct ksz_chip_data *info; struct mutex dev_mutex; /* device access */ struct mutex regmap_mutex; /* regmap access */ @@ -64,20 +90,9 @@ struct ksz_device { /* chip specific data */ u32 chip_id; - int num_vlans; - int num_alus; - int num_statics; int cpu_port; /* port connected to CPU */ - int cpu_ports; /* port bitmap can be cpu port */ int phy_port_cnt; - int port_cnt; - u8 reg_mib_cnt; - int mib_cnt; - const struct mib_names *mib_names; phy_interface_t compat_interface; - u32 regs_size; - bool phy_errata_9477; - bool ksz87xx_eee_link_erratum; bool synclko_125; bool synclko_disable; @@ -89,11 +104,42 @@ struct ksz_device { u16 mirror_rx; u16 mirror_tx; u32 features; /* chip specific features */ - u32 overrides; /* chip functions set by user */ - u16 host_mask; u16 port_mask; }; +/* List of supported models */ +enum ksz_model { + KSZ8795, + KSZ8794, + KSZ8765, + KSZ8830, + KSZ9477, + KSZ9897, + KSZ9893, + KSZ9567, + LAN9370, + LAN9371, + LAN9372, + LAN9373, + LAN9374, +}; + +enum ksz_chip_id { + KSZ8795_CHIP_ID = 0x8795, + KSZ8794_CHIP_ID = 0x8794, + KSZ8765_CHIP_ID = 0x8765, + KSZ8830_CHIP_ID = 0x8830, + KSZ9477_CHIP_ID = 0x00947700, + KSZ9897_CHIP_ID = 0x00989700, + KSZ9893_CHIP_ID = 0x00989300, + KSZ9567_CHIP_ID = 0x00956700, + LAN9370_CHIP_ID = 0x00937000, + LAN9371_CHIP_ID = 0x00937100, + LAN9372_CHIP_ID = 0x00937200, + LAN9373_CHIP_ID = 0x00937300, + LAN9374_CHIP_ID = 0x00937400, +}; + struct alu_struct { /* entry 1 */ u8 is_static:1; @@ -151,6 +197,12 @@ int ksz9477_switch_register(struct ksz_device *dev); void ksz_update_port_member(struct ksz_device *dev, int port); void ksz_init_mib_timer(struct ksz_device *dev); +void ksz_r_mib_stats64(struct ksz_device *dev, int port); +void ksz_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s); +void ksz_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config); +extern const struct ksz_chip_data ksz_switch_chips[]; /* Common DSA access functions */ @@ -165,6 +217,8 @@ int ksz_port_bridge_join(struct dsa_switch *ds, int port, struct netlink_ext_ack *extack); void ksz_port_bridge_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge); +void ksz_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state, int reg); void ksz_port_fast_age(struct dsa_switch *ds, int port); int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); @@ -175,6 +229,8 @@ int ksz_port_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); +void ksz_get_strings(struct dsa_switch *ds, int port, + u32 stringset, uint8_t *buf); /* Common register access functions */ @@ -292,6 +348,11 @@ static inline void ksz_regmap_unlock(void *__mtx) mutex_unlock(mtx); } +/* STP State Defines */ +#define PORT_TX_ENABLE BIT(2) +#define PORT_RX_ENABLE BIT(1) +#define PORT_LEARN_DISABLE BIT(0) + /* Regmap tables generation */ #define KSZ_SPI_OP_RD 3 #define KSZ_SPI_OP_WR 2 diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index fe3cb26f4287..2b02d823d497 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -24,6 +24,11 @@ #include "mt7530.h" +static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mt753x_pcs, pcs); +} + /* String, offset, and register size in bytes if different from 4 bytes */ static const struct mt7530_mib_desc mt7530_mib[] = { MIB_DESC(1, 0x00, "TxDrop"), @@ -2390,35 +2395,30 @@ mt7531_setup(struct dsa_switch *ds) return 0; } -static bool -mt7530_phy_mode_supported(struct dsa_switch *ds, int port, - const struct phylink_link_state *state) +static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - struct mt7530_priv *priv = ds->priv; - switch (port) { case 0 ... 4: /* Internal phy */ - if (state->interface != PHY_INTERFACE_MODE_GMII) - return false; + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); break; + case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ - if (!phy_interface_mode_is_rgmii(state->interface) && - state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_GMII) - return false; + phy_interface_set_rgmii(config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_MII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); break; + case 6: /* 1st cpu port */ - if (state->interface != PHY_INTERFACE_MODE_RGMII && - state->interface != PHY_INTERFACE_MODE_TRGMII) - return false; + __set_bit(PHY_INTERFACE_MODE_RGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_TRGMII, + config->supported_interfaces); break; - default: - dev_err(priv->dev, "%s: unsupported port: %i\n", __func__, - port); - return false; } - - return true; } static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) @@ -2426,42 +2426,35 @@ static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); } -static bool -mt7531_phy_mode_supported(struct dsa_switch *ds, int port, - const struct phylink_link_state *state) +static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { struct mt7530_priv *priv = ds->priv; switch (port) { case 0 ... 4: /* Internal phy */ - if (state->interface != PHY_INTERFACE_MODE_GMII) - return false; + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); break; + case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ - if (mt7531_is_rgmii_port(priv, port)) - return phy_interface_mode_is_rgmii(state->interface); + if (mt7531_is_rgmii_port(priv, port)) { + phy_interface_set_rgmii(config->supported_interfaces); + break; + } fallthrough; + case 6: /* 1st cpu port supports sgmii/8023z only */ - if (state->interface != PHY_INTERFACE_MODE_SGMII && - !phy_interface_mode_is_8023z(state->interface)) - return false; + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + + config->mac_capabilities |= MAC_2500FD; break; - default: - dev_err(priv->dev, "%s: unsupported port: %i\n", __func__, - port); - return false; } - - return true; -} - -static bool -mt753x_phy_mode_supported(struct dsa_switch *ds, int port, - const struct phylink_link_state *state) -{ - struct mt7530_priv *priv = ds->priv; - - return priv->info->phy_mode_supported(ds, port, state); } static int @@ -2534,30 +2527,11 @@ static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port, return 0; } -static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port, - unsigned long *supported) -{ - /* Port5 supports ethier RGMII or SGMII. - * Port6 supports SGMII only. - */ - switch (port) { - case 5: - if (mt7531_is_rgmii_port(priv, port)) - break; - fallthrough; - case 6: - phylink_set(supported, 1000baseX_Full); - phylink_set(supported, 2500baseX_Full); - phylink_set(supported, 2500baseT_Full); - } -} - -static void -mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port, - unsigned int mode, phy_interface_t interface, - int speed, int duplex) +static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) { - struct mt7530_priv *priv = ds->priv; + struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; + int port = pcs_to_mt753x_pcs(pcs)->port; unsigned int val; /* For adjusting speed and duplex of SGMII force mode. */ @@ -2583,6 +2557,9 @@ mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port, /* MT7531 SGMII 1G force mode can only work in full duplex mode, * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. + * + * The speed check is unnecessary as the MAC capabilities apply + * this restriction. --rmk */ if ((speed == SPEED_10 || speed == SPEED_100) && duplex != DUPLEX_FULL) @@ -2658,9 +2635,10 @@ static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port, return 0; } -static void mt7531_sgmii_restart_an(struct dsa_switch *ds, int port) +static void mt7531_pcs_an_restart(struct phylink_pcs *pcs) { - struct mt7530_priv *priv = ds->priv; + struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; + int port = pcs_to_mt753x_pcs(pcs)->port; u32 val; /* Only restart AN when AN is enabled */ @@ -2717,6 +2695,24 @@ mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode, return priv->info->mac_port_config(ds, port, mode, state->interface); } +static struct phylink_pcs * +mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port, + phy_interface_t interface) +{ + struct mt7530_priv *priv = ds->priv; + + switch (interface) { + case PHY_INTERFACE_MODE_TRGMII: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return &priv->pcs[port].pcs; + + default: + return NULL; + } +} + static void mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) @@ -2724,9 +2720,6 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, struct mt7530_priv *priv = ds->priv; u32 mcr_cur, mcr_new; - if (!mt753x_phy_mode_supported(ds, port, state)) - goto unsupported; - switch (port) { case 0 ... 4: /* Internal phy */ if (state->interface != PHY_INTERFACE_MODE_GMII) @@ -2781,17 +2774,6 @@ unsupported: mt7530_write(priv, MT7530_PMCR_P(port), mcr_new); } -static void -mt753x_phylink_mac_an_restart(struct dsa_switch *ds, int port) -{ - struct mt7530_priv *priv = ds->priv; - - if (!priv->info->mac_pcs_an_restart) - return; - - priv->info->mac_pcs_an_restart(ds, port); -} - static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) @@ -2801,16 +2783,13 @@ static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port, mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); } -static void mt753x_mac_pcs_link_up(struct dsa_switch *ds, int port, - unsigned int mode, phy_interface_t interface, - int speed, int duplex) +static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex) { - struct mt7530_priv *priv = ds->priv; - - if (!priv->info->mac_pcs_link_up) - return; - - priv->info->mac_pcs_link_up(ds, port, mode, interface, speed, duplex); + if (pcs->ops->pcs_link_up) + pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex); } static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, @@ -2823,8 +2802,6 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, struct mt7530_priv *priv = ds->priv; u32 mcr; - mt753x_mac_pcs_link_up(ds, port, mode, interface, speed, duplex); - mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; /* MT753x MAC works in 1G full duplex mode for all up-clocked @@ -2904,81 +2881,51 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port) return ret; mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPU_PORT_SETTING(priv->id)); + mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED, + interface, speed, DUPLEX_FULL); mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, speed, DUPLEX_FULL, true, true); return 0; } -static void -mt7530_mac_port_validate(struct dsa_switch *ds, int port, - unsigned long *supported) +static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - if (port == 5) - phylink_set(supported, 1000baseX_Full); -} - -static void mt7531_mac_port_validate(struct dsa_switch *ds, int port, - unsigned long *supported) -{ - struct mt7530_priv *priv = ds->priv; - - mt7531_sgmii_validate(priv, port, supported); -} - -static void -mt753x_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct mt7530_priv *priv = ds->priv; - if (state->interface != PHY_INTERFACE_MODE_NA && - !mt753x_phy_mode_supported(ds, port, state)) { - linkmode_zero(supported); - return; - } - - phylink_set_port_modes(mask); - - if (state->interface != PHY_INTERFACE_MODE_TRGMII && - !phy_interface_mode_is_8023z(state->interface)) { - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, Autoneg); - } - - /* This switch only supports 1G full-duplex. */ - if (state->interface != PHY_INTERFACE_MODE_MII) - phylink_set(mask, 1000baseT_Full); + /* This switch only supports full-duplex at 1Gbps */ + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; - priv->info->mac_port_validate(ds, port, mask); + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. + */ + config->legacy_pre_march2020 = false; - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); + priv->info->mac_port_get_caps(ds, port, config); +} - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); +static int mt753x_pcs_validate(struct phylink_pcs *pcs, + unsigned long *supported, + const struct phylink_link_state *state) +{ + /* Autonegotiation is not supported in TRGMII nor 802.3z modes */ + if (state->interface == PHY_INTERFACE_MODE_TRGMII || + phy_interface_mode_is_8023z(state->interface)) + phylink_clear(supported, Autoneg); - /* We can only operate at 2500BaseX or 1000BaseX. If requested - * to advertise both, only report advertising at 2500BaseX. - */ - phylink_helper_basex_speed(state); + return 0; } -static int -mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) +static void mt7530_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) { - struct mt7530_priv *priv = ds->priv; + struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; + int port = pcs_to_mt753x_pcs(pcs)->port; u32 pmsr; - if (port < 0 || port >= MT7530_NUM_PORTS) - return -EINVAL; - pmsr = mt7530_read(priv, MT7530_PMSR_P(port)); state->link = (pmsr & PMSR_LINK); @@ -3005,8 +2952,6 @@ mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port, state->pause |= MLO_PAUSE_RX; if (pmsr & PMSR_TX_FC) state->pause |= MLO_PAUSE_TX; - - return 1; } static int @@ -3048,33 +2993,59 @@ mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port, return 0; } -static int -mt7531_phylink_mac_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) +static void mt7531_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) { - struct mt7530_priv *priv = ds->priv; + struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; + int port = pcs_to_mt753x_pcs(pcs)->port; if (state->interface == PHY_INTERFACE_MODE_SGMII) - return mt7531_sgmii_pcs_get_state_an(priv, port, state); - - return -EOPNOTSUPP; + mt7531_sgmii_pcs_get_state_an(priv, port, state); + else + state->link = false; } -static int -mt753x_phylink_mac_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) +static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) { - struct mt7530_priv *priv = ds->priv; + return 0; +} - return priv->info->mac_port_get_state(ds, port, state); +static void mt7530_pcs_an_restart(struct phylink_pcs *pcs) +{ } +static const struct phylink_pcs_ops mt7530_pcs_ops = { + .pcs_validate = mt753x_pcs_validate, + .pcs_get_state = mt7530_pcs_get_state, + .pcs_config = mt753x_pcs_config, + .pcs_an_restart = mt7530_pcs_an_restart, +}; + +static const struct phylink_pcs_ops mt7531_pcs_ops = { + .pcs_validate = mt753x_pcs_validate, + .pcs_get_state = mt7531_pcs_get_state, + .pcs_config = mt753x_pcs_config, + .pcs_an_restart = mt7531_pcs_an_restart, + .pcs_link_up = mt7531_pcs_link_up, +}; + static int mt753x_setup(struct dsa_switch *ds) { struct mt7530_priv *priv = ds->priv; - int ret = priv->info->sw_setup(ds); + int i, ret; + + /* Initialise the PCS devices */ + for (i = 0; i < priv->ds->num_ports; i++) { + priv->pcs[i].pcs.ops = priv->info->pcs_ops; + priv->pcs[i].priv = priv; + priv->pcs[i].port = i; + } + ret = priv->info->sw_setup(ds); if (ret) return ret; @@ -3145,10 +3116,9 @@ static const struct dsa_switch_ops mt7530_switch_ops = { .port_vlan_del = mt7530_port_vlan_del, .port_mirror_add = mt753x_port_mirror_add, .port_mirror_del = mt753x_port_mirror_del, - .phylink_validate = mt753x_phylink_validate, - .phylink_mac_link_state = mt753x_phylink_mac_link_state, + .phylink_get_caps = mt753x_phylink_get_caps, + .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs, .phylink_mac_config = mt753x_phylink_mac_config, - .phylink_mac_an_restart = mt753x_phylink_mac_an_restart, .phylink_mac_link_down = mt753x_phylink_mac_link_down, .phylink_mac_link_up = mt753x_phylink_mac_link_up, .get_mac_eee = mt753x_get_mac_eee, @@ -3158,39 +3128,34 @@ static const struct dsa_switch_ops mt7530_switch_ops = { static const struct mt753x_info mt753x_table[] = { [ID_MT7621] = { .id = ID_MT7621, + .pcs_ops = &mt7530_pcs_ops, .sw_setup = mt7530_setup, .phy_read = mt7530_phy_read, .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, - .phy_mode_supported = mt7530_phy_mode_supported, - .mac_port_validate = mt7530_mac_port_validate, - .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_get_caps = mt7530_mac_port_get_caps, .mac_port_config = mt7530_mac_config, }, [ID_MT7530] = { .id = ID_MT7530, + .pcs_ops = &mt7530_pcs_ops, .sw_setup = mt7530_setup, .phy_read = mt7530_phy_read, .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, - .phy_mode_supported = mt7530_phy_mode_supported, - .mac_port_validate = mt7530_mac_port_validate, - .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_get_caps = mt7530_mac_port_get_caps, .mac_port_config = mt7530_mac_config, }, [ID_MT7531] = { .id = ID_MT7531, + .pcs_ops = &mt7531_pcs_ops, .sw_setup = mt7531_setup, .phy_read = mt7531_ind_phy_read, .phy_write = mt7531_ind_phy_write, .pad_setup = mt7531_pad_setup, .cpu_port_config = mt7531_cpu_port_config, - .phy_mode_supported = mt7531_phy_mode_supported, - .mac_port_validate = mt7531_mac_port_validate, - .mac_port_get_state = mt7531_phylink_mac_link_state, + .mac_port_get_caps = mt7531_mac_port_get_caps, .mac_port_config = mt7531_mac_config, - .mac_pcs_an_restart = mt7531_sgmii_restart_an, - .mac_pcs_link_up = mt7531_sgmii_link_up_force, }, }; @@ -3247,9 +3212,8 @@ mt7530_probe(struct mdio_device *mdiodev) */ if (!priv->info->sw_setup || !priv->info->pad_setup || !priv->info->phy_read || !priv->info->phy_write || - !priv->info->phy_mode_supported || - !priv->info->mac_port_validate || - !priv->info->mac_port_get_state || !priv->info->mac_port_config) + !priv->info->mac_port_get_caps || + !priv->info->mac_port_config) return -EINVAL; priv->id = priv->info->id; diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 91508e2feef9..71e36b69b96d 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -741,6 +741,12 @@ static const char *p5_intf_modes(unsigned int p5_interface) struct mt7530_priv; +struct mt753x_pcs { + struct phylink_pcs pcs; + struct mt7530_priv *priv; + int port; +}; + /* struct mt753x_info - This is the main data structure for holding the specific * part for each supported device * @sw_setup: Holding the handler to a device initialization @@ -752,36 +758,27 @@ struct mt7530_priv; * port * @mac_port_validate: Holding the way to set addition validate type for a * certan MAC port - * @mac_port_get_state: Holding the way getting the MAC/PCS state for a certain - * MAC port * @mac_port_config: Holding the way setting up the PHY attribute to a * certain MAC port - * @mac_pcs_an_restart Holding the way restarting PCS autonegotiation for a - * certain MAC port - * @mac_pcs_link_up: Holding the way setting up the PHY attribute to the pcs - * of the certain MAC port */ struct mt753x_info { enum mt753x_id id; + const struct phylink_pcs_ops *pcs_ops; + int (*sw_setup)(struct dsa_switch *ds); int (*phy_read)(struct mt7530_priv *priv, int port, int regnum); int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface); int (*cpu_port_config)(struct dsa_switch *ds, int port); - bool (*phy_mode_supported)(struct dsa_switch *ds, int port, - const struct phylink_link_state *state); + void (*mac_port_get_caps)(struct dsa_switch *ds, int port, + struct phylink_config *config); void (*mac_port_validate)(struct dsa_switch *ds, int port, + phy_interface_t interface, unsigned long *supported); - int (*mac_port_get_state)(struct dsa_switch *ds, int port, - struct phylink_link_state *state); int (*mac_port_config)(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface); - void (*mac_pcs_an_restart)(struct dsa_switch *ds, int port); - void (*mac_pcs_link_up)(struct dsa_switch *ds, int port, - unsigned int mode, phy_interface_t interface, - int speed, int duplex); }; /* struct mt7530_priv - This is the main data structure for holding the state @@ -823,6 +820,7 @@ struct mt7530_priv { u8 mirror_tx; struct mt7530_port ports[MT7530_NUM_PORTS]; + struct mt753x_pcs pcs[MT7530_NUM_PORTS]; /* protect among processes for registers access*/ struct mutex reg_mutex; int irq; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 64f4fdd02902..5d2c57a7c708 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -6276,6 +6276,32 @@ static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) return 0; } +static int mv88e6xxx_single_chip_detect(struct mv88e6xxx_chip *chip, + struct mdio_device *mdiodev) +{ + int err; + + /* dual_chip takes precedence over single/multi-chip modes */ + if (chip->info->dual_chip) + return -EINVAL; + + /* If the mdio addr is 16 indicating the first port address of a switch + * (e.g. mv88e6*41) in single chip addressing mode the device may be + * configured in single chip addressing mode. Setup the smi access as + * single chip addressing mode and attempt to detect the model of the + * switch, if this fails the device is not configured in single chip + * addressing mode. + */ + if (mdiodev->addr != 16) + return -EINVAL; + + err = mv88e6xxx_smi_init(chip, mdiodev->bus, 0); + if (err) + return err; + + return mv88e6xxx_detect(chip); +} + static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) { struct mv88e6xxx_chip *chip; @@ -6303,11 +6329,12 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, return chip->tag_protocol; } -static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port, +static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, enum dsa_tag_protocol proto) { struct mv88e6xxx_chip *chip = ds->priv; enum dsa_tag_protocol old_protocol; + struct dsa_port *cpu_dp; int err; switch (proto) { @@ -6332,11 +6359,24 @@ static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port, chip->tag_protocol = proto; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_setup_port_mode(chip, port); + dsa_switch_for_each_cpu_port(cpu_dp, ds) { + err = mv88e6xxx_setup_port_mode(chip, cpu_dp->index); + if (err) { + mv88e6xxx_reg_unlock(chip); + goto unwind; + } + } mv88e6xxx_reg_unlock(chip); - if (err) - chip->tag_protocol = old_protocol; + return 0; + +unwind: + chip->tag_protocol = old_protocol; + + mv88e6xxx_reg_lock(chip); + dsa_switch_for_each_cpu_port_continue_reverse(cpu_dp, ds) + mv88e6xxx_setup_port_mode(chip, cpu_dp->index); + mv88e6xxx_reg_unlock(chip); return err; } @@ -6830,11 +6870,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .vlan_msti_set = mv88e6xxx_vlan_msti_set, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, - .port_mdb_add = mv88e6xxx_port_mdb_add, - .port_mdb_del = mv88e6xxx_port_mdb_del, + .port_fdb_add = mv88e6xxx_port_fdb_add, + .port_fdb_del = mv88e6xxx_port_fdb_del, + .port_fdb_dump = mv88e6xxx_port_fdb_dump, + .port_mdb_add = mv88e6xxx_port_mdb_add, + .port_mdb_del = mv88e6xxx_port_mdb_del, .port_mirror_add = mv88e6xxx_port_mirror_add, .port_mirror_del = mv88e6xxx_port_mirror_del, .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, @@ -6959,10 +6999,6 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) chip->info = compat_info; - err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); - if (err) - goto out; - chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(chip->reset)) { err = PTR_ERR(chip->reset); @@ -6971,9 +7007,19 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) if (chip->reset) usleep_range(1000, 2000); - err = mv88e6xxx_detect(chip); - if (err) - goto out; + /* Detect if the device is configured in single chip addressing mode, + * otherwise continue with address specific smi init/detection. + */ + err = mv88e6xxx_single_chip_detect(chip, mdiodev); + if (err) { + err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); + if (err) + goto out; + + err = mv88e6xxx_detect(chip); + if (err) + goto out; + } if (chip->info->edsa_support == MV88E6XXX_EDSA_SUPPORTED) chip->tag_protocol = DSA_TAG_PROTO_EDSA; diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index faccfb3f0158..3e07dc39007a 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -42,145 +42,29 @@ static struct net_device *felix_classify_db(struct dsa_db db) } } -/* We are called before felix_npi_port_init(), so ocelot->npi is -1. */ -static int felix_migrate_fdbs_to_npi_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; - int err; - - err = ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); - if (err) - return err; - - return ocelot_fdb_add(ocelot, cpu, addr, vid, bridge_dev); -} - -static int felix_migrate_mdbs_to_npi_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct switchdev_obj_port_mdb mdb; - struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; - int err; - - memset(&mdb, 0, sizeof(mdb)); - ether_addr_copy(mdb.addr, addr); - mdb.vid = vid; - - err = ocelot_port_mdb_del(ocelot, port, &mdb, bridge_dev); - if (err) - return err; - - return ocelot_port_mdb_add(ocelot, cpu, &mdb, bridge_dev); -} - -static void felix_migrate_pgid_bit(struct dsa_switch *ds, int from, int to, - int pgid) -{ - struct ocelot *ocelot = ds->priv; - bool on; - u32 val; - - val = ocelot_read_rix(ocelot, ANA_PGID_PGID, pgid); - on = !!(val & BIT(from)); - val &= ~BIT(from); - if (on) - val |= BIT(to); - else - val &= ~BIT(to); - - ocelot_write_rix(ocelot, val, ANA_PGID_PGID, pgid); -} - -static void felix_migrate_flood_to_npi_port(struct dsa_switch *ds, int port) -{ - struct ocelot *ocelot = ds->priv; - - felix_migrate_pgid_bit(ds, port, ocelot->num_phys_ports, PGID_UC); - felix_migrate_pgid_bit(ds, port, ocelot->num_phys_ports, PGID_MC); - felix_migrate_pgid_bit(ds, port, ocelot->num_phys_ports, PGID_BC); -} - -static void -felix_migrate_flood_to_tag_8021q_port(struct dsa_switch *ds, int port) -{ - struct ocelot *ocelot = ds->priv; - - felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_UC); - felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_MC); - felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_BC); -} - -/* ocelot->npi was already set to -1 by felix_npi_port_deinit, so - * ocelot_fdb_add() will not redirect FDB entries towards the - * CPU port module here, which is what we want. - */ -static int -felix_migrate_fdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; - int err; - - err = ocelot_fdb_del(ocelot, cpu, addr, vid, bridge_dev); - if (err) - return err; - - return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev); -} - -static int -felix_migrate_mdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct switchdev_obj_port_mdb mdb; - struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; - int err; - - memset(&mdb, 0, sizeof(mdb)); - ether_addr_copy(mdb.addr, addr); - mdb.vid = vid; - - err = ocelot_port_mdb_del(ocelot, cpu, &mdb, bridge_dev); - if (err) - return err; - - return ocelot_port_mdb_add(ocelot, port, &mdb, bridge_dev); -} - /* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that * the tagger can perform RX source port identification. */ -static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port, + int upstream, u16 vid) { struct ocelot_vcap_filter *outer_tagging_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; - int key_length, upstream, err; + struct ocelot *ocelot = ds->priv; + unsigned long cookie; + int key_length, err; key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length; - upstream = dsa_upstream_port(ds, port); outer_tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); if (!outer_tagging_rule) return -ENOMEM; + cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream); + outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; outer_tagging_rule->prio = 1; - outer_tagging_rule->id.cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port); + outer_tagging_rule->id.cookie = cookie; outer_tagging_rule->id.tc_offload = false; outer_tagging_rule->block_id = VCAP_ES0; outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -201,16 +85,19 @@ static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid) return err; } -static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_del_rx(struct dsa_switch *ds, int port, + int upstream, u16 vid) { struct ocelot_vcap_filter *outer_tagging_rule; struct ocelot_vcap_block *block_vcap_es0; - struct ocelot *ocelot = &felix->ocelot; + struct ocelot *ocelot = ds->priv; + unsigned long cookie; block_vcap_es0 = &ocelot->block[VCAP_ES0]; + cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream); outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0, - port, false); + cookie, false); if (!outer_tagging_rule) return -ENOENT; @@ -220,12 +107,14 @@ static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid) /* Set up VCAP IS1 rules for stripping the tag_8021q VLAN on TX and VCAP IS2 * rules for steering those tagged packets towards the correct destination port */ -static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_add_tx(struct dsa_switch *ds, int port, + u16 vid) { struct ocelot_vcap_filter *untagging_rule, *redirect_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; - int upstream, err; + unsigned long cpu_ports = dsa_cpu_ports(ds); + struct ocelot *ocelot = ds->priv; + unsigned long cookie; + int err; untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); if (!untagging_rule) @@ -237,14 +126,14 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) return -ENOMEM; } - upstream = dsa_upstream_port(ds, port); + cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule->key_type = OCELOT_VCAP_KEY_ANY; - untagging_rule->ingress_port_mask = BIT(upstream); + untagging_rule->ingress_port_mask = cpu_ports; untagging_rule->vlan.vid.value = vid; untagging_rule->vlan.vid.mask = VLAN_VID_MASK; untagging_rule->prio = 1; - untagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); + untagging_rule->id.cookie = cookie; untagging_rule->id.tc_offload = false; untagging_rule->block_id = VCAP_IS1; untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -261,11 +150,13 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) return err; } + cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); + redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; - redirect_rule->ingress_port_mask = BIT(upstream); + redirect_rule->ingress_port_mask = cpu_ports; redirect_rule->pag = port; redirect_rule->prio = 1; - redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); + redirect_rule->id.cookie = cookie; redirect_rule->id.tc_offload = false; redirect_rule->block_id = VCAP_IS2; redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -283,19 +174,21 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) return 0; } -static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_del_tx(struct dsa_switch *ds, int port, u16 vid) { struct ocelot_vcap_filter *untagging_rule, *redirect_rule; struct ocelot_vcap_block *block_vcap_is1; struct ocelot_vcap_block *block_vcap_is2; - struct ocelot *ocelot = &felix->ocelot; + struct ocelot *ocelot = ds->priv; + unsigned long cookie; int err; block_vcap_is1 = &ocelot->block[VCAP_IS1]; block_vcap_is2 = &ocelot->block[VCAP_IS2]; + cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, - port, false); + cookie, false); if (!untagging_rule) return -ENOENT; @@ -303,8 +196,9 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) if (err) return err; + cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, - port, false); + cookie, false); if (!redirect_rule) return -ENOENT; @@ -314,7 +208,7 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, u16 flags) { - struct ocelot *ocelot = ds->priv; + struct dsa_port *cpu_dp; int err; /* tag_8021q.c assumes we are implementing this via port VLAN @@ -324,74 +218,65 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, if (!dsa_is_user_port(ds, port)) return 0; - err = felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); - if (err) - return err; - - err = felix_tag_8021q_vlan_add_tx(ocelot_to_felix(ocelot), port, vid); - if (err) { - felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); - return err; + dsa_switch_for_each_cpu_port(cpu_dp, ds) { + err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); + if (err) + return err; } + err = felix_tag_8021q_vlan_add_tx(ds, port, vid); + if (err) + goto add_tx_failed; + return 0; + +add_tx_failed: + dsa_switch_for_each_cpu_port(cpu_dp, ds) + felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid); + + return err; } static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) { - struct ocelot *ocelot = ds->priv; + struct dsa_port *cpu_dp; int err; if (!dsa_is_user_port(ds, port)) return 0; - err = felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); - if (err) - return err; - - err = felix_tag_8021q_vlan_del_tx(ocelot_to_felix(ocelot), port, vid); - if (err) { - felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); - return err; + dsa_switch_for_each_cpu_port(cpu_dp, ds) { + err = felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid); + if (err) + return err; } - return 0; -} - -/* Alternatively to using the NPI functionality, that same hardware MAC - * connected internally to the enetc or fman DSA master can be configured to - * use the software-defined tag_8021q frame format. As far as the hardware is - * concerned, it thinks it is a "dumb switch" - the queues of the CPU port - * module are now disconnected from it, but can still be accessed through - * register-based MMIO. - */ -static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port) -{ - mutex_lock(&ocelot->fwd_domain_lock); - - ocelot_port_set_dsa_8021q_cpu(ocelot, port); + err = felix_tag_8021q_vlan_del_tx(ds, port, vid); + if (err) + goto del_tx_failed; - /* Overwrite PGID_CPU with the non-tagging port */ - ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, PGID_CPU); + return 0; - ocelot_apply_bridge_fwd_mask(ocelot, true); +del_tx_failed: + dsa_switch_for_each_cpu_port(cpu_dp, ds) + felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); - mutex_unlock(&ocelot->fwd_domain_lock); + return err; } -static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) +static int felix_trap_get_cpu_port(struct dsa_switch *ds, + const struct ocelot_vcap_filter *trap) { - mutex_lock(&ocelot->fwd_domain_lock); - - ocelot_port_unset_dsa_8021q_cpu(ocelot, port); + struct dsa_port *dp; + int first_port; - /* Restore PGID_CPU */ - ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID, - PGID_CPU); + if (WARN_ON(!trap->ingress_port_mask)) + return -1; - ocelot_apply_bridge_fwd_mask(ocelot, true); + first_port = __ffs(trap->ingress_port_mask); + dp = dsa_to_port(ds, first_port); - mutex_unlock(&ocelot->fwd_domain_lock); + return dp->cpu_dp->index; } /* On switches with no extraction IRQ wired, trapped packets need to be @@ -407,19 +292,12 @@ static int felix_update_trapping_destinations(struct dsa_switch *ds, 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; + int err; if (!felix->info->quirk_no_xtr_irq) return 0; - /* Figure out the current CPU port */ - dsa_switch_for_each_cpu_port(dp, ds) { - cpu = dp->index; - break; - } - /* We are sure that "cpu" was found, otherwise * dsa_tree_setup_default_cpu() would have failed earlier. */ @@ -437,7 +315,7 @@ static int felix_update_trapping_destinations(struct dsa_switch *ds, * port module. */ mask_mode = OCELOT_MASK_MODE_REDIRECT; - port_mask = BIT(cpu); + port_mask = BIT(felix_trap_get_cpu_port(ds, trap)); cpu_copy_ena = !!trap->take_ts; } else { /* Trap packets only to the CPU port module, which is @@ -465,15 +343,113 @@ static int felix_update_trapping_destinations(struct dsa_switch *ds, return 0; } -static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) +/* The CPU port module is connected to the Node Processor Interface (NPI). This + * is the mode through which frames can be injected from and extracted to an + * external CPU, over Ethernet. In NXP SoCs, the "external CPU" is the ARM CPU + * running Linux, and this forms a DSA setup together with the enetc or fman + * DSA master. + */ +static void felix_npi_port_init(struct ocelot *ocelot, int port) +{ + ocelot->npi = port; + + ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | + QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port), + QSYS_EXT_CPU_CFG); + + /* NPI port Injection/Extraction configuration */ + ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, + ocelot->npi_xtr_prefix); + ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, + ocelot->npi_inj_prefix); + + /* Disable transmission of pause frames */ + ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0); +} + +static void felix_npi_port_deinit(struct ocelot *ocelot, int port) +{ + /* Restore hardware defaults */ + int unused_port = ocelot->num_phys_ports + 2; + + ocelot->npi = -1; + + ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPU_PORT(unused_port), + QSYS_EXT_CPU_CFG); + + ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, + OCELOT_TAG_PREFIX_DISABLED); + ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, + OCELOT_TAG_PREFIX_DISABLED); + + /* Enable transmission of pause frames */ + ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1); +} + +static int felix_tag_npi_setup(struct dsa_switch *ds) +{ + struct dsa_port *dp, *first_cpu_dp = NULL; + struct ocelot *ocelot = ds->priv; + + dsa_switch_for_each_user_port(dp, ds) { + if (first_cpu_dp && dp->cpu_dp != first_cpu_dp) { + dev_err(ds->dev, "Multiple NPI ports not supported\n"); + return -EINVAL; + } + + first_cpu_dp = dp->cpu_dp; + } + + if (!first_cpu_dp) + return -EINVAL; + + felix_npi_port_init(ocelot, first_cpu_dp->index); + + return 0; +} + +static void felix_tag_npi_teardown(struct dsa_switch *ds) +{ + struct ocelot *ocelot = ds->priv; + + felix_npi_port_deinit(ocelot, ocelot->npi); +} + +static unsigned long felix_tag_npi_get_host_fwd_mask(struct dsa_switch *ds) +{ + struct ocelot *ocelot = ds->priv; + + return BIT(ocelot->num_phys_ports); +} + +/* Alternatively to using the NPI functionality, that same hardware MAC + * connected internally to the enetc or fman DSA master can be configured to + * use the software-defined tag_8021q frame format. As far as the hardware is + * concerned, it thinks it is a "dumb switch" - the queues of the CPU port + * module are now disconnected from it, but can still be accessed through + * register-based MMIO. + */ +static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = { + .setup = felix_tag_npi_setup, + .teardown = felix_tag_npi_teardown, + .get_host_fwd_mask = felix_tag_npi_get_host_fwd_mask, +}; + +static int felix_tag_8021q_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct dsa_port *dp; int err; - felix_8021q_cpu_port_init(ocelot, cpu); + err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD)); + if (err) + return err; - dsa_switch_for_each_available_port(dp, ds) { + dsa_switch_for_each_user_port(dp, ds) + ocelot_port_assign_dsa_8021q_cpu(ocelot, dp->index, + dp->cpu_dp->index); + + 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: @@ -487,25 +463,6 @@ 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, dp->index); - } - - err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD)); - if (err) - return err; - - err = dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port); - if (err) - goto out_tag_8021q_unregister; - - err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_tag_8021q_port); - if (err) - goto out_migrate_fdbs; - - felix_migrate_flood_to_tag_8021q_port(ds, cpu); - - err = felix_update_trapping_destinations(ds, true); - if (err) - goto out_migrate_flood; /* The ownership of the CPU port module's queues might have just been * transferred to the tag_8021q tagger from the NPI-based tagger. @@ -517,31 +474,14 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) ocelot_drain_cpu_queue(ocelot, 0); return 0; - -out_migrate_flood: - felix_migrate_flood_to_npi_port(ds, cpu); - dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); -out_migrate_fdbs: - dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); -out_tag_8021q_unregister: - dsa_tag_8021q_unregister(ds); - return err; } -static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) +static void felix_tag_8021q_teardown(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct dsa_port *dp; - int err; - - 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); - - dsa_switch_for_each_available_port(dp, ds) { + dsa_switch_for_each_available_port(dp, ds) /* Restore the logic from ocelot_init: * do not forward BPDU frames to the front ports. */ @@ -549,173 +489,147 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff), ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); - } - felix_8021q_cpu_port_deinit(ocelot, cpu); + dsa_switch_for_each_user_port(dp, ds) + ocelot_port_unassign_dsa_8021q_cpu(ocelot, dp->index); + + dsa_tag_8021q_unregister(ds); } -/* The CPU port module is connected to the Node Processor Interface (NPI). This - * is the mode through which frames can be injected from and extracted to an - * external CPU, over Ethernet. In NXP SoCs, the "external CPU" is the ARM CPU - * running Linux, and this forms a DSA setup together with the enetc or fman - * DSA master. - */ -static void felix_npi_port_init(struct ocelot *ocelot, int port) +static unsigned long felix_tag_8021q_get_host_fwd_mask(struct dsa_switch *ds) { - ocelot->npi = port; - - ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | - QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port), - QSYS_EXT_CPU_CFG); - - /* NPI port Injection/Extraction configuration */ - ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, - ocelot->npi_xtr_prefix); - ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, - ocelot->npi_inj_prefix); - - /* Disable transmission of pause frames */ - ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0); + return dsa_cpu_ports(ds); } -static void felix_npi_port_deinit(struct ocelot *ocelot, int port) -{ - /* Restore hardware defaults */ - int unused_port = ocelot->num_phys_ports + 2; +static const struct felix_tag_proto_ops felix_tag_8021q_proto_ops = { + .setup = felix_tag_8021q_setup, + .teardown = felix_tag_8021q_teardown, + .get_host_fwd_mask = felix_tag_8021q_get_host_fwd_mask, +}; - ocelot->npi = -1; +static void felix_set_host_flood(struct dsa_switch *ds, unsigned long mask, + bool uc, bool mc, bool bc) +{ + struct ocelot *ocelot = ds->priv; + unsigned long val; - ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPU_PORT(unused_port), - QSYS_EXT_CPU_CFG); + val = uc ? mask : 0; + ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_UC); - ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, - OCELOT_TAG_PREFIX_DISABLED); - ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, - OCELOT_TAG_PREFIX_DISABLED); + val = mc ? mask : 0; + ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MC); + ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV4); + ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV6); - /* Enable transmission of pause frames */ - ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1); + val = bc ? mask : 0; + ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_BC); } -static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu) +static void +felix_migrate_host_flood(struct dsa_switch *ds, + const struct felix_tag_proto_ops *proto_ops, + const struct felix_tag_proto_ops *old_proto_ops) { struct ocelot *ocelot = ds->priv; - int err; - - err = dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); - if (err) - return err; - - err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); - if (err) - goto out_migrate_fdbs; - - felix_migrate_flood_to_npi_port(ds, cpu); - - felix_npi_port_init(ocelot, cpu); - - return 0; + struct felix *felix = ocelot_to_felix(ocelot); + unsigned long mask; -out_migrate_fdbs: - dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port); + if (old_proto_ops) { + mask = old_proto_ops->get_host_fwd_mask(ds); + felix_set_host_flood(ds, mask, false, false, false); + } - return err; + mask = proto_ops->get_host_fwd_mask(ds); + felix_set_host_flood(ds, mask, !!felix->host_flood_uc_mask, + !!felix->host_flood_mc_mask, true); } -static void felix_teardown_tag_npi(struct dsa_switch *ds, int cpu) +static int felix_migrate_mdbs(struct dsa_switch *ds, + const struct felix_tag_proto_ops *proto_ops, + const struct felix_tag_proto_ops *old_proto_ops) { struct ocelot *ocelot = ds->priv; + unsigned long from, to; + + if (!old_proto_ops) + return 0; + + from = old_proto_ops->get_host_fwd_mask(ds); + to = proto_ops->get_host_fwd_mask(ds); - felix_npi_port_deinit(ocelot, cpu); + return ocelot_migrate_mdbs(ocelot, from, to); } -static int felix_set_tag_protocol(struct dsa_switch *ds, int cpu, - enum dsa_tag_protocol proto) +/* Configure the shared hardware resources for a transition between + * @old_proto_ops and @proto_ops. + * Manual migration is needed because as far as DSA is concerned, no change of + * the CPU port is taking place here, just of the tagging protocol. + */ +static int +felix_tag_proto_setup_shared(struct dsa_switch *ds, + const struct felix_tag_proto_ops *proto_ops, + const struct felix_tag_proto_ops *old_proto_ops) { + bool using_tag_8021q = (proto_ops == &felix_tag_8021q_proto_ops); int err; - switch (proto) { - case DSA_TAG_PROTO_SEVILLE: - case DSA_TAG_PROTO_OCELOT: - err = felix_setup_tag_npi(ds, cpu); - break; - case DSA_TAG_PROTO_OCELOT_8021Q: - err = felix_setup_tag_8021q(ds, cpu); - break; - default: - err = -EPROTONOSUPPORT; - } + err = felix_migrate_mdbs(ds, proto_ops, old_proto_ops); + if (err) + return err; - return err; -} + felix_update_trapping_destinations(ds, using_tag_8021q); -static void felix_del_tag_protocol(struct dsa_switch *ds, int cpu, - enum dsa_tag_protocol proto) -{ - switch (proto) { - case DSA_TAG_PROTO_SEVILLE: - case DSA_TAG_PROTO_OCELOT: - felix_teardown_tag_npi(ds, cpu); - break; - case DSA_TAG_PROTO_OCELOT_8021Q: - felix_teardown_tag_8021q(ds, cpu); - break; - default: - break; - } + felix_migrate_host_flood(ds, proto_ops, old_proto_ops); + + return 0; } /* This always leaves the switch in a consistent state, because although the * tag_8021q setup can fail, the NPI setup can't. So either the change is made, * or the restoration is guaranteed to work. */ -static int felix_change_tag_protocol(struct dsa_switch *ds, int cpu, +static int felix_change_tag_protocol(struct dsa_switch *ds, enum dsa_tag_protocol proto) { + const struct felix_tag_proto_ops *old_proto_ops, *proto_ops; struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - enum dsa_tag_protocol old_proto = felix->tag_proto; - bool cpu_port_active = false; - struct dsa_port *dp; int err; - if (proto != DSA_TAG_PROTO_SEVILLE && - proto != DSA_TAG_PROTO_OCELOT && - proto != DSA_TAG_PROTO_OCELOT_8021Q) + switch (proto) { + case DSA_TAG_PROTO_SEVILLE: + case DSA_TAG_PROTO_OCELOT: + proto_ops = &felix_tag_npi_proto_ops; + break; + case DSA_TAG_PROTO_OCELOT_8021Q: + proto_ops = &felix_tag_8021q_proto_ops; + break; + default: return -EPROTONOSUPPORT; - - /* We don't support multiple CPU ports, yet the DT blob may have - * multiple CPU ports defined. The first CPU port is the active one, - * the others are inactive. In this case, DSA will call - * ->change_tag_protocol() multiple times, once per CPU port. - * Since we implement the tagging protocol change towards "ocelot" or - * "seville" as effectively initializing the NPI port, what we are - * doing is effectively changing who the NPI port is to the last @cpu - * argument passed, which is an unused DSA CPU port and not the one - * that should actively pass traffic. - * Suppress DSA's calls on CPU ports that are inactive. - */ - dsa_switch_for_each_user_port(dp, ds) { - if (dp->cpu_dp->index == cpu) { - cpu_port_active = true; - break; - } } - if (!cpu_port_active) - return 0; + old_proto_ops = felix->tag_proto_ops; - felix_del_tag_protocol(ds, cpu, old_proto); + err = proto_ops->setup(ds); + if (err) + goto setup_failed; - err = felix_set_tag_protocol(ds, cpu, proto); - if (err) { - felix_set_tag_protocol(ds, cpu, old_proto); - return err; - } + err = felix_tag_proto_setup_shared(ds, proto_ops, old_proto_ops); + if (err) + goto setup_shared_failed; + + if (old_proto_ops) + old_proto_ops->teardown(ds); + felix->tag_proto_ops = proto_ops; felix->tag_proto = proto; return 0; + +setup_shared_failed: + proto_ops->teardown(ds); +setup_failed: + return err; } static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, @@ -728,6 +642,28 @@ static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, return felix->tag_proto; } +static void felix_port_set_host_flood(struct dsa_switch *ds, int port, + bool uc, bool mc) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + unsigned long mask; + + if (uc) + felix->host_flood_uc_mask |= BIT(port); + else + felix->host_flood_uc_mask &= ~BIT(port); + + if (mc) + felix->host_flood_mc_mask |= BIT(port); + else + felix->host_flood_mc_mask &= ~BIT(port); + + mask = felix->tag_proto_ops->get_host_fwd_mask(ds); + felix_set_host_flood(ds, mask, !!felix->host_flood_uc_mask, + !!felix->host_flood_mc_mask, true); +} + static int felix_set_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) { @@ -762,15 +698,19 @@ static int felix_fdb_add(struct dsa_switch *ds, int port, struct dsa_db db) { struct net_device *bridge_dev = felix_classify_db(db); + struct dsa_port *dp = dsa_to_port(ds, port); struct ocelot *ocelot = ds->priv; if (IS_ERR(bridge_dev)) return PTR_ERR(bridge_dev); - if (dsa_is_cpu_port(ds, port) && !bridge_dev && + if (dsa_port_is_cpu(dp) && !bridge_dev && dsa_fdb_present_in_other_db(ds, port, addr, vid, db)) return 0; + if (dsa_port_is_cpu(dp)) + port = PGID_CPU; + return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev); } @@ -779,15 +719,19 @@ static int felix_fdb_del(struct dsa_switch *ds, int port, struct dsa_db db) { struct net_device *bridge_dev = felix_classify_db(db); + struct dsa_port *dp = dsa_to_port(ds, port); struct ocelot *ocelot = ds->priv; if (IS_ERR(bridge_dev)) return PTR_ERR(bridge_dev); - if (dsa_is_cpu_port(ds, port) && !bridge_dev && + if (dsa_port_is_cpu(dp) && !bridge_dev && dsa_fdb_present_in_other_db(ds, port, addr, vid, db)) return 0; + if (dsa_port_is_cpu(dp)) + port = PGID_CPU; + return ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); } @@ -831,6 +775,9 @@ static int felix_mdb_add(struct dsa_switch *ds, int port, dsa_mdb_present_in_other_db(ds, port, mdb, db)) return 0; + if (port == ocelot->npi) + port = ocelot->num_phys_ports; + return ocelot_port_mdb_add(ocelot, port, mdb, bridge_dev); } @@ -848,6 +795,9 @@ static int felix_mdb_del(struct dsa_switch *ds, int port, dsa_mdb_present_in_other_db(ds, port, mdb, db)) return 0; + if (port == ocelot->npi) + port = ocelot->num_phys_ports; + return ocelot_port_mdb_del(ocelot, port, mdb, bridge_dev); } @@ -874,6 +824,9 @@ static int felix_bridge_flags(struct dsa_switch *ds, int port, { struct ocelot *ocelot = ds->priv; + if (port == ocelot->npi) + port = ocelot->num_phys_ports; + ocelot_port_bridge_flags(ocelot, port, val); return 0; @@ -1107,6 +1060,7 @@ static const u32 felix_phy_match_table[PHY_INTERFACE_MODE_MAX] = { [PHY_INTERFACE_MODE_SGMII] = OCELOT_PORT_MODE_SGMII, [PHY_INTERFACE_MODE_QSGMII] = OCELOT_PORT_MODE_QSGMII, [PHY_INTERFACE_MODE_USXGMII] = OCELOT_PORT_MODE_USXGMII, + [PHY_INTERFACE_MODE_1000BASEX] = OCELOT_PORT_MODE_1000BASEX, [PHY_INTERFACE_MODE_2500BASEX] = OCELOT_PORT_MODE_2500BASEX, }; @@ -1202,7 +1156,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ocelot->map = felix->info->map; ocelot->stats_layout = felix->info->stats_layout; - ocelot->num_stats = felix->info->num_stats; ocelot->num_mact_rows = felix->info->num_mact_rows; ocelot->vcap = felix->info->vcap; ocelot->vcap_pol.base = felix->info->vcap_pol_base; @@ -1285,6 +1238,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ocelot_port->phy_mode = port_phy_modes[port]; ocelot_port->ocelot = ocelot; ocelot_port->target = target; + ocelot_port->index = port; ocelot->ports[port] = ocelot_port; } @@ -1387,7 +1341,6 @@ static int felix_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - unsigned long cpu_flood; struct dsa_port *dp; int err; @@ -1421,21 +1374,10 @@ static int felix_setup(struct dsa_switch *ds) if (err) goto out_deinit_ports; - 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, dp->index, felix->tag_proto); - - /* Start off with flooding disabled towards the NPI port - * (actually CPU port module). - */ - cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)); - ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC); - ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC); - - break; - } + /* The initial tag protocol is NPI which won't fail during initial + * setup, there's no real point in checking for errors. + */ + felix_change_tag_protocol(ds, felix->tag_proto); ds->mtu_enforcement_ingress = true; ds->assisted_learning_on_cpu_port = true; @@ -1464,10 +1406,8 @@ static void felix_teardown(struct dsa_switch *ds) struct felix *felix = ocelot_to_felix(ocelot); struct dsa_port *dp; - dsa_switch_for_each_cpu_port(dp, ds) { - felix_del_tag_protocol(ds, dp->index, felix->tag_proto); - break; - } + if (felix->tag_proto_ops) + felix->tag_proto_ops->teardown(ds); dsa_switch_for_each_available_port(dp, ds) ocelot_deinit_port(ocelot, dp->index); @@ -1953,6 +1893,7 @@ const struct dsa_switch_ops felix_switch_ops = { .port_get_dscp_prio = felix_port_get_dscp_prio, .port_add_dscp_prio = felix_port_add_dscp_prio, .port_del_dscp_prio = felix_port_del_dscp_prio, + .port_set_host_flood = felix_port_set_host_flood, }; struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index f083b06fdfe9..9e07eb7ee28d 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -12,6 +12,7 @@ #define OCELOT_PORT_MODE_QSGMII BIT(2) #define OCELOT_PORT_MODE_2500BASEX BIT(3) #define OCELOT_PORT_MODE_USXGMII BIT(4) +#define OCELOT_PORT_MODE_1000BASEX BIT(5) /* Platform-specific information */ struct felix_info { @@ -24,7 +25,6 @@ struct felix_info { const u32 *port_modes; int num_mact_rows; const struct ocelot_stat_layout *stats_layout; - unsigned int num_stats; int num_ports; int num_tx_queues; struct vcap_props *vcap; @@ -59,6 +59,19 @@ struct felix_info { struct resource *res); }; +/* Methods for initializing the hardware resources specific to a tagging + * protocol (like the NPI port, for "ocelot" or "seville", or the VCAP TCAMs, + * for "ocelot-8021q"). + * It is important that the resources configured here do not have side effects + * for the other tagging protocols. If that is the case, their configuration + * needs to go to felix_tag_proto_setup_shared(). + */ +struct felix_tag_proto_ops { + int (*setup)(struct dsa_switch *ds); + void (*teardown)(struct dsa_switch *ds); + unsigned long (*get_host_fwd_mask)(struct dsa_switch *ds); +}; + extern const struct dsa_switch_ops felix_switch_ops; /* DSA glue / front-end for struct ocelot */ @@ -71,7 +84,10 @@ struct felix { resource_size_t switch_base; resource_size_t imdio_base; enum dsa_tag_protocol tag_proto; + const struct felix_tag_proto_ops *tag_proto_ops; struct kthread_worker *xmit_worker; + unsigned long host_flood_uc_mask; + unsigned long host_flood_mc_mask; }; struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port); diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 52a8566071ed..570d0204b7be 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -28,6 +28,7 @@ #define VSC9959_PORT_MODE_SERDES (OCELOT_PORT_MODE_SGMII | \ OCELOT_PORT_MODE_QSGMII | \ + OCELOT_PORT_MODE_1000BASEX | \ OCELOT_PORT_MODE_2500BASEX | \ OCELOT_PORT_MODE_USXGMII) @@ -638,6 +639,7 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = { { .offset = 0x10F, .name = "drop_green_prio_5", }, { .offset = 0x110, .name = "drop_green_prio_6", }, { .offset = 0x111, .name = "drop_green_prio_7", }, + OCELOT_STAT_END }; static const struct vcap_field vsc9959_vcap_es0_keys[] = { @@ -972,6 +974,7 @@ static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, phylink_set(mask, 100baseT_Full); phylink_set(mask, 1000baseT_Half); phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); if (state->interface == PHY_INTERFACE_MODE_INTERNAL || state->interface == PHY_INTERFACE_MODE_2500BASEX || @@ -2159,7 +2162,8 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot) if (ocelot->npi >= 0) mask |= BIT(ocelot->npi); else - mask |= ocelot_get_dsa_8021q_cpu_mask(ocelot); + mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot, + port); } /* Calculate the minimum link speed, among the ports that are @@ -2216,7 +2220,6 @@ static const struct felix_info felix_info_vsc9959 = { .map = vsc9959_regmap, .ops = &vsc9959_ops, .stats_layout = vsc9959_stats_layout, - .num_stats = ARRAY_SIZE(vsc9959_stats_layout), .vcap = vsc9959_vcap_props, .vcap_pol_base = VSC9959_VCAP_POLICER_BASE, .vcap_pol_max = VSC9959_VCAP_POLICER_MAX, diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 68ef8f111bbe..ea0649211356 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -21,7 +21,8 @@ #define VSC9953_VCAP_POLICER_BASE2 120 #define VSC9953_VCAP_POLICER_MAX2 161 -#define VSC9953_PORT_MODE_SERDES (OCELOT_PORT_MODE_SGMII | \ +#define VSC9953_PORT_MODE_SERDES (OCELOT_PORT_MODE_1000BASEX | \ + OCELOT_PORT_MODE_SGMII | \ OCELOT_PORT_MODE_QSGMII) static const u32 vsc9953_port_modes[VSC9953_NUM_PORTS] = { @@ -636,6 +637,7 @@ static const struct ocelot_stat_layout vsc9953_stats_layout[] = { { .offset = 0x8F, .name = "drop_green_prio_5", }, { .offset = 0x90, .name = "drop_green_prio_6", }, { .offset = 0x91, .name = "drop_green_prio_7", }, + OCELOT_STAT_END }; static const struct vcap_field vsc9953_vcap_es0_keys[] = { @@ -946,6 +948,7 @@ static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, phylink_set(mask, 100baseT_Full); phylink_set(mask, 100baseT_Half); phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); if (state->interface == PHY_INTERFACE_MODE_INTERNAL) { phylink_set(mask, 2500baseT_Full); @@ -1086,7 +1089,6 @@ static const struct felix_info seville_info_vsc9953 = { .map = vsc9953_regmap, .ops = &vsc9953_ops, .stats_layout = vsc9953_stats_layout, - .num_stats = ARRAY_SIZE(vsc9953_stats_layout), .vcap = vsc9953_vcap_props, .vcap_pol_base = VSC9953_VCAP_POLICER_BASE, .vcap_pol_max = VSC9953_VCAP_POLICER_MAX, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index d3ed0a7f8077..2727d3169c25 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -1287,87 +1287,71 @@ qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) if (ret >= 0) return ret; - return qca8k_mdio_read(priv, phy, regnum); + ret = qca8k_mdio_read(priv, phy, regnum); + + if (ret < 0) + return 0xffff; + + return ret; } static int -qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) +qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) { - struct qca8k_priv *priv = ds->priv; - int ret; + port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - /* Check if the legacy mapping should be used and the - * port is not correctly mapped to the right PHY in the - * devicetree - */ - if (priv->legacy_phy_port_mapping) - port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - - /* 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); + return qca8k_internal_mdio_write(slave_bus, port, regnum, data); } static int -qca8k_phy_read(struct dsa_switch *ds, int port, int regnum) +qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) { - struct qca8k_priv *priv = ds->priv; - int ret; + port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - /* Check if the legacy mapping should be used and the - * port is not correctly mapped to the right PHY in the - * devicetree - */ - if (priv->legacy_phy_port_mapping) - port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; - - /* 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; - - return ret; + return qca8k_internal_mdio_read(slave_bus, port, regnum); } static int -qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio) +qca8k_mdio_register(struct qca8k_priv *priv) { struct dsa_switch *ds = priv->ds; + struct device_node *mdio; struct mii_bus *bus; bus = devm_mdiobus_alloc(ds->dev); - if (!bus) return -ENOMEM; bus->priv = (void *)priv; - bus->name = "qca8k slave mii"; - bus->read = qca8k_internal_mdio_read; - bus->write = qca8k_internal_mdio_write; - snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", - ds->index); - + snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", + ds->dst->index, ds->index); bus->parent = ds->dev; bus->phy_mask = ~ds->phys_mii_mask; - ds->slave_mii_bus = bus; - return devm_of_mdiobus_register(priv->dev, bus, mdio); + /* Check if the devicetree declare the port:phy mapping */ + mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (of_device_is_available(mdio)) { + bus->name = "qca8k slave mii"; + bus->read = qca8k_internal_mdio_read; + bus->write = qca8k_internal_mdio_write; + return devm_of_mdiobus_register(priv->dev, bus, mdio); + } + + /* If a mapping can't be found the legacy mapping is used, + * using the qca8k_port_to_phy function + */ + bus->name = "qca8k-legacy slave mii"; + bus->read = qca8k_legacy_mdio_read; + bus->write = qca8k_legacy_mdio_write; + return devm_mdiobus_register(priv->dev, bus); } static int qca8k_setup_mdio_bus(struct qca8k_priv *priv) { u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; - struct device_node *ports, *port, *mdio; + struct device_node *ports, *port; phy_interface_t mode; int err; @@ -1429,24 +1413,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv) QCA8K_MDIO_MASTER_EN); } - /* Check if the devicetree declare the port:phy mapping */ - mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); - if (of_device_is_available(mdio)) { - err = qca8k_mdio_register(priv, mdio); - if (err) - of_node_put(mdio); - - return err; - } - - /* If a mapping can't be found the legacy mapping is used, - * using the qca8k_port_to_phy function - */ - priv->legacy_phy_port_mapping = true; - priv->ops.phy_read = qca8k_phy_read; - priv->ops.phy_write = qca8k_phy_write; - - return 0; + return qca8k_mdio_register(priv); } static int @@ -2346,7 +2313,7 @@ qca8k_port_enable(struct dsa_switch *ds, int port, struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; qca8k_port_set_status(priv, port, 1); - priv->port_sts[port].enabled = 1; + priv->port_enabled_map |= BIT(port); if (dsa_is_user_port(ds, port)) phy_support_asym_pause(phy); @@ -2360,23 +2327,25 @@ qca8k_port_disable(struct dsa_switch *ds, int port) struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; qca8k_port_set_status(priv, port, 0); - priv->port_sts[port].enabled = 0; + priv->port_enabled_map &= ~BIT(port); } static int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { struct qca8k_priv *priv = ds->priv; - int i, mtu = 0; - priv->port_mtu[port] = new_mtu; - - for (i = 0; i < QCA8K_NUM_PORTS; i++) - if (priv->port_mtu[i] > mtu) - mtu = priv->port_mtu[i]; + /* We have only have a general MTU setting. + * DSA always set the CPU port's MTU to the largest MTU of the slave + * ports. + * Setting MTU just for the CPU port is sufficient to correctly set a + * value for every port. + */ + if (!dsa_is_cpu_port(ds, port)) + return 0; /* Include L2 header / FCS length */ - return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); + return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); } static int @@ -3033,16 +3002,6 @@ qca8k_setup(struct dsa_switch *ds) 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 */ @@ -3202,8 +3161,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->ds->dev = &mdiodev->dev; priv->ds->num_ports = QCA8K_NUM_PORTS; priv->ds->priv = priv; - priv->ops = qca8k_switch_ops; - priv->ds->ops = &priv->ops; + priv->ds->ops = &qca8k_switch_ops; mutex_init(&priv->reg_mutex); dev_set_drvdata(&mdiodev->dev, priv); @@ -3243,13 +3201,16 @@ static void qca8k_sw_shutdown(struct mdio_device *mdiodev) static void qca8k_set_pm(struct qca8k_priv *priv, int enable) { - int i; + int port; - for (i = 0; i < QCA8K_NUM_PORTS; i++) { - if (!priv->port_sts[i].enabled) + for (port = 0; port < QCA8K_NUM_PORTS; port++) { + /* Do not enable on resume if the port was + * disabled before. + */ + if (!(priv->port_enabled_map & BIT(port))) continue; - qca8k_port_set_status(priv, i, enable); + qca8k_port_set_status(priv, port, enable); } } diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index f375627174c8..04408e11402a 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -324,10 +324,6 @@ enum qca8k_mid_cmd { QCA8K_MIB_CAST = 3, }; -struct ar8xxx_port_status { - int enabled; -}; - struct qca8k_match_data { u8 id; bool reduced_package; @@ -388,17 +384,17 @@ struct qca8k_priv { u8 mirror_rx; u8 mirror_tx; u8 lag_hash_mode; - bool legacy_phy_port_mapping; + /* Each bit correspond to a port. This switch can support a max of 7 port. + * Bit 1: port enabled. Bit 0: port disabled. + */ + u8 port_enabled_map; struct qca8k_ports_config ports_config; struct regmap *regmap; struct mii_bus *bus; - struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; struct dsa_switch *ds; struct mutex reg_mutex; struct device *dev; - 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; diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 3d70e8a77ecf..3bb42a9f236d 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -1778,7 +1778,7 @@ static int rtl8365mb_cpu_config(struct realtek_priv *priv) return 0; } -static int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, int cpu_index, +static int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, enum dsa_tag_protocol proto) { struct realtek_priv *priv = ds->priv; diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index 1a3406b9e64c..25f88022b9e4 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -1653,29 +1653,37 @@ static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - ret = regmap_write(priv->map, RTL8366RB_PHY_ACCESS_CTRL_REG, + mutex_lock(&priv->map_lock); + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_READ); if (ret) - return ret; + goto out; reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; - ret = regmap_write(priv->map, reg, 0); + ret = regmap_write(priv->map_nolock, reg, 0); if (ret) { dev_err(priv->dev, "failed to write PHY%d reg %04x @ %04x, ret %d\n", phy, regnum, reg, ret); - return ret; + goto out; } - ret = regmap_read(priv->map, RTL8366RB_PHY_ACCESS_DATA_REG, &val); + ret = regmap_read(priv->map_nolock, RTL8366RB_PHY_ACCESS_DATA_REG, + &val); if (ret) - return ret; + goto out; + + ret = val; dev_dbg(priv->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", phy, regnum, reg, val); - return val; +out: + mutex_unlock(&priv->map_lock); + + return ret; } static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, @@ -1687,21 +1695,26 @@ static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, if (phy > RTL8366RB_PHY_NO_MAX) return -EINVAL; - ret = regmap_write(priv->map, RTL8366RB_PHY_ACCESS_CTRL_REG, + mutex_lock(&priv->map_lock); + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, RTL8366RB_PHY_CTRL_WRITE); if (ret) - return ret; + goto out; reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; dev_dbg(priv->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", phy, regnum, reg, val); - ret = regmap_write(priv->map, reg, val); + ret = regmap_write(priv->map_nolock, reg, val); if (ret) - return ret; + goto out; - return 0; +out: + mutex_unlock(&priv->map_lock); + + return ret; } static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index b33841c6507a..72b6fc1932b5 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2252,14 +2252,13 @@ int sja1105_static_config_reload(struct sja1105_private *priv, * change it through the dynamic interface later. */ for (i = 0; i < ds->num_ports; i++) { - u32 reg_addr = mdiobus_c45_addr(MDIO_MMD_VEND2, MDIO_CTRL1); - speed_mbps[i] = sja1105_port_speed_to_ethtool(priv, mac[i].speed); mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; if (priv->xpcs[i]) - bmcr[i] = mdiobus_read(priv->mdio_pcs, i, reg_addr); + bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i, + MDIO_MMD_VEND2, MDIO_CTRL1); } /* No PTP operations can run right now */ |