diff options
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 81 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 8 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.c | 35 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.h | 16 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_atu.c | 5 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_vtu.c | 58 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.c | 26 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.h | 14 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.c | 77 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.h | 14 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/smi.c | 25 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105.h | 20 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_dynamic_config.c | 144 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_dynamic_config.h | 11 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_main.c | 197 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_spi.c | 12 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_static_config.c | 20 | ||||
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_static_config.h | 26 |
18 files changed, 727 insertions, 62 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 063c7a671b41..4b2f8d6f0744 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -825,6 +825,12 @@ static int mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip, STATS_TYPE_BANK0 | STATS_TYPE_PORT); } +static int mv88e6250_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) +{ + return mv88e6xxx_stats_get_strings(chip, data, STATS_TYPE_BANK0); +} + static int mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, uint8_t *data) { @@ -895,6 +901,11 @@ static int mv88e6095_stats_get_sset_count(struct mv88e6xxx_chip *chip) STATS_TYPE_PORT); } +static int mv88e6250_stats_get_sset_count(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0); +} + static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) { return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | @@ -962,6 +973,13 @@ static int mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port, 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); } +static int mv88e6250_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + return mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0, + 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); +} + static int mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data) { @@ -3444,6 +3462,44 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .phylink_validate = mv88e6352_phylink_validate, }; +static const struct mv88e6xxx_ops mv88e6250_ops = { + /* MV88E6XXX_FAMILY_6250 */ + .ieee_pri_map = mv88e6250_g1_ieee_pri_map, + .ip_pri_map = mv88e6085_g1_ip_pri_map, + .irl_init_all = mv88e6352_g2_irl_init_all, + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .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_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6250_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, + .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_limit = mv88e6097_port_pause_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6250_port_link_state, + .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6250_stats_get_sset_count, + .stats_get_strings = mv88e6250_stats_get_strings, + .stats_get_stats = mv88e6250_stats_get_stats, + .set_cpu_port = mv88e6095_g1_set_cpu_port, + .set_egress_port = mv88e6095_g1_set_egress_port, + .watchdog_ops = &mv88e6250_watchdog_ops, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, + .reset = mv88e6250_g1_reset, + .vtu_getnext = mv88e6250_g1_vtu_getnext, + .vtu_loadpurge = mv88e6250_g1_vtu_loadpurge, + .phylink_validate = mv88e6065_phylink_validate, +}; + static const struct mv88e6xxx_ops mv88e6290_ops = { /* MV88E6XXX_FAMILY_6390 */ .setup_errata = mv88e6390_setup_errata, @@ -4229,6 +4285,27 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6240_ops, }, + [MV88E6250] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6250, + .family = MV88E6XXX_FAMILY_6250, + .name = "Marvell 88E6250", + .num_databases = 64, + .num_ports = 7, + .num_internal_phys = 5, + .max_vid = 4095, + .port_base_addr = 0x08, + .phy_base_addr = 0x00, + .global1_addr = 0x0f, + .global2_addr = 0x07, + .age_time_coeff = 15000, + .g1_irqs = 9, + .g2_irqs = 10, + .atu_move_port_mask = 0xf, + .dual_chip = true, + .tag_protocol = DSA_TAG_PROTO_DSA, + .ops = &mv88e6250_ops, + }, + [MV88E6290] = { .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290, .family = MV88E6XXX_FAMILY_6390, @@ -4837,6 +4914,10 @@ static const struct of_device_id mv88e6xxx_of_match[] = { .compatible = "marvell,mv88e6190", .data = &mv88e6xxx_table[MV88E6190], }, + { + .compatible = "marvell,mv88e6250", + .data = &mv88e6xxx_table[MV88E6250], + }, { /* sentinel */ }, }; diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index d3e10111a6fe..a3121a12bafc 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -58,6 +58,7 @@ enum mv88e6xxx_model { MV88E6190X, MV88E6191, MV88E6240, + MV88E6250, MV88E6290, MV88E6320, MV88E6321, @@ -76,6 +77,7 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */ MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ + MV88E6XXX_FAMILY_6250, /* 6250 */ MV88E6XXX_FAMILY_6320, /* 6320 6321 */ MV88E6XXX_FAMILY_6341, /* 6141 6341 */ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ @@ -108,6 +110,12 @@ struct mv88e6xxx_info { * when it is non-zero, and use indirect access to internal registers. */ bool multi_chip; + /* Dual-chip Addressing Mode + * Some chips respond to only half of the 32 SMI addresses, + * allowing two to coexist on the same SMI interface. + */ + bool dual_chip; + enum dsa_tag_protocol tag_protocol; /* Mask for FromPort and ToPort value of PortVec used in ATU Move diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 09b8a3d0dd37..1323ef30a5e9 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -178,7 +178,7 @@ int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip) return mv88e6185_g1_wait_ppu_polling(chip); } -int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) +int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip) { u16 val; int err; @@ -194,7 +194,14 @@ int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) if (err) return err; - err = mv88e6xxx_g1_wait_init_ready(chip); + return mv88e6xxx_g1_wait_init_ready(chip); +} + +int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) +{ + int err; + + err = mv88e6250_g1_reset(chip); if (err) return err; @@ -295,6 +302,12 @@ int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa41); } +int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip) +{ + /* Reset the IEEE Tag priorities to defaults */ + return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa50); +} + /* Offset 0x1a: Monitor Control */ /* Offset 0x1a: Monitor & MGMT Control on some devices */ @@ -375,26 +388,26 @@ int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) u16 ptr; int err; - /* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */ - ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XLO; + /* 01:80:c2:00:00:00-01:80:c2:00:00:07 are Management */ + ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200000XLO; err = mv88e6390_g1_monitor_write(chip, ptr, 0xff); if (err) return err; - /* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */ - ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XHI; + /* 01:80:c2:00:00:08-01:80:c2:00:00:0f are Management */ + ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200000XHI; err = mv88e6390_g1_monitor_write(chip, ptr, 0xff); if (err) return err; - /* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */ - ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XLO; + /* 01:80:c2:00:00:20-01:80:c2:00:00:27 are Management */ + ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200002XLO; err = mv88e6390_g1_monitor_write(chip, ptr, 0xff); if (err) return err; - /* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */ - ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XHI; + /* 01:80:c2:00:00:28-01:80:c2:00:00:2f are Management */ + ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200002XHI; err = mv88e6390_g1_monitor_write(chip, ptr, 0xff); if (err) return err; @@ -461,7 +474,7 @@ int mv88e6xxx_g1_set_device_number(struct mv88e6xxx_chip *chip, int index) /* Offset 0x1d: Statistics Operation 2 */ -int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) +static int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_STATS_OP, MV88E6XXX_G1_STATS_OP_BUSY); diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 7bd5ab733a3f..d444266f7d78 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -186,10 +186,10 @@ #define MV88E6390_G1_MONITOR_MGMT_CTL 0x1a #define MV88E6390_G1_MONITOR_MGMT_CTL_UPDATE 0x8000 #define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_MASK 0x3f00 -#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XLO 0x0000 -#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XHI 0x0100 -#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XLO 0x0200 -#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XHI 0x0300 +#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200000XLO 0x0000 +#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200000XHI 0x0100 +#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200002XLO 0x0200 +#define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C200002XHI 0x0300 #define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST 0x2000 #define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST 0x2100 #define MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST 0x3000 @@ -255,11 +255,11 @@ int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr); int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); +int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip); -int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); @@ -274,7 +274,9 @@ int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); int mv88e6085_g1_ip_pri_map(struct mv88e6xxx_chip *chip); + int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip); +int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip); int mv88e6185_g1_set_cascade_port(struct mv88e6xxx_chip *chip, int port); @@ -301,6 +303,10 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 4542dfa5fc69..cef5046983e8 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -90,7 +90,7 @@ static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) if (err) return err; } else { - if (mv88e6xxx_num_databases(chip) > 16) { + if (mv88e6xxx_num_databases(chip) > 64) { /* ATU DBNum[7:4] are located in ATU Control 15:12 */ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val); @@ -102,6 +102,9 @@ static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) val); if (err) return err; + } else if (mv88e6xxx_num_databases(chip) > 16) { + /* ATU DBNum[5:4] are located in ATU Operation 9:8 */ + op |= (fid & 0x30) << 4; } /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 3e9be3f51196..45040f963142 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -303,6 +303,35 @@ static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_vtu_vid_read(chip, entry); } +int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_vtu_getnext(chip, entry); + if (err) + return err; + + if (entry->valid) { + err = mv88e6185_g1_vtu_data_read(chip, entry); + if (err) + return err; + + /* VTU DBNum[3:0] are located in VTU Operation 3:0 + * VTU DBNum[5:4] are located in VTU Operation 9:8 + */ + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); + if (err) + return err; + + entry->fid = val & 0x000f; + entry->fid |= (val & 0x0300) >> 4; + } + + return 0; +} + int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -392,6 +421,35 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return 0; } +int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE; + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + if (err) + return err; + + if (entry->valid) { + err = mv88e6185_g1_vtu_data_write(chip, entry); + if (err) + return err; + + /* VTU DBNum[3:0] are located in VTU Operation 3:0 + * VTU DBNum[5:4] are located in VTU Operation 9:8 + */ + op |= entry->fid & 0x000f; + op |= (entry->fid & 0x0030) << 8; + } + + return mv88e6xxx_g1_vtu_op(chip, op); +} + int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 1546171210a1..b176ee1adbba 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -812,6 +812,32 @@ const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = { .irq_free = mv88e6097_watchdog_free, }; +static void mv88e6250_watchdog_free(struct mv88e6xxx_chip *chip) +{ + u16 reg; + + mv88e6xxx_g2_read(chip, MV88E6250_G2_WDOG_CTL, ®); + + reg &= ~(MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE | + MV88E6250_G2_WDOG_CTL_QC_ENABLE); + + mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, reg); +} + +static int mv88e6250_watchdog_setup(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, + MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE | + MV88E6250_G2_WDOG_CTL_QC_ENABLE | + MV88E6250_G2_WDOG_CTL_SWRESET); +} + +const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = { + .irq_action = mv88e6097_watchdog_action, + .irq_setup = mv88e6250_watchdog_setup, + .irq_free = mv88e6250_watchdog_free, +}; + static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g2_update(chip, MV88E6390_G2_WDOG_CTL, diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index bfb2c6123f55..a664fc25f132 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -202,6 +202,18 @@ #define MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK 0x00ff /* Offset 0x1B: Watch Dog Control Register */ +#define MV88E6250_G2_WDOG_CTL 0x1b +#define MV88E6250_G2_WDOG_CTL_QC_HISTORY 0x0100 +#define MV88E6250_G2_WDOG_CTL_QC_EVENT 0x0080 +#define MV88E6250_G2_WDOG_CTL_QC_ENABLE 0x0040 +#define MV88E6250_G2_WDOG_CTL_EGRESS_HISTORY 0x0020 +#define MV88E6250_G2_WDOG_CTL_EGRESS_EVENT 0x0010 +#define MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE 0x0008 +#define MV88E6250_G2_WDOG_CTL_FORCE_IRQ 0x0004 +#define MV88E6250_G2_WDOG_CTL_HISTORY 0x0002 +#define MV88E6250_G2_WDOG_CTL_SWRESET 0x0001 + +/* Offset 0x1B: Watch Dog Control Register */ #define MV88E6352_G2_WDOG_CTL 0x1b #define MV88E6352_G2_WDOG_CTL_EGRESS_EVENT 0x0080 #define MV88E6352_G2_WDOG_CTL_RMU_TIMEOUT 0x0040 @@ -330,6 +342,7 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, int port); extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; +extern const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops; extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; extern const struct mv88e6xxx_avb_ops mv88e6165_avb_ops; @@ -480,6 +493,7 @@ static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) } static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; +static const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = {}; static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; static const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {}; diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 9a2b4b385a2c..04309ef0a1cc 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -290,6 +290,18 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, false, false); } +/* Support 10, 100 Mbps (e.g. 88E6250 family) */ +int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 100; + + if (speed > 100) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */ int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) { @@ -517,6 +529,71 @@ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) return 0; } +int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + if (port < 5) { + switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { + case MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF: + state->speed = SPEED_10; + state->duplex = DUPLEX_HALF; + break; + case MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF: + state->speed = SPEED_100; + state->duplex = DUPLEX_HALF; + break; + case MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL: + state->speed = SPEED_10; + state->duplex = DUPLEX_FULL; + break; + case MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL: + state->speed = SPEED_100; + state->duplex = DUPLEX_FULL; + break; + default: + state->speed = SPEED_UNKNOWN; + state->duplex = DUPLEX_UNKNOWN; + break; + } + } else { + switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { + case MV88E6250_PORT_STS_PORTMODE_MII_10_HALF: + state->speed = SPEED_10; + state->duplex = DUPLEX_HALF; + break; + case MV88E6250_PORT_STS_PORTMODE_MII_100_HALF: + state->speed = SPEED_100; + state->duplex = DUPLEX_HALF; + break; + case MV88E6250_PORT_STS_PORTMODE_MII_10_FULL: + state->speed = SPEED_10; + state->duplex = DUPLEX_FULL; + break; + case MV88E6250_PORT_STS_PORTMODE_MII_100_FULL: + state->speed = SPEED_100; + state->duplex = DUPLEX_FULL; + break; + default: + state->speed = SPEED_UNKNOWN; + state->duplex = DUPLEX_UNKNOWN; + break; + } + } + + state->link = !!(reg & MV88E6250_PORT_STS_LINK); + state->an_enabled = 1; + state->an_complete = state->link; + + return 0; +} + int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state) { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index f2fba3f73199..8d5a6cd6fb19 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -19,6 +19,16 @@ #define MV88E6XXX_PORT_STS_MY_PAUSE 0x4000 #define MV88E6XXX_PORT_STS_HD_FLOW 0x2000 #define MV88E6XXX_PORT_STS_PHY_DETECT 0x1000 +#define MV88E6250_PORT_STS_LINK 0x1000 +#define MV88E6250_PORT_STS_PORTMODE_MASK 0x0f00 +#define MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF 0x0800 +#define MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF 0x0900 +#define MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL 0x0a00 +#define MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL 0x0b00 +#define MV88E6250_PORT_STS_PORTMODE_MII_10_HALF 0x0c00 +#define MV88E6250_PORT_STS_PORTMODE_MII_100_HALF 0x0d00 +#define MV88E6250_PORT_STS_PORTMODE_MII_10_FULL 0x0e00 +#define MV88E6250_PORT_STS_PORTMODE_MII_100_FULL 0x0f00 #define MV88E6XXX_PORT_STS_LINK 0x0800 #define MV88E6XXX_PORT_STS_DUPLEX 0x0400 #define MV88E6XXX_PORT_STS_SPEED_MASK 0x0300 @@ -108,6 +118,7 @@ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 @@ -275,6 +286,7 @@ int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); @@ -328,6 +340,8 @@ int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state); +int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c index 92e9324f1fb9..5fc78a063843 100644 --- a/drivers/net/dsa/mv88e6xxx/smi.c +++ b/drivers/net/dsa/mv88e6xxx/smi.c @@ -20,6 +20,10 @@ * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing * multiple devices to share the SMI interface. In this mode it responds to only * 2 registers, used to indirectly access the internal SMI devices. + * + * Some chips use a different scheme: Only the ADDR4 pin is used for + * configuration, and the device responds to 16 of the 32 SMI + * addresses, allowing two to coexist on the same SMI interface. */ static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip, @@ -72,6 +76,23 @@ static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = { .write = mv88e6xxx_smi_direct_write, }; +static int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 *data) +{ + return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data); +} + +static int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 data) +{ + return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data); +} + +static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = { + .read = mv88e6xxx_smi_dual_direct_read, + .write = mv88e6xxx_smi_dual_direct_write, +}; + /* Offset 0x00: SMI Command Register * Offset 0x01: SMI Data Register */ @@ -140,7 +161,9 @@ static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = { int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, struct mii_bus *bus, int sw_addr) { - if (sw_addr == 0) + if (chip->info->dual_chip) + chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops; + else if (sw_addr == 0) chip->smi_ops = &mv88e6xxx_smi_direct_ops; else if (chip->info->multi_chip) chip->smi_ops = &mv88e6xxx_smi_indirect_ops; diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index b043bfc408f2..61d00682de60 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -55,6 +55,11 @@ struct sja1105_info { const struct sja1105_regs *regs; int (*reset_cmd)(const void *ctx, const void *data); int (*setup_rgmii_delay)(const void *ctx, int port); + /* Prototypes from include/net/dsa.h */ + int (*fdb_add_cmd)(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); + int (*fdb_del_cmd)(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); const char *name; }; @@ -142,7 +147,20 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv, enum sja1105_blk_idx blk_idx, int index, void *entry, bool keep); -u8 sja1105_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid); +enum sja1105_iotag { + SJA1105_C_TAG = 0, /* Inner VLAN header */ + SJA1105_S_TAG = 1, /* Outer VLAN header */ +}; + +u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid); +int sja1105et_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); +int sja1105et_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); +int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); +int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); /* Common implementations for the static and dynamic configs */ size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr, diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index e73ab28bf632..352bb6e89297 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -35,17 +35,72 @@ #define SJA1105_MAX_DYN_CMD_SIZE \ SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD +struct sja1105_dyn_cmd { + bool search; + u64 valid; + u64 rdwrset; + u64 errors; + u64 valident; + u64 index; +}; + +enum sja1105_hostcmd { + SJA1105_HOSTCMD_SEARCH = 1, + SJA1105_HOSTCMD_READ = 2, + SJA1105_HOSTCMD_WRITE = 3, + SJA1105_HOSTCMD_INVALIDATE = 4, +}; + static void sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, enum packing_op op) { u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; const int size = SJA1105_SIZE_DYN_CMD; + u64 lockeds = 0; + u64 hostcmd; sja1105_packing(p, &cmd->valid, 31, 31, size, op); sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); sja1105_packing(p, &cmd->errors, 29, 29, size, op); + sja1105_packing(p, &lockeds, 28, 28, size, op); sja1105_packing(p, &cmd->valident, 27, 27, size, op); + + /* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T, + * using it to delete a management route was unsupported. UM10944 + * said about it: + * + * In case of a write access with the MGMTROUTE flag set, + * the flag will be ignored. It will always be found cleared + * for read accesses with the MGMTROUTE flag set. + * + * SJA1105 P/Q/R/S keeps the same behavior w.r.t. VALIDENT, but there + * is now another flag called HOSTCMD which does more stuff (quoting + * from UM11040): + * + * A write request is accepted only when HOSTCMD is set to write host + * or invalid. A read request is accepted only when HOSTCMD is set to + * search host or read host. + * + * So it is possible to translate a RDWRSET/VALIDENT combination into + * HOSTCMD so that we keep the dynamic command API in place, and + * at the same time achieve compatibility with the management route + * command structure. + */ + if (cmd->rdwrset == SPI_READ) { + if (cmd->search) + hostcmd = SJA1105_HOSTCMD_SEARCH; + else + hostcmd = SJA1105_HOSTCMD_READ; + } else { + /* SPI_WRITE */ + if (cmd->valident) + hostcmd = SJA1105_HOSTCMD_WRITE; + else + hostcmd = SJA1105_HOSTCMD_INVALIDATE; + } + sja1105_packing(p, &hostcmd, 25, 23, size, op); + /* Hack - The hardware takes the 'index' field within * struct sja1105_l2_lookup_entry as the index on which this command * will operate. However it will ignore everything else, so 'index' @@ -54,9 +109,8 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, * such that our API doesn't need to ask for a full-blown entry * structure when e.g. a delete is requested. */ - sja1105_packing(buf, &cmd->index, 29, 20, + sja1105_packing(buf, &cmd->index, 15, 6, SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op); - /* TODO hostcmd */ } static void @@ -107,6 +161,36 @@ static size_t sja1105et_mgmt_route_entry_packing(void *buf, void *entry_ptr, return size; } +static void +sja1105pqrs_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, + enum packing_op op) +{ + u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; + u64 mgmtroute = 1; + + sja1105pqrs_l2_lookup_cmd_packing(buf, cmd, op); + if (op == PACK) + sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD); +} + +static size_t sja1105pqrs_mgmt_route_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; + struct sja1105_mgmt_entry *entry = entry_ptr; + + /* In P/Q/R/S, enfport got renamed to mgmtvalid, but its purpose + * is the same (driver uses it to confirm that frame was sent). + * So just keep the name from E/T. + */ + sja1105_packing(buf, &entry->tsreg, 71, 71, size, op); + sja1105_packing(buf, &entry->takets, 70, 70, size, op); + sja1105_packing(buf, &entry->macaddr, 69, 22, size, op); + sja1105_packing(buf, &entry->destports, 21, 17, size, op); + sja1105_packing(buf, &entry->enfport, 16, 16, size, op); + return size; +} + /* In E/T, entry is at addresses 0x27-0x28. There is a 4 byte gap at 0x29, * and command is at 0x2a. Similarly in P/Q/R/S there is a 1 register gap * between entry (0x2d, 0x2e) and command (0x30). @@ -240,6 +324,7 @@ sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, #define OP_READ BIT(0) #define OP_WRITE BIT(1) #define OP_DEL BIT(2) +#define OP_SEARCH BIT(3) /* SJA1105E/T: First generation */ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { @@ -304,14 +389,22 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_XMII_PARAMS] = {0}, }; -/* SJA1105P/Q/R/S: Second generation: TODO */ +/* SJA1105P/Q/R/S: Second generation */ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_L2_LOOKUP] = { .entry_packing = sja1105pqrs_l2_lookup_entry_packing, .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, - .access = (OP_READ | OP_WRITE | OP_DEL), + .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, - .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, + .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, + .addr = 0x24, + }, + [BLK_IDX_MGMT_ROUTE] = { + .entry_packing = sja1105pqrs_mgmt_route_entry_packing, + .cmd_packing = sja1105pqrs_mgmt_route_cmd_packing, + .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), + .max_entry_count = SJA1105_NUM_PORTS, + .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, .addr = 0x24, }, [BLK_IDX_L2_POLICING] = {0}, @@ -359,6 +452,24 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_XMII_PARAMS] = {0}, }; +/* Provides read access to the settings through the dynamic interface + * of the switch. + * @blk_idx is used as key to select from the sja1105_dynamic_table_ops. + * The selection is limited by the hardware in respect to which + * configuration blocks can be read through the dynamic interface. + * @index is used to retrieve a particular table entry. If negative, + * (and if the @blk_idx supports the searching operation) a search + * is performed by the @entry parameter. + * @entry Type-casted to an unpacked structure that holds a table entry + * of the type specified in @blk_idx. + * Usually an output argument. If @index is negative, then this + * argument is used as input/output: it should be pre-populated + * with the element to search for. Entries which support the + * search operation will have an "index" field (not the @index + * argument to this function) and that is where the found index + * will be returned (or left unmodified - thus negative - if not + * found). + */ int sja1105_dynamic_config_read(struct sja1105_private *priv, enum sja1105_blk_idx blk_idx, int index, void *entry) @@ -375,8 +486,10 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv, ops = &priv->info->dyn_ops[blk_idx]; - if (index >= ops->max_entry_count) + if (index >= 0 && index >= ops->max_entry_count) return -ERANGE; + if (index < 0 && !(ops->access & OP_SEARCH)) + return -EOPNOTSUPP; if (!(ops->access & OP_READ)) return -EOPNOTSUPP; if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE) @@ -388,9 +501,20 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv, cmd.valid = true; /* Trigger action on table entry */ cmd.rdwrset = SPI_READ; /* Action is read */ - cmd.index = index; + if (index < 0) { + /* Avoid copying a signed negative number to an u64 */ + cmd.index = 0; + cmd.search = true; + } else { + cmd.index = index; + cmd.search = false; + } + cmd.valident = true; ops->cmd_packing(packed_buf, &cmd, PACK); + if (cmd.search) + ops->entry_packing(packed_buf, entry, PACK); + /* Send SPI write operation: read config table entry */ rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE, ops->addr, packed_buf, ops->packed_size); @@ -416,7 +540,7 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv, * So don't error out in that case. */ if (!cmd.valident && blk_idx != BLK_IDX_MGMT_ROUTE) - return -EINVAL; + return -ENOENT; cpu_relax(); } while (cmd.valid && --retries); @@ -448,6 +572,8 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv, if (index >= ops->max_entry_count) return -ERANGE; + if (index < 0) + return -ERANGE; if (!(ops->access & OP_WRITE)) return -EOPNOTSUPP; if (!keep && !(ops->access & OP_DEL)) @@ -510,7 +636,7 @@ static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly) * is also received as argument in the Koopman notation that the switch * hardware stores it in. */ -u8 sja1105_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid) +u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid) { struct sja1105_l2_lookup_params_entry *l2_lookup_params = priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS].entries; diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.h b/drivers/net/dsa/sja1105/sja1105_dynamic_config.h index 77be59546a55..740dadf43f01 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.h +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.h @@ -7,13 +7,10 @@ #include "sja1105.h" #include <linux/packing.h> -struct sja1105_dyn_cmd { - u64 valid; - u64 rdwrset; - u64 errors; - u64 valident; - u64 index; -}; +/* Special index that can be used for sja1105_dynamic_config_read */ +#define SJA1105_SEARCH -1 + +struct sja1105_dyn_cmd; struct sja1105_dynamic_table_ops { /* This returns size_t just to keep same prototype as the diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 1c3959efebc4..66e90bbe8bc9 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -210,6 +210,8 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) .maxage = SJA1105_AGEING_TIME_MS(300000), /* All entries within a FDB bin are available for learning */ .dyn_tbsz = SJA1105ET_FDB_BIN_SIZE, + /* And the P/Q/R/S equivalent setting: */ + .start_dynspc = 0, /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */ .poly = 0x97, /* This selects between Independent VLAN Learning (IVL) and @@ -225,6 +227,13 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) * Maybe correlate with no_linklocal_learn from bridge driver? */ .no_mgmt_learn = true, + /* P/Q/R/S only */ + .use_static = true, + /* Dynamically learned FDB entries can overwrite other (older) + * dynamic FDB entries + */ + .owr_dyn = true, + .drpnolearn = true, }; table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; @@ -734,15 +743,16 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, return sja1105_clocking_setup_port(priv, port); } -static void sja1105_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static void sja1105_mac_config(struct dsa_switch *ds, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state) { struct sja1105_private *priv = ds->priv; - if (!phydev->link) + if (!state->link) sja1105_adjust_port_config(priv, port, 0, false); else - sja1105_adjust_port_config(priv, port, phydev->speed, true); + sja1105_adjust_port_config(priv, port, state->speed, true); } static void sja1105_phylink_validate(struct dsa_switch *ds, int port, @@ -785,10 +795,10 @@ static inline int sja1105et_fdb_index(int bin, int way) return bin * SJA1105ET_FDB_BIN_SIZE + way; } -static int sja1105_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, - const u8 *addr, u16 vid, - struct sja1105_l2_lookup_entry *match, - int *last_unused) +static int sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, + const u8 *addr, u16 vid, + struct sja1105_l2_lookup_entry *match, + int *last_unused) { int way; @@ -817,8 +827,8 @@ static int sja1105_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, return -1; } -static int sja1105_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +int sja1105et_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct sja1105_l2_lookup_entry l2_lookup = {0}; struct sja1105_private *priv = ds->priv; @@ -826,10 +836,10 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, int last_unused = -1; int bin, way; - bin = sja1105_fdb_hash(priv, addr, vid); + bin = sja1105et_fdb_hash(priv, addr, vid); - way = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid, - &l2_lookup, &last_unused); + way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid, + &l2_lookup, &last_unused); if (way >= 0) { /* We have an FDB entry. Is our port in the destination * mask? If yes, we need to do nothing. If not, we need @@ -873,17 +883,17 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, true); } -static int sja1105_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +int sja1105et_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct sja1105_l2_lookup_entry l2_lookup = {0}; struct sja1105_private *priv = ds->priv; int index, bin, way; bool keep; - bin = sja1105_fdb_hash(priv, addr, vid); - way = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid, - &l2_lookup, NULL); + bin = sja1105et_fdb_hash(priv, addr, vid); + way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid, + &l2_lookup, NULL); if (way < 0) return 0; index = sja1105et_fdb_index(bin, way); @@ -893,8 +903,8 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port, * need to completely evict the FDB entry. * Otherwise we just write it back. */ - if (l2_lookup.destports & BIT(port)) - l2_lookup.destports &= ~BIT(port); + l2_lookup.destports &= ~BIT(port); + if (l2_lookup.destports) keep = true; else @@ -904,6 +914,138 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port, index, &l2_lookup, keep); } +int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct sja1105_l2_lookup_entry l2_lookup = {0}; + struct sja1105_private *priv = ds->priv; + int rc, i; + + /* Search for an existing entry in the FDB table */ + l2_lookup.macaddr = ether_addr_to_u64(addr); + l2_lookup.vlanid = vid; + l2_lookup.iotag = SJA1105_S_TAG; + l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); + l2_lookup.mask_vlanid = VLAN_VID_MASK; + l2_lookup.mask_iotag = BIT(0); + l2_lookup.destports = BIT(port); + + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + SJA1105_SEARCH, &l2_lookup); + if (rc == 0) { + /* Found and this port is already in the entry's + * port mask => job done + */ + if (l2_lookup.destports & BIT(port)) + return 0; + /* l2_lookup.index is populated by the switch in case it + * found something. + */ + l2_lookup.destports |= BIT(port); + goto skip_finding_an_index; + } + + /* Not found, so try to find an unused spot in the FDB. + * This is slightly inefficient because the strategy is knock-knock at + * every possible position from 0 to 1023. + */ + for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + i, NULL); + if (rc < 0) + break; + } + if (i == SJA1105_MAX_L2_LOOKUP_COUNT) { + dev_err(ds->dev, "FDB is full, cannot add entry.\n"); + return -EINVAL; + } + l2_lookup.index = i; + +skip_finding_an_index: + return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup.index, &l2_lookup, + true); +} + +int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct sja1105_l2_lookup_entry l2_lookup = {0}; + struct sja1105_private *priv = ds->priv; + bool keep; + int rc; + + l2_lookup.macaddr = ether_addr_to_u64(addr); + l2_lookup.vlanid = vid; + l2_lookup.iotag = SJA1105_S_TAG; + l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); + l2_lookup.mask_vlanid = VLAN_VID_MASK; + l2_lookup.mask_iotag = BIT(0); + l2_lookup.destports = BIT(port); + + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + SJA1105_SEARCH, &l2_lookup); + if (rc < 0) + return 0; + + l2_lookup.destports &= ~BIT(port); + + /* Decide whether we remove just this port from the FDB entry, + * or if we remove it completely. + */ + if (l2_lookup.destports) + keep = true; + else + keep = false; + + return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup.index, &l2_lookup, keep); +} + +static int sja1105_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + /* Since we make use of VLANs even when the bridge core doesn't tell us + * to, translate these FDB entries into the correct dsa_8021q ones. + */ + if (!dsa_port_is_vlan_filtering(&ds->ports[port])) { + unsigned int upstream = dsa_upstream_port(priv->ds, port); + u16 tx_vid = dsa_8021q_tx_vid(ds, port); + u16 rx_vid = dsa_8021q_rx_vid(ds, port); + + rc = priv->info->fdb_add_cmd(ds, port, addr, tx_vid); + if (rc < 0) + return rc; + return priv->info->fdb_add_cmd(ds, upstream, addr, rx_vid); + } + return priv->info->fdb_add_cmd(ds, port, addr, vid); +} + +static int sja1105_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + /* Since we make use of VLANs even when the bridge core doesn't tell us + * to, translate these FDB entries into the correct dsa_8021q ones. + */ + if (!dsa_port_is_vlan_filtering(&ds->ports[port])) { + unsigned int upstream = dsa_upstream_port(priv->ds, port); + u16 tx_vid = dsa_8021q_tx_vid(ds, port); + u16 rx_vid = dsa_8021q_rx_vid(ds, port); + + rc = priv->info->fdb_del_cmd(ds, port, addr, tx_vid); + if (rc < 0) + return rc; + return priv->info->fdb_del_cmd(ds, upstream, addr, rx_vid); + } + return priv->info->fdb_del_cmd(ds, port, addr, vid); +} + static int sja1105_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { @@ -919,7 +1061,7 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, i, &l2_lookup); /* No fdb entry at i, not an issue */ - if (rc == -EINVAL) + if (rc == -ENOENT) continue; if (rc) { dev_err(dev, "Failed to dump FDB: %d\n", rc); @@ -935,6 +1077,15 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, if (!(l2_lookup.destports & BIT(port))) continue; u64_to_ether_addr(l2_lookup.macaddr, macaddr); + + /* We need to hide the dsa_8021q VLAN from the user. + * Convert the TX VID into the pvid that is active in + * standalone and non-vlan_filtering modes, aka 1. + * The RX VID is applied on the CPU port, which is not seen by + * the bridge core anyway, so there's nothing to hide. + */ + if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + l2_lookup.vlanid = 1; cb(macaddr, l2_lookup.vlanid, false, data); } return 0; @@ -1446,6 +1597,8 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, if (!timeout) { /* Clean up the management route so that a follow-up * frame may not match on it by mistake. + * This is only hardware supported on P/Q/R/S - on E/T it is + * a no-op and we are silently discarding the -EOPNOTSUPP. */ sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE, slot, &mgmt_route, false); @@ -1515,9 +1668,9 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds, static const struct dsa_switch_ops sja1105_switch_ops = { .get_tag_protocol = sja1105_get_tag_protocol, .setup = sja1105_setup, - .adjust_link = sja1105_adjust_link, .set_ageing_time = sja1105_set_ageing_time, .phylink_validate = sja1105_phylink_validate, + .phylink_mac_config = sja1105_mac_config, .get_strings = sja1105_get_strings, .get_ethtool_stats = sja1105_get_ethtool_stats, .get_sset_count = sja1105_get_sset_count, diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 2eb70b8acfc3..b1344ed1697f 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -541,6 +541,8 @@ struct sja1105_info sja1105e_info = { .static_ops = sja1105e_table_ops, .dyn_ops = sja1105et_dyn_ops, .reset_cmd = sja1105et_reset_cmd, + .fdb_add_cmd = sja1105et_fdb_add, + .fdb_del_cmd = sja1105et_fdb_del, .regs = &sja1105et_regs, .name = "SJA1105E", }; @@ -550,6 +552,8 @@ struct sja1105_info sja1105t_info = { .static_ops = sja1105t_table_ops, .dyn_ops = sja1105et_dyn_ops, .reset_cmd = sja1105et_reset_cmd, + .fdb_add_cmd = sja1105et_fdb_add, + .fdb_del_cmd = sja1105et_fdb_del, .regs = &sja1105et_regs, .name = "SJA1105T", }; @@ -559,6 +563,8 @@ struct sja1105_info sja1105p_info = { .static_ops = sja1105p_table_ops, .dyn_ops = sja1105pqrs_dyn_ops, .reset_cmd = sja1105pqrs_reset_cmd, + .fdb_add_cmd = sja1105pqrs_fdb_add, + .fdb_del_cmd = sja1105pqrs_fdb_del, .regs = &sja1105pqrs_regs, .name = "SJA1105P", }; @@ -568,6 +574,8 @@ struct sja1105_info sja1105q_info = { .static_ops = sja1105q_table_ops, .dyn_ops = sja1105pqrs_dyn_ops, .reset_cmd = sja1105pqrs_reset_cmd, + .fdb_add_cmd = sja1105pqrs_fdb_add, + .fdb_del_cmd = sja1105pqrs_fdb_del, .regs = &sja1105pqrs_regs, .name = "SJA1105Q", }; @@ -577,6 +585,8 @@ struct sja1105_info sja1105r_info = { .static_ops = sja1105r_table_ops, .dyn_ops = sja1105pqrs_dyn_ops, .reset_cmd = sja1105pqrs_reset_cmd, + .fdb_add_cmd = sja1105pqrs_fdb_add, + .fdb_del_cmd = sja1105pqrs_fdb_del, .regs = &sja1105pqrs_regs, .name = "SJA1105R", }; @@ -587,5 +597,7 @@ struct sja1105_info sja1105s_info = { .dyn_ops = sja1105pqrs_dyn_ops, .regs = &sja1105pqrs_regs, .reset_cmd = sja1105pqrs_reset_cmd, + .fdb_add_cmd = sja1105pqrs_fdb_add, + .fdb_del_cmd = sja1105pqrs_fdb_del, .name = "SJA1105S", }; diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c index b3c992b0abb0..6d65a7b09395 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.c +++ b/drivers/net/dsa/sja1105/sja1105_static_config.c @@ -236,10 +236,20 @@ size_t sja1105pqrs_l2_lookup_entry_packing(void *buf, void *entry_ptr, const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; struct sja1105_l2_lookup_entry *entry = entry_ptr; - /* These are static L2 lookup entries, so the structure - * should match UM11040 Table 16/17 definitions when - * LOCKEDS is 1. - */ + if (entry->lockeds) { + sja1105_packing(buf, &entry->tsreg, 159, 159, size, op); + sja1105_packing(buf, &entry->mirrvlan, 158, 147, size, op); + sja1105_packing(buf, &entry->takets, 146, 146, size, op); + sja1105_packing(buf, &entry->mirr, 145, 145, size, op); + sja1105_packing(buf, &entry->retag, 144, 144, size, op); + } else { + sja1105_packing(buf, &entry->touched, 159, 159, size, op); + sja1105_packing(buf, &entry->age, 158, 144, size, op); + } + sja1105_packing(buf, &entry->mask_iotag, 143, 143, size, op); + sja1105_packing(buf, &entry->mask_vlanid, 142, 131, size, op); + sja1105_packing(buf, &entry->mask_macaddr, 130, 83, size, op); + sja1105_packing(buf, &entry->iotag, 82, 82, size, op); sja1105_packing(buf, &entry->vlanid, 81, 70, size, op); sja1105_packing(buf, &entry->macaddr, 69, 22, size, op); sja1105_packing(buf, &entry->destports, 21, 17, size, op); @@ -442,7 +452,7 @@ const char *sja1105_static_config_error_msg[] = { "vl-forwarding-parameters-table.partspc.", }; -sja1105_config_valid_t +static sja1105_config_valid_t static_config_check_memory_size(const struct sja1105_table *tables) { const struct sja1105_l2_forwarding_params_entry *l2_fwd_params; diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.h b/drivers/net/dsa/sja1105/sja1105_static_config.h index 069ca8fd059c..d513b1c91b98 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.h +++ b/drivers/net/dsa/sja1105/sja1105_static_config.h @@ -122,9 +122,35 @@ struct sja1105_l2_lookup_entry { u64 destports; u64 enfport; u64 index; + /* P/Q/R/S only */ + u64 mask_iotag; + u64 mask_vlanid; + u64 mask_macaddr; + u64 iotag; + bool lockeds; + union { + /* LOCKEDS=1: Static FDB entries */ + struct { + u64 tsreg; + u64 mirrvlan; + u64 takets; + u64 mirr; + u64 retag; + }; + /* LOCKEDS=0: Dynamically learned FDB entries */ + struct { + u64 touched; + u64 age; + }; + }; }; struct sja1105_l2_lookup_params_entry { + u64 start_dynspc; /* P/Q/R/S only */ + u64 drpnolearn; /* P/Q/R/S only */ + u64 use_static; /* P/Q/R/S only */ + u64 owr_dyn; /* P/Q/R/S only */ + u64 learn_once; /* P/Q/R/S only */ u64 maxage; /* Shared */ u64 dyn_tbsz; /* E/T only */ u64 poly; /* E/T only */ |