Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[platform/kernel/linux-rpi.git] / drivers / net / dsa / mv88e6xxx / chip.c
index e08bf93..860dca4 100644 (file)
@@ -485,12 +485,12 @@ static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port,
                                          struct phylink_link_state *state)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
-       u8 lane;
+       int lane;
        int err;
 
        mv88e6xxx_reg_lock(chip);
        lane = mv88e6xxx_serdes_get_lane(chip, port);
-       if (lane && chip->info->ops->serdes_pcs_get_state)
+       if (lane >= 0 && chip->info->ops->serdes_pcs_get_state)
                err = chip->info->ops->serdes_pcs_get_state(chip, port, lane,
                                                            state);
        else
@@ -506,11 +506,11 @@ static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
                                       const unsigned long *advertise)
 {
        const struct mv88e6xxx_ops *ops = chip->info->ops;
-       u8 lane;
+       int lane;
 
        if (ops->serdes_pcs_config) {
                lane = mv88e6xxx_serdes_get_lane(chip, port);
-               if (lane)
+               if (lane >= 0)
                        return ops->serdes_pcs_config(chip, port, lane, mode,
                                                      interface, advertise);
        }
@@ -523,14 +523,14 @@ static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port)
        struct mv88e6xxx_chip *chip = ds->priv;
        const struct mv88e6xxx_ops *ops;
        int err = 0;
-       u8 lane;
+       int lane;
 
        ops = chip->info->ops;
 
        if (ops->serdes_pcs_an_restart) {
                mv88e6xxx_reg_lock(chip);
                lane = mv88e6xxx_serdes_get_lane(chip, port);
-               if (lane)
+               if (lane >= 0)
                        err = ops->serdes_pcs_an_restart(chip, port, lane);
                mv88e6xxx_reg_unlock(chip);
 
@@ -544,11 +544,11 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
                                        int speed, int duplex)
 {
        const struct mv88e6xxx_ops *ops = chip->info->ops;
-       u8 lane;
+       int lane;
 
        if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) {
                lane = mv88e6xxx_serdes_get_lane(chip, port);
-               if (lane)
+               if (lane >= 0)
                        return ops->serdes_pcs_link_up(chip, port, lane,
                                                       speed, duplex);
        }
@@ -635,6 +635,29 @@ static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
        mv88e6390_phylink_validate(chip, port, mask, state);
 }
 
+static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+                                       unsigned long *mask,
+                                       struct phylink_link_state *state)
+{
+       if (port == 0 || port == 9 || port == 10) {
+               phylink_set(mask, 10000baseT_Full);
+               phylink_set(mask, 10000baseKR_Full);
+               phylink_set(mask, 10000baseCR_Full);
+               phylink_set(mask, 10000baseSR_Full);
+               phylink_set(mask, 10000baseLR_Full);
+               phylink_set(mask, 10000baseLRM_Full);
+               phylink_set(mask, 10000baseER_Full);
+               phylink_set(mask, 5000baseT_Full);
+               phylink_set(mask, 2500baseX_Full);
+               phylink_set(mask, 2500baseT_Full);
+       }
+
+       phylink_set(mask, 1000baseT_Full);
+       phylink_set(mask, 1000baseX_Full);
+
+       mv88e6065_phylink_validate(chip, port, mask, state);
+}
+
 static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
                               unsigned long *supported,
                               struct phylink_link_state *state)
@@ -1456,6 +1479,13 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
 
+       if (dsa_to_port(ds, port)->lag_dev)
+               /* Hardware is incapable of fast-aging a LAG through a
+                * regular ATU move operation. Until we have something
+                * more fancy in place this is a no-op.
+                */
+               return;
+
        mv88e6xxx_reg_lock(chip);
        err = mv88e6xxx_g1_atu_remove(chip, 0, port, false);
        mv88e6xxx_reg_unlock(chip);
@@ -1472,13 +1502,54 @@ static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
        return mv88e6xxx_g1_vtu_flush(chip);
 }
 
-static int mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
-                                struct mv88e6xxx_vtu_entry *entry)
+static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
+                            struct mv88e6xxx_vtu_entry *entry)
+{
+       int err;
+
+       if (!chip->info->ops->vtu_getnext)
+               return -EOPNOTSUPP;
+
+       entry->vid = vid ? vid - 1 : mv88e6xxx_max_vid(chip);
+       entry->valid = false;
+
+       err = chip->info->ops->vtu_getnext(chip, entry);
+
+       if (entry->vid != vid)
+               entry->valid = false;
+
+       return err;
+}
+
+static int mv88e6xxx_vtu_walk(struct mv88e6xxx_chip *chip,
+                             int (*cb)(struct mv88e6xxx_chip *chip,
+                                       const struct mv88e6xxx_vtu_entry *entry,
+                                       void *priv),
+                             void *priv)
 {
+       struct mv88e6xxx_vtu_entry entry = {
+               .vid = mv88e6xxx_max_vid(chip),
+               .valid = false,
+       };
+       int err;
+
        if (!chip->info->ops->vtu_getnext)
                return -EOPNOTSUPP;
 
-       return chip->info->ops->vtu_getnext(chip, entry);
+       do {
+               err = chip->info->ops->vtu_getnext(chip, &entry);
+               if (err)
+                       return err;
+
+               if (!entry.valid)
+                       break;
+
+               err = cb(chip, &entry, priv);
+               if (err)
+                       return err;
+       } while (entry.vid < mv88e6xxx_max_vid(chip));
+
+       return 0;
 }
 
 static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
@@ -1490,9 +1561,18 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
        return chip->info->ops->vtu_loadpurge(chip, entry);
 }
 
+static int mv88e6xxx_fid_map_vlan(struct mv88e6xxx_chip *chip,
+                                 const struct mv88e6xxx_vtu_entry *entry,
+                                 void *_fid_bitmap)
+{
+       unsigned long *fid_bitmap = _fid_bitmap;
+
+       set_bit(entry->fid, fid_bitmap);
+       return 0;
+}
+
 int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap)
 {
-       struct mv88e6xxx_vtu_entry vlan;
        int i, err;
        u16 fid;
 
@@ -1508,21 +1588,7 @@ int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap)
        }
 
        /* Set every FID bit used by the VLAN entries */
-       vlan.vid = mv88e6xxx_max_vid(chip);
-       vlan.valid = false;
-
-       do {
-               err = mv88e6xxx_vtu_getnext(chip, &vlan);
-               if (err)
-                       return err;
-
-               if (!vlan.valid)
-                       break;
-
-               set_bit(vlan.fid, fid_bitmap);
-       } while (vlan.vid < mv88e6xxx_max_vid(chip));
-
-       return 0;
+       return mv88e6xxx_vtu_walk(chip, mv88e6xxx_fid_map_vlan, fid_bitmap);
 }
 
 static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
@@ -1559,19 +1625,13 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
                return 0;
 
-       vlan.vid = vid - 1;
-       vlan.valid = false;
-
-       err = mv88e6xxx_vtu_getnext(chip, &vlan);
+       err = mv88e6xxx_vtu_get(chip, vid, &vlan);
        if (err)
                return err;
 
        if (!vlan.valid)
                return 0;
 
-       if (vlan.vid != vid)
-               return 0;
-
        for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
                if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
                        continue;
@@ -1653,15 +1713,12 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
                if (err)
                        return err;
        } else {
-               vlan.vid = vid - 1;
-               vlan.valid = false;
-
-               err = mv88e6xxx_vtu_getnext(chip, &vlan);
+               err = mv88e6xxx_vtu_get(chip, vid, &vlan);
                if (err)
                        return err;
 
                /* switchdev expects -EOPNOTSUPP to honor software VLANs */
-               if (vlan.vid != vid || !vlan.valid)
+               if (!vlan.valid)
                        return -EOPNOTSUPP;
 
                fid = vlan.fid;
@@ -1911,8 +1968,10 @@ static int mv88e6xxx_set_rxnfc(struct dsa_switch *ds, int port,
 static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port,
                                        u16 vid)
 {
-       const char broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
        u8 state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC;
+       u8 broadcast[ETH_ALEN];
+
+       eth_broadcast_addr(broadcast);
 
        return mv88e6xxx_port_db_load_purge(chip, port, broadcast, vid, state);
 }
@@ -1923,6 +1982,19 @@ static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid)
        int err;
 
        for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
+               struct dsa_port *dp = dsa_to_port(chip->ds, port);
+               struct net_device *brport;
+
+               if (dsa_is_unused_port(chip->ds, port))
+                       continue;
+
+               brport = dsa_port_to_bridge_port(dp);
+               if (brport && !br_port_flag_is_set(brport, BR_BCAST_FLOOD))
+                       /* Skip bridged user ports where broadcast
+                        * flooding is disabled.
+                        */
+                       continue;
+
                err = mv88e6xxx_port_add_broadcast(chip, port, vid);
                if (err)
                        return err;
@@ -1931,6 +2003,53 @@ static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid)
        return 0;
 }
 
+struct mv88e6xxx_port_broadcast_sync_ctx {
+       int port;
+       bool flood;
+};
+
+static int
+mv88e6xxx_port_broadcast_sync_vlan(struct mv88e6xxx_chip *chip,
+                                  const struct mv88e6xxx_vtu_entry *vlan,
+                                  void *_ctx)
+{
+       struct mv88e6xxx_port_broadcast_sync_ctx *ctx = _ctx;
+       u8 broadcast[ETH_ALEN];
+       u8 state;
+
+       if (ctx->flood)
+               state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC;
+       else
+               state = MV88E6XXX_G1_ATU_DATA_STATE_MC_UNUSED;
+
+       eth_broadcast_addr(broadcast);
+
+       return mv88e6xxx_port_db_load_purge(chip, ctx->port, broadcast,
+                                           vlan->vid, state);
+}
+
+static int mv88e6xxx_port_broadcast_sync(struct mv88e6xxx_chip *chip, int port,
+                                        bool flood)
+{
+       struct mv88e6xxx_port_broadcast_sync_ctx ctx = {
+               .port = port,
+               .flood = flood,
+       };
+       struct mv88e6xxx_vtu_entry vid0 = {
+               .vid = 0,
+       };
+       int err;
+
+       /* Update the port's private database... */
+       err = mv88e6xxx_port_broadcast_sync_vlan(chip, &vid0, &ctx);
+       if (err)
+               return err;
+
+       /* ...and the database for all VLANs. */
+       return mv88e6xxx_vtu_walk(chip, mv88e6xxx_port_broadcast_sync_vlan,
+                                 &ctx);
+}
+
 static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port,
                                    u16 vid, u8 member, bool warn)
 {
@@ -1938,14 +2057,11 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port,
        struct mv88e6xxx_vtu_entry vlan;
        int i, err;
 
-       vlan.vid = vid - 1;
-       vlan.valid = false;
-
-       err = mv88e6xxx_vtu_getnext(chip, &vlan);
+       err = mv88e6xxx_vtu_get(chip, vid, &vlan);
        if (err)
                return err;
 
-       if (vlan.vid != vid || !vlan.valid) {
+       if (!vlan.valid) {
                memset(&vlan, 0, sizeof(vlan));
 
                err = mv88e6xxx_atu_new(chip, &vlan.fid);
@@ -2041,17 +2157,14 @@ static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip,
        if (!vid)
                return -EOPNOTSUPP;
 
-       vlan.vid = vid - 1;
-       vlan.valid = false;
-
-       err = mv88e6xxx_vtu_getnext(chip, &vlan);
+       err = mv88e6xxx_vtu_get(chip, vid, &vlan);
        if (err)
                return err;
 
        /* If the VLAN doesn't exist in hardware or the port isn't a member,
         * tell switchdev that this VLAN is likely handled in software.
         */
-       if (vlan.vid != vid || !vlan.valid ||
+       if (!vlan.valid ||
            vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
                return -EOPNOTSUPP;
 
@@ -2168,10 +2281,30 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
        return err;
 }
 
+struct mv88e6xxx_port_db_dump_vlan_ctx {
+       int port;
+       dsa_fdb_dump_cb_t *cb;
+       void *data;
+};
+
+static int mv88e6xxx_port_db_dump_vlan(struct mv88e6xxx_chip *chip,
+                                      const struct mv88e6xxx_vtu_entry *entry,
+                                      void *_data)
+{
+       struct mv88e6xxx_port_db_dump_vlan_ctx *ctx = _data;
+
+       return mv88e6xxx_port_db_dump_fid(chip, entry->fid, entry->vid,
+                                         ctx->port, ctx->cb, ctx->data);
+}
+
 static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
                                  dsa_fdb_dump_cb_t *cb, void *data)
 {
-       struct mv88e6xxx_vtu_entry vlan;
+       struct mv88e6xxx_port_db_dump_vlan_ctx ctx = {
+               .port = port,
+               .cb = cb,
+               .data = data,
+       };
        u16 fid;
        int err;
 
@@ -2184,25 +2317,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
        if (err)
                return err;
 
-       /* Dump VLANs' Filtering Information Databases */
-       vlan.vid = mv88e6xxx_max_vid(chip);
-       vlan.valid = false;
-
-       do {
-               err = mv88e6xxx_vtu_getnext(chip, &vlan);
-               if (err)
-                       return err;
-
-               if (!vlan.valid)
-                       break;
-
-               err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
-                                                cb, data);
-               if (err)
-                       return err;
-       } while (vlan.vid < mv88e6xxx_max_vid(chip));
-
-       return err;
+       return mv88e6xxx_vtu_walk(chip, mv88e6xxx_port_db_dump_vlan, &ctx);
 }
 
 static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
@@ -2434,19 +2549,15 @@ static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port)
 
 static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port)
 {
-       struct dsa_switch *ds = chip->ds;
-       bool flood;
        int err;
 
-       /* Upstream ports flood frames with unknown unicast or multicast DA */
-       flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);
        if (chip->info->ops->port_set_ucast_flood) {
-               err = chip->info->ops->port_set_ucast_flood(chip, port, flood);
+               err = chip->info->ops->port_set_ucast_flood(chip, port, true);
                if (err)
                        return err;
        }
        if (chip->info->ops->port_set_mcast_flood) {
-               err = chip->info->ops->port_set_mcast_flood(chip, port, flood);
+               err = chip->info->ops->port_set_mcast_flood(chip, port, true);
                if (err)
                        return err;
        }
@@ -2460,11 +2571,11 @@ static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id)
        struct mv88e6xxx_chip *chip = mvp->chip;
        irqreturn_t ret = IRQ_NONE;
        int port = mvp->port;
-       u8 lane;
+       int lane;
 
        mv88e6xxx_reg_lock(chip);
        lane = mv88e6xxx_serdes_get_lane(chip, port);
-       if (lane)
+       if (lane >= 0)
                ret = mv88e6xxx_serdes_irq_status(chip, port, lane);
        mv88e6xxx_reg_unlock(chip);
 
@@ -2472,7 +2583,7 @@ static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id)
 }
 
 static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port,
-                                       u8 lane)
+                                       int lane)
 {
        struct mv88e6xxx_port *dev_id = &chip->ports[port];
        unsigned int irq;
@@ -2501,7 +2612,7 @@ static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port,
 }
 
 static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port,
-                                    u8 lane)
+                                    int lane)
 {
        struct mv88e6xxx_port *dev_id = &chip->ports[port];
        unsigned int irq = dev_id->serdes_irq;
@@ -2526,11 +2637,11 @@ static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port,
 static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
                                  bool on)
 {
-       u8 lane;
+       int lane;
        int err;
 
        lane = mv88e6xxx_serdes_get_lane(chip, port);
-       if (!lane)
+       if (lane < 0)
                return 0;
 
        if (on) {
@@ -2550,6 +2661,27 @@ static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
        return err;
 }
 
+static int mv88e6xxx_set_egress_port(struct mv88e6xxx_chip *chip,
+                                    enum mv88e6xxx_egress_direction direction,
+                                    int port)
+{
+       int err;
+
+       if (!chip->info->ops->set_egress_port)
+               return -EOPNOTSUPP;
+
+       err = chip->info->ops->set_egress_port(chip, direction, port);
+       if (err)
+               return err;
+
+       if (direction == MV88E6XXX_EGRESS_DIR_INGRESS)
+               chip->ingress_dest_port = port;
+       else
+               chip->egress_dest_port = port;
+
+       return 0;
+}
+
 static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)
 {
        struct dsa_switch *ds = chip->ds;
@@ -2572,19 +2704,17 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)
                                return err;
                }
 
-               if (chip->info->ops->set_egress_port) {
-                       err = chip->info->ops->set_egress_port(chip,
+               err = mv88e6xxx_set_egress_port(chip,
                                                MV88E6XXX_EGRESS_DIR_INGRESS,
                                                upstream_port);
-                       if (err)
-                               return err;
+               if (err && err != -EOPNOTSUPP)
+                       return err;
 
-                       err = chip->info->ops->set_egress_port(chip,
+               err = mv88e6xxx_set_egress_port(chip,
                                                MV88E6XXX_EGRESS_DIR_EGRESS,
                                                upstream_port);
-                       if (err)
-                               return err;
-               }
+               if (err && err != -EOPNOTSUPP)
+                       return err;
        }
 
        return 0;
@@ -2670,15 +2800,20 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
                        return err;
        }
 
-       /* Port Association Vector: when learning source addresses
-        * of packets, add the address to the address database using
-        * a port bitmap that has only the bit for this port set and
-        * the other bits clear.
+       /* Port Association Vector: disable automatic address learning
+        * on all user ports since they start out in standalone
+        * mode. When joining a bridge, learning will be configured to
+        * match the bridge port settings. Enable learning on all
+        * DSA/CPU ports. NOTE: FROM_CPU frames always bypass the
+        * learning process.
+        *
+        * Disable HoldAt1, IntOnAgeOut, LockedPort, IgnoreWrongData,
+        * and RefreshLocked. I.e. setup standard automatic learning.
         */
-       reg = 1 << port;
-       /* Disable learning for CPU port */
-       if (dsa_is_cpu_port(ds, port))
+       if (dsa_is_user_port(ds, port))
                reg = 0;
+       else
+               reg = 1 << port;
 
        err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
                                   reg);
@@ -4566,6 +4701,70 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
        .phylink_validate = mv88e6390x_phylink_validate,
 };
 
+static const struct mv88e6xxx_ops mv88e6393x_ops = {
+       /* MV88E6XXX_FAMILY_6393 */
+       .setup_errata = mv88e6393x_serdes_setup_errata,
+       .irl_init_all = mv88e6390_g2_irl_init_all,
+       .get_eeprom = mv88e6xxx_g2_get_eeprom8,
+       .set_eeprom = mv88e6xxx_g2_set_eeprom8,
+       .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
+       .phy_read = mv88e6xxx_g2_smi_phy_read,
+       .phy_write = mv88e6xxx_g2_smi_phy_write,
+       .port_set_link = mv88e6xxx_port_set_link,
+       .port_sync_link = mv88e6xxx_port_sync_link,
+       .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
+       .port_set_speed_duplex = mv88e6393x_port_set_speed_duplex,
+       .port_max_speed_mode = mv88e6393x_port_max_speed_mode,
+       .port_tag_remap = mv88e6390_port_tag_remap,
+       .port_set_policy = mv88e6393x_port_set_policy,
+       .port_set_frame_mode = mv88e6351_port_set_frame_mode,
+       .port_set_ucast_flood = mv88e6352_port_set_ucast_flood,
+       .port_set_mcast_flood = mv88e6352_port_set_mcast_flood,
+       .port_set_ether_type = mv88e6393x_port_set_ether_type,
+       .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
+       .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+       .port_pause_limit = mv88e6390_port_pause_limit,
+       .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
+       .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
+       .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6393x_port_set_cmode,
+       .port_setup_message_port = mv88e6xxx_setup_message_port,
+       .port_set_upstream_port = mv88e6393x_port_set_upstream_port,
+       .stats_snapshot = mv88e6390_g1_stats_snapshot,
+       .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
+       .stats_get_sset_count = mv88e6320_stats_get_sset_count,
+       .stats_get_strings = mv88e6320_stats_get_strings,
+       .stats_get_stats = mv88e6390_stats_get_stats,
+       /* .set_cpu_port is missing because this family does not support a global
+        * CPU port, only per port CPU port which is set via
+        * .port_set_upstream_port method.
+        */
+       .set_egress_port = mv88e6393x_set_egress_port,
+       .watchdog_ops = &mv88e6390_watchdog_ops,
+       .mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu,
+       .pot_clear = mv88e6xxx_g2_pot_clear,
+       .reset = mv88e6352_g1_reset,
+       .rmu_disable = mv88e6390_g1_rmu_disable,
+       .atu_get_hash = mv88e6165_g1_atu_get_hash,
+       .atu_set_hash = mv88e6165_g1_atu_set_hash,
+       .vtu_getnext = mv88e6390_g1_vtu_getnext,
+       .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
+       .serdes_power = mv88e6393x_serdes_power,
+       .serdes_get_lane = mv88e6393x_serdes_get_lane,
+       .serdes_pcs_get_state = mv88e6393x_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
+       .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
+       .serdes_irq_enable = mv88e6393x_serdes_irq_enable,
+       .serdes_irq_status = mv88e6393x_serdes_irq_status,
+       /* TODO: serdes stats */
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6390_avb_ops,
+       .ptp_ops = &mv88e6352_ptp_ops,
+       .phylink_validate = mv88e6393x_phylink_validate,
+};
+
 static const struct mv88e6xxx_info mv88e6xxx_table[] = {
        [MV88E6085] = {
                .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085,
@@ -4937,6 +5136,52 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .ops = &mv88e6191_ops,
        },
 
+       [MV88E6191X] = {
+               .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6191X,
+               .family = MV88E6XXX_FAMILY_6393,
+               .name = "Marvell 88E6191X",
+               .num_databases = 4096,
+               .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 9,
+               .max_vid = 8191,
+               .port_base_addr = 0x0,
+               .phy_base_addr = 0x0,
+               .global1_addr = 0x1b,
+               .global2_addr = 0x1c,
+               .age_time_coeff = 3750,
+               .g1_irqs = 10,
+               .g2_irqs = 14,
+               .atu_move_port_mask = 0x1f,
+               .pvt = true,
+               .multi_chip = true,
+               .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
+               .ops = &mv88e6393x_ops,
+       },
+
+       [MV88E6193X] = {
+               .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6193X,
+               .family = MV88E6XXX_FAMILY_6393,
+               .name = "Marvell 88E6193X",
+               .num_databases = 4096,
+               .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 9,
+               .max_vid = 8191,
+               .port_base_addr = 0x0,
+               .phy_base_addr = 0x0,
+               .global1_addr = 0x1b,
+               .global2_addr = 0x1c,
+               .age_time_coeff = 3750,
+               .g1_irqs = 10,
+               .g2_irqs = 14,
+               .atu_move_port_mask = 0x1f,
+               .pvt = true,
+               .multi_chip = true,
+               .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
+               .ops = &mv88e6393x_ops,
+       },
+
        [MV88E6220] = {
                .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6220,
                .family = MV88E6XXX_FAMILY_6250,
@@ -5227,6 +5472,29 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .ptp_support = true,
                .ops = &mv88e6390x_ops,
        },
+
+       [MV88E6393X] = {
+               .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6393X,
+               .family = MV88E6XXX_FAMILY_6393,
+               .name = "Marvell 88E6393X",
+               .num_databases = 4096,
+               .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 9,
+               .max_vid = 8191,
+               .port_base_addr = 0x0,
+               .phy_base_addr = 0x0,
+               .global1_addr = 0x1b,
+               .global2_addr = 0x1c,
+               .age_time_coeff = 3750,
+               .g1_irqs = 10,
+               .g2_irqs = 14,
+               .atu_move_port_mask = 0x1f,
+               .pvt = true,
+               .multi_chip = true,
+               .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
+               .ops = &mv88e6393x_ops,
+       },
 };
 
 static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
@@ -5334,9 +5602,6 @@ static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port,
        int i;
        int err;
 
-       if (!chip->info->ops->set_egress_port)
-               return -EOPNOTSUPP;
-
        mutex_lock(&chip->reg_lock);
        if ((ingress ? chip->ingress_dest_port : chip->egress_dest_port) !=
            mirror->to_local_port) {
@@ -5351,9 +5616,8 @@ static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port,
                        goto out;
                }
 
-               err = chip->info->ops->set_egress_port(chip,
-                                                      direction,
-                                                      mirror->to_local_port);
+               err = mv88e6xxx_set_egress_port(chip, direction,
+                                               mirror->to_local_port);
                if (err)
                        goto out;
        }
@@ -5386,10 +5650,8 @@ static void mv88e6xxx_port_mirror_del(struct dsa_switch *ds, int port,
 
        /* Reset egress port when no other mirror is active */
        if (!other_mirrors) {
-               if (chip->info->ops->set_egress_port(chip,
-                                                    direction,
-                                                    dsa_upstream_port(ds,
-                                                                      port)))
+               if (mv88e6xxx_set_egress_port(chip, direction,
+                                             dsa_upstream_port(ds, port)))
                        dev_err(ds->dev, "failed to set egress port\n");
        }
 
@@ -5403,7 +5665,8 @@ static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
        struct mv88e6xxx_chip *chip = ds->priv;
        const struct mv88e6xxx_ops *ops;
 
-       if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
+       if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
+                          BR_BCAST_FLOOD))
                return -EINVAL;
 
        ops = chip->info->ops;
@@ -5422,10 +5685,23 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
                                       struct netlink_ext_ack *extack)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
+       bool do_fast_age = false;
        int err = -EOPNOTSUPP;
 
        mv88e6xxx_reg_lock(chip);
 
+       if (flags.mask & BR_LEARNING) {
+               bool learning = !!(flags.val & BR_LEARNING);
+               u16 pav = learning ? (1 << port) : 0;
+
+               err = mv88e6xxx_port_set_assoc_vector(chip, port, pav);
+               if (err)
+                       goto out;
+
+               if (!learning)
+                       do_fast_age = true;
+       }
+
        if (flags.mask & BR_FLOOD) {
                bool unicast = !!(flags.val & BR_FLOOD);
 
@@ -5444,9 +5720,20 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
                        goto out;
        }
 
+       if (flags.mask & BR_BCAST_FLOOD) {
+               bool broadcast = !!(flags.val & BR_BCAST_FLOOD);
+
+               err = mv88e6xxx_port_broadcast_sync(chip, port, broadcast);
+               if (err)
+                       goto out;
+       }
+
 out:
        mv88e6xxx_reg_unlock(chip);
 
+       if (do_fast_age)
+               mv88e6xxx_port_fast_age(ds, port);
+
        return err;
 }