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
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);
}
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);
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);
}
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)
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);
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,
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;
}
/* 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)
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;
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;
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);
}
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;
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)
{
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);
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;
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;
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,
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;
}
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);
}
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;
}
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;
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) {
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;
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;
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);
.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,
.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,
.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)
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) {
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;
}
/* 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");
}
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;
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);
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;
}