summaryrefslogtreecommitdiff
path: root/drivers/net/dsa/mv88e6xxx/chip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx/chip.c')
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c210
1 files changed, 173 insertions, 37 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 3c946af1159d..44ba8cff5631 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1123,27 +1123,42 @@ out:
return err;
}
-static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
+static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)
{
- struct dsa_switch *ds = chip->ds;
- struct net_device *bridge = ds->ports[port].bridge_dev;
- u16 output_ports = 0;
+ struct dsa_switch *ds = NULL;
+ struct net_device *br;
+ u16 pvlan;
int i;
- /* allow CPU port or DSA link(s) to send frames to every port */
- if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
- output_ports = ~0;
- } else {
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- /* allow sending frames to every group member */
- if (bridge && ds->ports[i].bridge_dev == bridge)
- output_ports |= BIT(i);
+ if (dev < DSA_MAX_SWITCHES)
+ ds = chip->ds->dst->ds[dev];
- /* allow sending frames to CPU port and DSA link(s) */
- if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
- output_ports |= BIT(i);
- }
- }
+ /* Prevent frames from unknown switch or port */
+ if (!ds || port >= ds->num_ports)
+ return 0;
+
+ /* Frames from DSA links and CPU ports can egress any local port */
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return mv88e6xxx_port_mask(chip);
+
+ br = ds->ports[port].bridge_dev;
+ pvlan = 0;
+
+ /* Frames from user ports can egress any local DSA links and CPU ports,
+ * as well as any local member of their bridge group.
+ */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
+ if (dsa_is_cpu_port(chip->ds, i) ||
+ dsa_is_dsa_port(chip->ds, i) ||
+ (br && chip->ds->ports[i].bridge_dev == br))
+ pvlan |= BIT(i);
+
+ return pvlan;
+}
+
+static int mv88e6xxx_port_vlan_map(struct mv88e6xxx_chip *chip, int port)
+{
+ u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port);
/* prevent frames from going back out of the port they came in on */
output_ports &= ~BIT(port);
@@ -1198,6 +1213,46 @@ static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip)
return mv88e6xxx_g1_atu_set_age_time(chip, 300000);
}
+static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
+{
+ u16 pvlan = 0;
+
+ if (!mv88e6xxx_has_pvt(chip))
+ return -EOPNOTSUPP;
+
+ /* Skip the local source device, which uses in-chip port VLAN */
+ if (dev != chip->ds->index)
+ pvlan = mv88e6xxx_port_vlan(chip, dev, port);
+
+ return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
+}
+
+static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
+{
+ int dev, port;
+ int err;
+
+ if (!mv88e6xxx_has_pvt(chip))
+ return 0;
+
+ /* Clear 5 Bit Port for usage with Marvell Link Street devices:
+ * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev.
+ */
+ err = mv88e6xxx_g2_misc_4_bit_port(chip);
+ if (err)
+ return err;
+
+ for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; ++dev) {
+ for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; ++port) {
+ err = mv88e6xxx_pvt_map(chip, dev, port);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_chip *chip = ds->priv;
@@ -2085,23 +2140,52 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
return err;
}
-static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
- struct net_device *br)
+static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
+ struct net_device *br)
{
- struct mv88e6xxx_chip *chip = ds->priv;
- int i, err = 0;
-
- mutex_lock(&chip->reg_lock);
+ struct dsa_switch *ds;
+ int port;
+ int dev;
+ int err;
- /* Remap each port's VLANTable */
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- if (ds->ports[i].bridge_dev == br) {
- err = _mv88e6xxx_port_based_vlan_map(chip, i);
+ /* Remap the Port VLAN of each local bridge group member */
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
+ if (chip->ds->ports[port].bridge_dev == br) {
+ err = mv88e6xxx_port_vlan_map(chip, port);
if (err)
- break;
+ return err;
+ }
+ }
+
+ if (!mv88e6xxx_has_pvt(chip))
+ return 0;
+
+ /* Remap the Port VLAN of each cross-chip bridge group member */
+ for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) {
+ ds = chip->ds->dst->ds[dev];
+ if (!ds)
+ break;
+
+ for (port = 0; port < ds->num_ports; ++port) {
+ if (ds->ports[port].bridge_dev == br) {
+ err = mv88e6xxx_pvt_map(chip, dev, port);
+ if (err)
+ return err;
+ }
}
}
+ return 0;
+}
+
+static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *br)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_bridge_map(chip, br);
mutex_unlock(&chip->reg_lock);
return err;
@@ -2111,17 +2195,41 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
struct net_device *br)
{
struct mv88e6xxx_chip *chip = ds->priv;
- int i;
mutex_lock(&chip->reg_lock);
+ if (mv88e6xxx_bridge_map(chip, br) ||
+ mv88e6xxx_port_vlan_map(chip, port))
+ dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");
+ mutex_unlock(&chip->reg_lock);
+}
- /* Remap each port's VLANTable */
- for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
- if (i == port || ds->ports[i].bridge_dev == br)
- if (_mv88e6xxx_port_based_vlan_map(chip, i))
- netdev_warn(ds->ports[i].netdev,
- "failed to remap\n");
+static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev,
+ int port, struct net_device *br)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ if (!mv88e6xxx_has_pvt(chip))
+ return 0;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_pvt_map(chip, dev, port);
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev,
+ int port, struct net_device *br)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ if (!mv88e6xxx_has_pvt(chip))
+ return;
+
+ mutex_lock(&chip->reg_lock);
+ if (mv88e6xxx_pvt_map(chip, dev, port))
+ dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n");
mutex_unlock(&chip->reg_lock);
}
@@ -2435,7 +2543,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
if (err)
return err;
- err = _mv88e6xxx_port_based_vlan_map(chip, port);
+ err = mv88e6xxx_port_vlan_map(chip, port);
if (err)
return err;
@@ -2594,6 +2702,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
goto unlock;
}
+ err = mv88e6xxx_pvt_setup(chip);
+ if (err)
+ goto unlock;
+
err = mv88e6xxx_atu_setup(chip);
if (err)
goto unlock;
@@ -3578,6 +3690,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 8,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
.ops = &mv88e6085_ops,
@@ -3610,6 +3723,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 8,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
.ops = &mv88e6097_ops,
@@ -3626,6 +3740,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
.ops = &mv88e6123_ops,
@@ -3657,6 +3772,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.global1_addr = 0x1b,
.age_time_coeff = 3750,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6341,
.ops = &mv88e6141_ops,
@@ -3673,6 +3789,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
.ops = &mv88e6161_ops,
@@ -3689,6 +3806,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
.ops = &mv88e6165_ops,
@@ -3705,6 +3823,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6171_ops,
@@ -3721,6 +3840,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6172_ops,
@@ -3737,6 +3857,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6175_ops,
@@ -3753,6 +3874,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6176_ops,
@@ -3785,6 +3907,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.tag_protocol = DSA_TAG_PROTO_DSA,
.age_time_coeff = 3750,
.g1_irqs = 9,
+ .pvt = true,
.atu_move_port_mask = 0x1f,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6190_ops,
@@ -3801,6 +3924,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 3750,
.g1_irqs = 9,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6190x_ops,
@@ -3817,6 +3941,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 3750,
.g1_irqs = 9,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6191_ops,
@@ -3833,6 +3958,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6240_ops,
@@ -3849,6 +3975,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 3750,
.g1_irqs = 9,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6290_ops,
@@ -3865,6 +3992,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 8,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
.ops = &mv88e6320_ops,
@@ -3896,6 +4024,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.global1_addr = 0x1b,
.age_time_coeff = 3750,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6341,
.ops = &mv88e6341_ops,
@@ -3912,6 +4041,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6350_ops,
@@ -3928,6 +4058,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
.ops = &mv88e6351_ops,
@@ -3944,6 +4075,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 15000,
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
.ops = &mv88e6352_ops,
@@ -3959,6 +4091,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 3750,
.g1_irqs = 9,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6390_ops,
@@ -3974,6 +4107,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.age_time_coeff = 3750,
.g1_irqs = 9,
.atu_move_port_mask = 0x1f,
+ .pvt = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.flags = MV88E6XXX_FLAGS_FAMILY_6390,
.ops = &mv88e6390x_ops,
@@ -4209,6 +4343,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.port_mdb_add = mv88e6xxx_port_mdb_add,
.port_mdb_del = mv88e6xxx_port_mdb_del,
.port_mdb_dump = mv88e6xxx_port_mdb_dump,
+ .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
+ .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
};
static struct dsa_switch_driver mv88e6xxx_switch_drv = {
@@ -4220,7 +4356,7 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
struct device *dev = chip->dev;
struct dsa_switch *ds;
- ds = dsa_switch_alloc(dev, DSA_MAX_PORTS);
+ ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip));
if (!ds)
return -ENOMEM;