From f3645652216839aabcbd864b81b25ba7fbab9e82 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:07 -0400 Subject: net: dsa: mv88e6xxx: move PVT description in info Not all Marvell switch chips feature a Cross-chip Port VLAN Table (PVT). Chips with a PVT use the same implementation, so a new mv88e6xxx_ops member won't be necessary yet. Add a "pvt" boolean member to the mv88e6xxx_info structure and kill the obsolete MV88E6XXX_FLAGS_PVT flag. Add a mv88e6xxx_has_pvt helper to wrap future checks of that condition. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 22 ++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global2.c | 2 +- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 32 +++++++++++--------------------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3c946af1159d..8f1f881d0375 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3578,6 +3578,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 +3611,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 +3628,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 +3660,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 +3677,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 +3694,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 +3711,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 +3728,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 +3745,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 +3762,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 +3795,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 +3812,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 +3829,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 +3846,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 +3863,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 +3880,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 +3912,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 +3929,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 +3946,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 +3963,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 +3979,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 +3995,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, diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 132559d46b95..6228aab2ad35 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -966,7 +966,7 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) return err; } - if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) { + if (mv88e6xxx_has_pvt(chip)) { /* Initialize Cross-chip Port VLAN Table to reset defaults */ err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_OP_INIT_ONES); diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 55367d05374e..97dd3e2d2a56 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -527,8 +527,6 @@ enum mv88e6xxx_cap { MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ MV88E6XXX_CAP_G2_IRL_CMD, /* (0x09) Ingress Rate Command */ MV88E6XXX_CAP_G2_IRL_DATA, /* (0x0a) Ingress Rate Data */ - MV88E6XXX_CAP_G2_PVT_ADDR, /* (0x0b) Cross Chip Port VLAN Addr */ - MV88E6XXX_CAP_G2_PVT_DATA, /* (0x0c) Cross Chip Port VLAN Data */ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ /* Per VLAN Spanning Tree Unit (STU). @@ -561,8 +559,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X) #define MV88E6XXX_FLAG_G2_IRL_CMD BIT_ULL(MV88E6XXX_CAP_G2_IRL_CMD) #define MV88E6XXX_FLAG_G2_IRL_DATA BIT_ULL(MV88E6XXX_CAP_G2_IRL_DATA) -#define MV88E6XXX_FLAG_G2_PVT_ADDR BIT_ULL(MV88E6XXX_CAP_G2_PVT_ADDR) -#define MV88E6XXX_FLAG_G2_PVT_DATA BIT_ULL(MV88E6XXX_CAP_G2_PVT_DATA) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) #define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU) @@ -578,11 +574,6 @@ enum mv88e6xxx_cap { (MV88E6XXX_FLAG_SMI_CMD | \ MV88E6XXX_FLAG_SMI_DATA) -/* Cross-chip Port VLAN Table */ -#define MV88E6XXX_FLAGS_PVT \ - (MV88E6XXX_FLAG_G2_PVT_ADDR | \ - MV88E6XXX_FLAG_G2_PVT_DATA) - /* Fiber/SERDES Registers at SMI address F, page 1 */ #define MV88E6XXX_FLAGS_SERDES \ (MV88E6XXX_FLAG_PHY_PAGE | \ @@ -604,8 +595,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6165 \ (MV88E6XXX_FLAG_G1_VTU_FID | \ @@ -617,8 +607,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6185 \ (MV88E6XXX_FLAG_GLOBAL2 | \ @@ -635,8 +624,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ @@ -648,7 +636,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT | \ MV88E6XXX_FLAGS_SERDES) #define MV88E6XXX_FLAGS_FAMILY_6351 \ @@ -661,8 +648,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ @@ -676,7 +662,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT | \ MV88E6XXX_FLAGS_SERDES) #define MV88E6XXX_FLAGS_FAMILY_6390 \ @@ -686,8 +671,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; @@ -701,6 +685,7 @@ struct mv88e6xxx_info { unsigned int global1_addr; unsigned int age_time_coeff; unsigned int g1_irqs; + bool pvt; enum dsa_tag_protocol tag_protocol; unsigned long long flags; @@ -936,6 +921,11 @@ static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip, return (chip->info->flags & flags) == flags; } +static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip) +{ + return chip->info->pvt; +} + static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip) { return chip->info->num_databases; -- cgit v1.2.3 From 812289960f720c4a075f8766d40a3c6b5840c0cd Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:08 -0400 Subject: net: dsa: mv88e6xxx: use 4-bit port for PVT data The Cross-chip Port Based VLAN Table (PVT) supports two indexing modes, one using 5-bit for device and 4-bit for port, the other using 4-bit for device and 5-bit for port, configured via the Global 2 Misc register. Only 4 bits for the source port are needed when interconnecting 88E6xxx switch devices since they all support less than 16 physical ports. The full 5 bits are needed when interconnecting a device with 98DXxxx switch devices since they support more than 16 physical ports. Add a mv88e6xxx_pvt_setup helper to set the 4-bit port PVT mode, which will be extended later to also initialize the PVT content. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 15 +++++++++++++++ drivers/net/dsa/mv88e6xxx/global2.c | 25 +++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global2.h | 7 +++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + 4 files changed, 48 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8f1f881d0375..2a32bb490f92 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1198,6 +1198,17 @@ static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_atu_set_age_time(chip, 300000); } +static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip) +{ + 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. + */ + return mv88e6xxx_g2_misc_4_bit_port(chip); +} + static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; @@ -2594,6 +2605,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; diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 6228aab2ad35..d64f4c15ccb7 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -784,6 +784,31 @@ static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) return err; } +/* Offset 0x1D: Misc Register */ + +static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip, + bool port_5_bit) +{ + u16 val; + int err; + + err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val); + if (err) + return err; + + if (port_5_bit) + val |= GLOBAL2_MISC_5_BIT_PORT; + else + val &= ~GLOBAL2_MISC_5_BIT_PORT; + + return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val); +} + +int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_misc_5_bit_port(chip, false); +} + static void mv88e6xxx_g2_irq_mask(struct irq_data *d) { struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index f8b6dd93213a..71fb2ff541ba 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -42,6 +42,8 @@ int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip); + int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); @@ -110,6 +112,11 @@ static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, return -EOPNOTSUPP; } +int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) +{ + return -EOPNOTSUPP; +} + static inline int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) { return -EOPNOTSUPP; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 97dd3e2d2a56..bcaa55b20f5a 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -439,6 +439,7 @@ #define GLOBAL2_WDOG_FORCE_IRQ BIT(0) #define GLOBAL2_QOS_WEIGHT 0x1c #define GLOBAL2_MISC 0x1d +#define GLOBAL2_MISC_5_BIT_PORT BIT(14) #define MV88E6XXX_N_FID 4096 -- cgit v1.2.3 From 17a1594e2d58f021d629da72d1a82b1441217460 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:09 -0400 Subject: net: dsa: mv88e6xxx: program the PVT with all ones The Cross-chip Port Based VLAN Table (PVT) is currently initialized with all ones, allowing any external ports to egress frames on local ports. This commit implements the PVT access functions and programs the PVT with all ones for the local switch ports only, instead of using the Init operation. The current behavior is unchanged for the moment. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 31 ++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/global2.c | 52 +++++++++++++++++++++++++++++------ drivers/net/dsa/mv88e6xxx/global2.h | 8 ++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 4 +++ 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2a32bb490f92..fb6a723c2137 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1198,15 +1198,44 @@ 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_mask(chip); + + 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. */ - return mv88e6xxx_g2_misc_4_bit_port(chip); + 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) diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index d64f4c15ccb7..7c6bc33a9516 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -172,6 +172,50 @@ static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip) return err; } +/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register + * Offset 0x0C: Cross-chip Port VLAN Data Register + */ + +static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY); +} + +static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 op) +{ + int err; + + /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared, + * source device is 5-bit, source port is 4-bit. + */ + op |= (src_dev & 0x1f) << 4; + op |= (src_port & 0xf); + + err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op); + if (err) + return err; + + return mv88e6xxx_g2_pvt_op_wait(chip); +} + +int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data) +{ + int err; + + err = mv88e6xxx_g2_pvt_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data); + if (err) + return err; + + return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port, + GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN); +} + /* Offset 0x0D: Switch MAC/WoL/WoF register */ static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, @@ -991,14 +1035,6 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) return err; } - if (mv88e6xxx_has_pvt(chip)) { - /* Initialize Cross-chip Port VLAN Table to reset defaults */ - err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, - GLOBAL2_PVT_ADDR_OP_INIT_ONES); - if (err) - return err; - } - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) { /* Clear the priority override table. */ err = mv88e6xxx_g2_clear_pot(chip); diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 71fb2ff541ba..96046bb12ca1 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -42,6 +42,8 @@ int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data); int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); @@ -112,6 +114,12 @@ static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, return -EOPNOTSUPP; } +int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data) +{ + return -EOPNOTSUPP; +} + int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) { return -EOPNOTSUPP; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index bcaa55b20f5a..c8f54986996b 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -443,6 +443,10 @@ #define MV88E6XXX_N_FID 4096 +/* PVT limits for 4-bit port and 5-bit switch */ +#define MV88E6XXX_MAX_PVT_SWITCHES 32 +#define MV88E6XXX_MAX_PVT_PORTS 16 + enum mv88e6xxx_frame_mode { MV88E6XXX_FRAME_MODE_NORMAL, MV88E6XXX_FRAME_MODE_DSA, -- cgit v1.2.3 From 73b1204d070d2970749beb3fa3ca6e639dc7a3b6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:10 -0400 Subject: net: dsa: mv88e6xxx: allocate the number of ports The current code allocates DSA_MAX_PORTS ports for a Marvell dsa_switch structure. Provide the exact number of ports so the corresponding ds->num_ports is accurate. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index fb6a723c2137..28bdfadbf050 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4286,7 +4286,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; -- cgit v1.2.3 From e5887a2a110947cdd936fcc8722d8f08f3e8e392 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:11 -0400 Subject: net: dsa: mv88e6xxx: rework in-chip bridging All ports -- internal and external, for chips featuring a PVT -- have a mask restricting to which internal ports a frame is allowed to egress. Now that DSA exposes the number of ports and their bridge devices, it is possible to extract the code generating the VLAN map and make it generic so that it can be shared later with the cross-chip bridging code. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 49 ++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 28bdfadbf050..9b2d369715d7 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_based_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); -- cgit v1.2.3 From 240ea3ef70a4591ba0f77cfafe4b467d55ce80d0 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:12 -0400 Subject: net: dsa: mv88e6xxx: factorize in-chip bridge map Factorize the code in the DSA port_bridge_{join,leave} routines used to program the port VLAN map of all local ports of a given bridge group. At the same time shorten the _mv88e6xxx_port_based_vlan_map to get rid of the old underscore prefix naming convention. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 47 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 9b2d369715d7..3802e1bdd111 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1156,7 +1156,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) return pvlan; } -static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_port_vlan_map(struct mv88e6xxx_chip *chip, int port) { u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port); @@ -2140,23 +2140,32 @@ 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); + int port; + 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; } } + 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; @@ -2166,17 +2175,11 @@ 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); - - /* 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"); - + 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); } @@ -2490,7 +2493,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; -- cgit v1.2.3 From e96a6e027510279a5089b24e2f217693488327f3 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:13 -0400 Subject: net: dsa: mv88e6xxx: remap existing bridge members When a local port of a switch chip becomes a member of a bridge group, we need to reprogram the Cross-chip Port Based VLAN Table (PVT) to allow existing cross-chip bridge members to egress frames on the new ports. There is no functional changes yet, since the PVT is still programmed with all ones, allowing any external port to egress frames locally. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3802e1bdd111..c6f45a2a9335 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2143,7 +2143,9 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, struct net_device *br) { + struct dsa_switch *ds; int port; + int dev; int err; /* Remap the Port VLAN of each local bridge group member */ @@ -2155,6 +2157,24 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, } } + 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; } -- cgit v1.2.3 From 40ef2c93395fcd25051643707d0956ce9120de41 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:14 -0400 Subject: net: dsa: add cross-chip bridging operations Introduce crosschip_bridge_{join,leave} operations in the dsa_switch_ops structure, which can be used by switches supporting interconnection. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 8 ++++++++ net/dsa/switch.c | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 951b5e49e899..ffe56cc338fe 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -456,6 +456,14 @@ struct dsa_switch_ops { bool ingress); void (*port_mirror_del)(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); + + /* + * Cross-chip operations + */ + int (*crosschip_bridge_join)(struct dsa_switch *ds, int sw_index, + int port, struct net_device *br); + void (*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index, + int port, struct net_device *br); }; struct dsa_switch_driver { diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 7b6f38e5fef6..ca6e26e514f0 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -20,9 +20,9 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds, if (ds->index == info->sw_index && ds->ops->port_bridge_join) return ds->ops->port_bridge_join(ds, info->port, info->br); - if (ds->index != info->sw_index) - dev_dbg(ds->dev, "crosschip DSA port %d.%d bridged to %s\n", - info->sw_index, info->port, netdev_name(info->br)); + if (ds->index != info->sw_index && ds->ops->crosschip_bridge_join) + return ds->ops->crosschip_bridge_join(ds, info->sw_index, + info->port, info->br); return 0; } @@ -33,9 +33,9 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, if (ds->index == info->sw_index && ds->ops->port_bridge_leave) ds->ops->port_bridge_leave(ds, info->port, info->br); - if (ds->index != info->sw_index) - dev_dbg(ds->dev, "crosschip DSA port %d.%d unbridged from %s\n", - info->sw_index, info->port, netdev_name(info->br)); + if (ds->index != info->sw_index && ds->ops->crosschip_bridge_leave) + ds->ops->crosschip_bridge_leave(ds, info->sw_index, info->port, + info->br); return 0; } -- cgit v1.2.3 From aec5ac88d31b385d851f5f29939e556a07ca7d3d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:15 -0400 Subject: net: dsa: mv88e6xxx: add cross-chip bridging Implement the DSA cross-chip bridging operations by remapping the local ports an external source port can egress frames to, when this cross-chip port joins or leaves a bridge. The PVT is no longer configured with all ones allowing any external frame to egress any local port. Only DSA and CPU ports, as well as bridge group members, can egress frames on local ports. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c6f45a2a9335..44ba8cff5631 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1222,7 +1222,7 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) /* Skip the local source device, which uses in-chip port VLAN */ if (dev != chip->ds->index) - pvlan = mv88e6xxx_port_mask(chip); + pvlan = mv88e6xxx_port_vlan(chip, dev, port); return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan); } @@ -2203,6 +2203,36 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } +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); +} + static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip) { if (chip->info->ops->reset) @@ -4313,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 = { -- cgit v1.2.3