On Thu, 29 Oct 2020 15:42:29 +1000 Pavana Sharma <pavana.sha...@digi.com> wrote:
> The Marvell 88E6393X device is a single-chip integration of a 11-port > Ethernet switch with eight integrated Gigabit Ethernet (GbE) transceivers > and three 10-Gigabit interfaces. > > This patch adds functionalities specific to mv88e6393x family (88E6393X, > 88E6193X and 88E6191X) > > Co-developed-by: Ashkan Boldaji <ashkan.bold...@digi.com> > Signed-off-by: Ashkan Boldaji <ashkan.bold...@digi.com> > Signed-off-by: Pavana Sharma <pavana.sha...@digi.com> > --- > Changes in v2: > - Fix a warning (Reported-by: kernel test robot <l...@intel.com>) > Changes in v3: > - Fix 'unused function' warning > Changes in v4, v5, v6: > - Incorporated feedback from maintainers. > --- > drivers/net/dsa/mv88e6xxx/chip.c | 91 +++++++++++ > drivers/net/dsa/mv88e6xxx/chip.h | 4 + > drivers/net/dsa/mv88e6xxx/global1.h | 2 + > drivers/net/dsa/mv88e6xxx/global2.h | 8 + > drivers/net/dsa/mv88e6xxx/port.c | 234 ++++++++++++++++++++++++++++ > drivers/net/dsa/mv88e6xxx/port.h | 43 ++++- > drivers/net/dsa/mv88e6xxx/serdes.c | 225 ++++++++++++++++++++++++++ > drivers/net/dsa/mv88e6xxx/serdes.h | 39 +++++ > 8 files changed, 644 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/dsa/mv88e6xxx/chip.c > b/drivers/net/dsa/mv88e6xxx/chip.c > index f0dbc05e30a4..de96fd08e77a 100644 > --- a/drivers/net/dsa/mv88e6xxx/chip.c > +++ b/drivers/net/dsa/mv88e6xxx/chip.c > @@ -634,6 +634,24 @@ static void mv88e6390x_phylink_validate(struct > mv88e6xxx_chip *chip, int port, > mv88e6390_phylink_validate(chip, port, mask, state); > } > > +static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int > port, > + unsigned long *mask, > + struct phylink_link_state *state) > +{ > + if (port == 0 || port == 9 || port == 10) { > + phylink_set(mask, 10000baseT_Full); > + phylink_set(mask, 10000baseKR_Full); > + phylink_set(mask, 5000baseT_Full); > + phylink_set(mask, 2500baseX_Full); > + phylink_set(mask, 2500baseT_Full); > + } > + > + phylink_set(mask, 1000baseT_Full); > + phylink_set(mask, 1000baseX_Full); > + > + mv88e6065_phylink_validate(chip, port, mask, state); > +} > + > static void mv88e6xxx_validate(struct dsa_switch *ds, int port, > unsigned long *supported, > struct phylink_link_state *state) > @@ -4141,6 +4159,56 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { > .phylink_validate = mv88e6390_phylink_validate, > }; > > +static const struct mv88e6xxx_ops mv88e6193x_ops = { > + /* MV88E6XXX_FAMILY_6393 */ > + .setup_errata = mv88e6393x_setup_errata, > + .irl_init_all = mv88e6390_g2_irl_init_all, > + .get_eeprom = mv88e6xxx_g2_get_eeprom8, > + .set_eeprom = mv88e6xxx_g2_set_eeprom8, > + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, > + .phy_read = mv88e6xxx_g2_smi_phy_read, > + .phy_write = mv88e6xxx_g2_smi_phy_write, > + .port_set_link = mv88e6xxx_port_set_link, > + .port_set_speed_duplex = mv88e6393x_port_set_speed_duplex, > + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, > + .port_tag_remap = mv88e6390_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 = mv88e6393x_port_set_ether_type, > + .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, > + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, > + .port_pause_limit = mv88e6390_port_pause_limit, > + .port_set_cmode = mv88e6393x_port_set_cmode, > + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, > + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, > + .port_get_cmode = mv88e6352_port_get_cmode, > + .stats_snapshot = mv88e6390_g1_stats_snapshot, > + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, > + .stats_get_sset_count = mv88e6320_stats_get_sset_count, > + .stats_get_strings = mv88e6320_stats_get_strings, > + .stats_get_stats = mv88e6390_stats_get_stats, > + .set_cpu_port = mv88e6393x_port_set_cpu_dest, > + .set_egress_port = mv88e6393x_set_egress_port, > + .watchdog_ops = &mv88e6390_watchdog_ops, > + .mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu, > + .pot_clear = mv88e6xxx_g2_pot_clear, > + .reset = mv88e6352_g1_reset, > + .rmu_disable = mv88e6390_g1_rmu_disable, > + .vtu_getnext = mv88e6390_g1_vtu_getnext, > + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, > + .serdes_power = mv88e6393x_serdes_power, > + .serdes_get_lane = mv88e6393x_serdes_get_lane, > + /* Check status register pause & lpa register */ Is this a TODO comment? If so, it should be marked as such. > + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, > + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, > + .serdes_irq_enable = mv88e6393x_serdes_irq_enable, > + .serdes_irq_status = mv88e6393x_serdes_irq_status, > + .gpio_ops = &mv88e6352_gpio_ops, > + .avb_ops = &mv88e6390_avb_ops, > + .ptp_ops = &mv88e6352_ptp_ops, > + .phylink_validate = mv88e6393x_phylink_validate, > +}; > + > static const struct mv88e6xxx_ops mv88e6240_ops = { > /* MV88E6XXX_FAMILY_6352 */ > .ieee_pri_map = mv88e6085_g1_ieee_pri_map, > @@ -5073,6 +5141,29 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = > { > .ops = &mv88e6191_ops, > }, > > + [MV88E6193X] = { > + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6193X, > + .family = MV88E6XXX_FAMILY_6393, > + .name = "Marvell 88E6193X", > + .num_databases = 4096, > + .num_ports = 11, /* 10 + Z80 */ > + .num_internal_phys = 9, > + .max_vid = 8191, > + .port_base_addr = 0x0, > + .phy_base_addr = 0x0, > + .global1_addr = 0x1b, > + .global2_addr = 0x1c, > + .age_time_coeff = 3750, > + .g1_irqs = 10, > + .g2_irqs = 14, > + .atu_move_port_mask = 0x1f, > + .pvt = true, > + .multi_chip = true, > + .tag_protocol = DSA_TAG_PROTO_DSA, > + .ptp_support = true, > + .ops = &mv88e6193x_ops, > + }, > + Why not add 6393X here as well? > [MV88E6220] = { > .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6220, > .family = MV88E6XXX_FAMILY_6250, > diff --git a/drivers/net/dsa/mv88e6xxx/chip.h > b/drivers/net/dsa/mv88e6xxx/chip.h > index 823ae89e5fca..b7864a24a840 100644 > --- a/drivers/net/dsa/mv88e6xxx/chip.h > +++ b/drivers/net/dsa/mv88e6xxx/chip.h > @@ -63,6 +63,8 @@ enum mv88e6xxx_model { > MV88E6190, > MV88E6190X, > MV88E6191, > + MV88E6191X, > + MV88E6193X, > MV88E6220, > MV88E6240, > MV88E6250, > @@ -75,6 +77,7 @@ enum mv88e6xxx_model { > MV88E6352, > MV88E6390, > MV88E6390X, > + MV88E6393X, > }; > > enum mv88e6xxx_family { > @@ -90,6 +93,7 @@ enum mv88e6xxx_family { > MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ > MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ > MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ > + MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6393X */ > }; > > struct mv88e6xxx_ops; > diff --git a/drivers/net/dsa/mv88e6xxx/global1.h > b/drivers/net/dsa/mv88e6xxx/global1.h > index 1e3546f8b072..bd17eed15aad 100644 > --- a/drivers/net/dsa/mv88e6xxx/global1.h > +++ b/drivers/net/dsa/mv88e6xxx/global1.h > @@ -22,6 +22,7 @@ > #define MV88E6185_G1_STS_PPU_STATE_DISABLED 0x8000 > #define MV88E6185_G1_STS_PPU_STATE_POLLING 0xc000 > #define MV88E6XXX_G1_STS_INIT_READY 0x0800 > +#define MV88E6193X_G1_STS_IRQ_DEVICE_2 9 > #define MV88E6XXX_G1_STS_IRQ_AVB 8 > #define MV88E6XXX_G1_STS_IRQ_DEVICE 7 > #define MV88E6XXX_G1_STS_IRQ_STATS 6 > @@ -59,6 +60,7 @@ > #define MV88E6185_G1_CTL1_SCHED_PRIO 0x0800 > #define MV88E6185_G1_CTL1_MAX_FRAME_1632 0x0400 > #define MV88E6185_G1_CTL1_RELOAD_EEPROM 0x0200 > +#define MV88E6193X_G1_CTL1_DEVICE2_EN 0x0200 Since the family is called 6393X, please use MV88E6393X prefix for these macros. > #define MV88E6XXX_G1_CTL1_DEVICE_EN 0x0080 > #define MV88E6XXX_G1_CTL1_STATS_DONE_EN 0x0040 > #define MV88E6XXX_G1_CTL1_VTU_PROBLEM_EN 0x0020 > diff --git a/drivers/net/dsa/mv88e6xxx/global2.h > b/drivers/net/dsa/mv88e6xxx/global2.h > index 1f42ee656816..04696cb68971 100644 > --- a/drivers/net/dsa/mv88e6xxx/global2.h > +++ b/drivers/net/dsa/mv88e6xxx/global2.h > @@ -38,9 +38,15 @@ > /* Offset 0x02: MGMT Enable Register 2x */ > #define MV88E6XXX_G2_MGMT_EN_2X 0x02 > > +/* Offset 0x02: MAC LINK change IRQ Register for MV88E6393X */ > +#define MV88E6393X_G2_MACLINK_INT_SRC 0x02 > + Here you are using prefix with 6393X, but above you used 6193X, ... > /* Offset 0x03: MGMT Enable Register 0x */ > #define MV88E6XXX_G2_MGMT_EN_0X 0x03 > > +/* Offset 0x03: MAC LINK change IRQ Mask Register for MV88E6393X */ > +#define MV88E6393X_G2_MACLINK_INT_MASK 0x03 > + > /* Offset 0x04: Flow Control Delay Register */ > #define MV88E6XXX_G2_FLOW_CTL 0x04 > > @@ -52,6 +58,8 @@ > #define MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI 0x0080 > #define MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU 0x0008 > > +#define MV88E6393X_G2_EGRESS_MONITOR_DEST 0x05 > + > /* Offset 0x06: Device Mapping Table Register */ > #define MV88E6XXX_G2_DEVICE_MAPPING 0x06 > #define MV88E6XXX_G2_DEVICE_MAPPING_UPDATE 0x8000 > diff --git a/drivers/net/dsa/mv88e6xxx/port.c > b/drivers/net/dsa/mv88e6xxx/port.c > index 8128dc607cf4..8e36a69d8cad 100644 > --- a/drivers/net/dsa/mv88e6xxx/port.c > +++ b/drivers/net/dsa/mv88e6xxx/port.c > @@ -14,6 +14,7 @@ > #include <linux/phylink.h> > > #include "chip.h" > +#include "global2.h" > #include "port.h" > #include "serdes.h" > > @@ -25,6 +26,14 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int > port, int reg, > return mv88e6xxx_read(chip, addr, reg, val); > } > > +int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg, > + int bit, int val) > +{ > + int addr = chip->info->port_base_addr + port; > + > + return mv88e6xxx_wait_bit(chip, addr, reg, bit, val); > +} > + > int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, > u16 val) > { > @@ -390,6 +399,87 @@ phy_interface_t mv88e6390x_port_max_speed_mode(int port) > return PHY_INTERFACE_MODE_NA; > } > > +/* Support 10, 100, 200, 1000, 2500, 5000, 10000 Mbps (e.g. 88E6393X) */ > +int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, > + int speed, int duplex) > +{ > + u16 reg, ctrl; > + int err; > + > + if (speed == SPEED_MAX) > + speed = (port > 0 && port < 9) ? 1000 : 10000; > + > + if (speed == 200 && port != 0) > + return -EOPNOTSUPP; > + > + if (speed >= 2500 && port > 0 && port < 9) > + return -EOPNOTSUPP; > + > + switch (speed) { > + case 10: > + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10; > + break; > + case 100: > + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100; > + break; > + case 200: > + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 | > + MV88E6390_PORT_MAC_CTL_ALTSPEED; > + break; > + case 1000: > + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000; > + break; > + case 2500: > + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000 | > + MV88E6390_PORT_MAC_CTL_ALTSPEED; > + break; > + case 5000: > + ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 | > + MV88E6390_PORT_MAC_CTL_ALTSPEED; > + break; > + case 10000: > + case SPEED_UNFORCED: > + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED; > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + switch (duplex) { > + case DUPLEX_HALF: > + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; > + break; > + case DUPLEX_FULL: > + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | > + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; > + break; > + case DUPLEX_UNFORCED: > + /* normal duplex detection */ > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); > + if (err) > + return err; > + > + reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK | > + MV88E6390_PORT_MAC_CTL_ALTSPEED | > + MV88E6390_PORT_MAC_CTL_FORCE_SPEED); > + > + if (speed != SPEED_UNFORCED) > + reg |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED; > + > + reg |= ctrl; > + > + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); > + if (err) > + return err; > + > + return 0; > +} > + > static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, > phy_interface_t mode, bool force) > { > @@ -421,6 +511,16 @@ static int mv88e6xxx_port_set_cmode(struct > mv88e6xxx_chip *chip, int port, > case PHY_INTERFACE_MODE_RXAUI: > cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI; > break; > + case PHY_INTERFACE_MODE_5GBASER: > + cmode = MV88E6XXX_PORT_STS_CMODE_5GBASER; > + break; > + case PHY_INTERFACE_MODE_10GBASER: > + case PHY_INTERFACE_MODE_10GKR: > + cmode = MV88E6XXX_PORT_STS_CMODE_10GBASER; > + break; > + case PHY_INTERFACE_MODE_USXGMII: > + cmode = MV88E6XXX_PORT_STS_CMODE_USXGMII; > + break; > default: > cmode = 0; > } > @@ -505,6 +605,15 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip > *chip, int port, > return mv88e6xxx_port_set_cmode(chip, port, mode, false); > } > > +int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, > + phy_interface_t mode) > +{ > + if (port != 0 && port != 9 && port != 10) > + return -EOPNOTSUPP; > + > + return mv88e6xxx_port_set_cmode(chip, port, mode, false); > +} > + > static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip, > int port) > { > @@ -1128,6 +1237,131 @@ int mv88e6xxx_port_disable_pri_override(struct > mv88e6xxx_chip *chip, int port) > return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0); > } > > +/* Offset 0x0E: Policy & MGMT Control Register for FAMILY 6191X 6193X 6393X*/ > + > +static int mv88e6393x_port_policy_write(struct mv88e6xxx_chip *chip, u16 > pointer, > + u8 data) > +{ > + > + int err = 0; > + int port; > + u16 reg; > + > + /* Setup per Port policy register */ > + for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { > + if (dsa_is_unused_port(chip->ds, port)) > + continue; > + > + /* Prevent the use of an invalid port. */ > + if (mv88e6xxx_is_invalid_port(chip, port)) { > + dev_err(chip->dev, "port %d is invalid\n", port); > + err = -EINVAL; > + } > + reg = MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE | pointer | data; > + err = mv88e6xxx_port_write(chip, port, > MV88E6393X_PORT_POLICY_MGMT_CTL, reg); > + } > + return err; > +} > + > +int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip, > + enum mv88e6xxx_egress_direction direction, > + int port) > +{ > + u16 ptr; > + int err; > + > + switch (direction) { > + case MV88E6XXX_EGRESS_DIR_INGRESS: > + ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST; > + err = mv88e6393x_port_policy_write(chip, ptr, port); > + if (err) > + return err; > + break; > + case MV88E6XXX_EGRESS_DIR_EGRESS: > + ptr = MV88E6393X_G2_EGRESS_MONITOR_DEST; > + err = mv88e6xxx_g2_write(chip, ptr, port); > + if (err) > + return err; > + break; > + } > + return 0; > +} > + > +int mv88e6393x_port_set_cpu_dest(struct mv88e6xxx_chip *chip, int port) > +{ > + u16 ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST; > + u8 data = MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI | port; > + > + return mv88e6393x_port_policy_write(chip, ptr, data); > +} > + > +int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) > +{ > + u16 ptr; > + int err; > + > + /* Consider the frames with reserved multicast destination > + * addresses matching 01:80:c2:00:00:00 and > + * 01:80:c2:00:00:02 as MGMT. > + */ > + ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO; > + err = mv88e6393x_port_policy_write(chip, ptr, 0xff); > + if (err) > + return err; > + > + ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI; > + err = mv88e6393x_port_policy_write(chip, ptr, 0xff); > + if (err) > + return err; > + > + ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO; > + err = mv88e6393x_port_policy_write(chip, ptr, 0xff); > + if (err) > + return err; > + > + ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI; > + err = mv88e6393x_port_policy_write(chip, ptr, 0xff); > + if (err) > + return err; > + > + return 0; > +} > + > +/* Offset 0x10 & 0x11: EPC */ > + > +static int mv88e6393x_epc_wait_ready(struct mv88e6xxx_chip *chip, int port) > +{ > + int bit = __bf_shf(MV88E6393X_PORT_EPC_CMD_BUSY); > + > + return mv88e6xxx_port_wait_bit(chip, port, MV88E6393X_PORT_EPC_CMD, > bit, 0); > +} > + > +/* Port Ether type for 6393X family */ > + > +int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, > + u16 etype) > +{ > + u16 val; > + int err; > + > + err = mv88e6393x_epc_wait_ready(chip, port); > + if (err) > + return err; > + > + err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_DATA, etype); > + if (err) > + return err; > + > + val = MV88E6393X_PORT_EPC_CMD_BUSY | > + MV88E6393X_PORT_EPC_CMD_WRITE | > + MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE; > + err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_CMD, val); > + if (err) > + return err; > + > + return 0; > +} > + > /* Offset 0x0f: Port Ether type */ > > int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, > diff --git a/drivers/net/dsa/mv88e6xxx/port.h > b/drivers/net/dsa/mv88e6xxx/port.h > index 44d76ac973f6..fbe16168ac09 100644 > --- a/drivers/net/dsa/mv88e6xxx/port.h > +++ b/drivers/net/dsa/mv88e6xxx/port.h > @@ -49,6 +49,9 @@ > #define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b > #define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c > #define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d > +#define MV88E6XXX_PORT_STS_CMODE_5GBASER 0x000c > +#define MV88E6XXX_PORT_STS_CMODE_10GBASER 0x000d > +#define MV88E6XXX_PORT_STS_CMODE_USXGMII 0x000e > #define MV88E6185_PORT_STS_CDUPLEX 0x0008 > #define MV88E6185_PORT_STS_CMODE_MASK 0x0007 > #define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000 > @@ -117,6 +120,8 @@ > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6176 0x1760 > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900 > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 > +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191X 0x1920 > +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6193X 0x1930 Different indentation (the first uses spaces, the second uses tabs) > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6220 0x2200 > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 > @@ -129,6 +134,7 @@ > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6350 0x3710 > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6351 0x3750 > #define MV88E6XXX_PORT_SWITCH_ID_PROD_6390 0x3900 > +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6393X 0x3930 > #define MV88E6XXX_PORT_SWITCH_ID_REV_MASK 0x000f > > /* Offset 0x04: Port Control Register */ > @@ -236,6 +242,19 @@ > #define MV88E6XXX_PORT_POLICY_CTL_TRAP 0x0002 > #define MV88E6XXX_PORT_POLICY_CTL_DISCARD 0x0003 > > +/* Offset 0x0E: Policy & MGMT Control Register (FAMILY_6393X)*/ Space missing before end of comment */ > +#define MV88E6393X_PORT_POLICY_MGMT_CTL 0x0e > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE 0x8000 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_MASK 0x3f00 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO 0x2000 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI 0x2100 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO 0x2400 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI 0x2500 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST 0x3000 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST 0x3800 > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_DATA_MASK 0x00ff > +#define MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI 0x00e0 > + > /* Offset 0x0F: Port Special Ether Type */ > #define MV88E6XXX_PORT_ETH_TYPE 0x0f > #define MV88E6XXX_PORT_ETH_TYPE_DEFAULT 0x9100 > @@ -243,6 +262,15 @@ > /* Offset 0x10: InDiscards Low Counter */ > #define MV88E6XXX_PORT_IN_DISCARD_LO 0x10 > > +/* Offset 0x10: Extended Port Control Command */ > +#define MV88E6393X_PORT_EPC_CMD 0x10 > +#define MV88E6393X_PORT_EPC_CMD_BUSY 0x8000 > +#define MV88E6393X_PORT_EPC_CMD_WRITE 0x0300 > +#define MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE 0x02 > + > +/* Offset 0x11: Extended Port Control Data */ > +#define MV88E6393X_PORT_EPC_DATA 0x11 > + > /* Offset 0x11: InDiscards High Counter */ > #define MV88E6XXX_PORT_IN_DISCARD_HI 0x11 > > @@ -288,7 +316,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int > port, int reg, > u16 *val); > int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, > u16 val); > - > +int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg, > + int bit, int val); > int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, > int pause); > int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, > @@ -312,7 +341,8 @@ int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip > *chip, int port, > int speed, int duplex); > int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, > int speed, int duplex); > - > +int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, > + int speed, int duplex); > phy_interface_t mv88e6341_port_max_speed_mode(int port); > phy_interface_t mv88e6390_port_max_speed_mode(int port); > phy_interface_t mv88e6390x_port_max_speed_mode(int port); > @@ -346,6 +376,13 @@ int mv88e6352_port_set_policy(struct mv88e6xxx_chip > *chip, int port, > enum mv88e6xxx_policy_action action); > int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, > u16 etype); > +int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip, > + enum mv88e6xxx_egress_direction direction, > + int port); > +int mv88e6393x_port_set_cpu_dest(struct mv88e6xxx_chip *chip, int port); > +int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); > +int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, > + u16 etype); > int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, > bool message_port); > int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port, > @@ -362,6 +399,8 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, > int port, > phy_interface_t mode); > int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, > phy_interface_t mode); > +int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, > + phy_interface_t mode); > 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 mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); > diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c > b/drivers/net/dsa/mv88e6xxx/serdes.c > index 9c07b4f3d345..9148dd0f4555 100644 > --- a/drivers/net/dsa/mv88e6xxx/serdes.c > +++ b/drivers/net/dsa/mv88e6xxx/serdes.c > @@ -526,6 +526,26 @@ u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip > *chip, int port) > return lane; > } > > +/* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address > + * a port is using else Returns -ENODEV. > + */ > +u8 mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) > +{ > + u8 cmode = chip->ports[port].cmode; > + u8 lane = 0; > + > + if (port == 0 || port == 9 || port == 10) { > + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || > + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || > + cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX || > + cmode == MV88E6XXX_PORT_STS_CMODE_5GBASER || > + cmode == MV88E6XXX_PORT_STS_CMODE_10GBASER || > + cmode == MV88E6XXX_PORT_STS_CMODE_USXGMII) > + lane = port; > + } > + return lane; > +} > + > /* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */ > static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane, > bool up) > @@ -916,6 +936,51 @@ static int mv88e6390_serdes_irq_status_sgmii(struct > mv88e6xxx_chip *chip, > return err; > } > > +int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, > + u8 lane, bool enable) > +{ > + u8 cmode = chip->ports[port].cmode; > + int err = 0; > + > + switch (cmode) { > + case MV88E6XXX_PORT_STS_CMODE_SGMII: > + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: > + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: > + case MV88E6XXX_PORT_STS_CMODE_5GBASER: > + case MV88E6XXX_PORT_STS_CMODE_10GBASER: > + err = mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable); > + } > + > + return err; > +} > + > +irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int > port, > + u8 lane) > +{ > + u8 cmode = chip->ports[port].cmode; > + irqreturn_t ret = IRQ_NONE; > + u16 status; > + int err; > + > + switch (cmode) { > + case MV88E6XXX_PORT_STS_CMODE_SGMII: > + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: > + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: > + case MV88E6XXX_PORT_STS_CMODE_5GBASER: > + case MV88E6XXX_PORT_STS_CMODE_10GBASER: > + err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); > + if (err) > + return ret; > + if (status & (MV88E6390_SGMII_INT_LINK_DOWN | > + MV88E6390_SGMII_INT_LINK_UP)) { > + ret = IRQ_HANDLED; > + mv88e6390_serdes_irq_link_sgmii(chip, port, lane); > + } > + } > + > + return ret; > +} > + > irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int > port, > u8 lane) > { > @@ -999,3 +1064,163 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip > *chip, int port, void *_p) > p[i] = reg; > } > } > + > +int mv88e6393x_setup_errata(struct mv88e6xxx_chip *chip) > +{ > + u16 config0, config9, config10; > + u16 pcs0, pcs9, pcs10; > + int err = 0; > + > + /* mv88e6393x family errata 3.8 : > + * When a SERDES port is operating in 1000BASE-X or SGMII mode link may > not > + * come up after hardware reset or software reset of SERDES core. > + * Workaround is to write SERDES register 4.F074.14 =1 for only those > modes > + * and 0 in all other modes. > + */ > + err = mv88e6390_serdes_read(chip, MV88E6393X_PORT0_LANE, MDIO_MMD_PHYXS, > + MV88E6393X_ERRATA_1000BASEX_SGMII, > &config0); > + err = mv88e6390_serdes_read(chip, MV88E6393X_PORT9_LANE, MDIO_MMD_PHYXS, > + MV88E6393X_ERRATA_1000BASEX_SGMII, > &config9); > + err = mv88e6390_serdes_read(chip, MV88E6393X_PORT10_LANE, > MDIO_MMD_PHYXS, > + MV88E6393X_ERRATA_1000BASEX_SGMII, > &config10); > + > + err = mv88e6390_serdes_read(chip, MV88E6393X_PORT0_LANE, MDIO_MMD_PHYXS, > + MV88E6393X_SERDES_POC, &pcs0); > + pcs0 &= MV88E6393X_SERDES_POC_PCS_MODE_MASK; > + err = mv88e6390_serdes_read(chip, MV88E6393X_PORT9_LANE, MDIO_MMD_PHYXS, > + MV88E6393X_SERDES_POC, &pcs9); > + pcs9 &= MV88E6393X_SERDES_POC_PCS_MODE_MASK; > + err = mv88e6390_serdes_read(chip, MV88E6393X_PORT10_LANE, > MDIO_MMD_PHYXS, > + MV88E6393X_SERDES_POC, &pcs10); > + pcs10 &= MV88E6393X_SERDES_POC_PCS_MODE_MASK; > + > + if (pcs0 == MV88E6393X_PCS_SELECT_1000BASEX || > + pcs0 == MV88E6393X_PCS_SELECT_SGMII_PHY || > + pcs0 == MV88E6393X_PCS_SELECT_SGMII_MAC) { > + config0 |= MV88E6393X_ERRATA_1000BASEX_SGMII_BIT; > + err = mv88e6390_serdes_write(chip, MV88E6393X_PORT0_LANE, > + MDIO_MMD_PHYXS, > + > MV88E6393X_ERRATA_1000BASEX_SGMII, > + config0); > + } else { > + config0 &= ~MV88E6393X_ERRATA_1000BASEX_SGMII_BIT; > + err = mv88e6390_serdes_write(chip, MV88E6393X_PORT0_LANE, > + MDIO_MMD_PHYXS, > + > MV88E6393X_ERRATA_1000BASEX_SGMII, > + config0); > + } > + > + if (pcs9 == MV88E6393X_PCS_SELECT_1000BASEX || > + pcs9 == MV88E6393X_PCS_SELECT_SGMII_PHY || > + pcs9 == MV88E6393X_PCS_SELECT_SGMII_MAC) { > + config9 |= MV88E6393X_ERRATA_1000BASEX_SGMII_BIT; > + err = mv88e6390_serdes_write(chip, MV88E6393X_PORT9_LANE, > + MDIO_MMD_PHYXS, > + > MV88E6393X_ERRATA_1000BASEX_SGMII, > + config9); > + } else { > + config9 &= ~MV88E6393X_ERRATA_1000BASEX_SGMII_BIT; > + err = mv88e6390_serdes_write(chip, MV88E6393X_PORT9_LANE, > + MDIO_MMD_PHYXS, > + > MV88E6393X_ERRATA_1000BASEX_SGMII, > + config9); > + } > + > + if (pcs10 == MV88E6393X_PCS_SELECT_1000BASEX || > + pcs10 == MV88E6393X_PCS_SELECT_SGMII_PHY || > + pcs10 == MV88E6393X_PCS_SELECT_SGMII_MAC) { > + config10 |= MV88E6393X_ERRATA_1000BASEX_SGMII_BIT; > + err = mv88e6390_serdes_write(chip, MV88E6393X_PORT10_LANE, > + MDIO_MMD_PHYXS, > + > MV88E6393X_ERRATA_1000BASEX_SGMII, > + config10); > + } else { > + config10 &= ~MV88E6393X_ERRATA_1000BASEX_SGMII_BIT; > + err = mv88e6390_serdes_write(chip, MV88E6393X_PORT10_LANE, > + MDIO_MMD_PHYXS, > + > MV88E6393X_ERRATA_1000BASEX_SGMII, > + config10); > + } > + return err; > +} > + > +static int mv88e6393x_serdes_port_config(struct mv88e6xxx_chip *chip, u8 > lane, > + bool on) > +{ > + u8 cmode = chip->ports[lane].cmode; > + u16 config, pcs; > + > + switch (cmode) { > + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: > + pcs = MV88E6393X_PCS_SELECT_1000BASEX; > + break; > + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: > + pcs = MV88E6393X_PCS_SELECT_2500BASEX; > + break; > + case MV88E6XXX_PORT_STS_CMODE_10GBASER: > + pcs = MV88E6393X_PCS_SELECT_10GBASER; > + break; > + default: > + pcs = MV88E6393X_PCS_SELECT_1000BASEX; > + break; > + } > + > + if (on) { > + /* mv88e6393x family errata 3.6 : > + * When changing c_mode on Port 0 from [x]MII mode to any > + * SERDES mode SERDES will not be operational. > + * Workaround: Set Port0 SERDES register 4.F002.5=0 > + */ > + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, > + MV88E6393X_SERDES_POC, &config); > + config &= ~(MV88E6393X_SERDES_POC_PCS_MODE_MASK | > + MV88E6393X_SERDES_POC_PDOWN); > + config |= pcs; > + mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, > + MV88E6393X_SERDES_POC, config); > + config |= MV88E6393X_SERDES_POC_RESET; > + mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, > + MV88E6393X_SERDES_POC, config); > + > + /* mv88e6393x family errata 3.7 : > + * When changing cmode on SERDES port from any other mode to > + * 1000BASE-X mode the link may not come up due to invalid > + * 1000BASE-X advertisement. > + * Workaround: Correct advertisement and reset PHY core. > + */ > + config = MV88E6390_SGMII_ANAR_1000BASEX_FD; > + mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, > + MV88E6390_SGMII_ANAR, config); > + > + /* soft reset the PCS/PMA */ > + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, > + MV88E6390_SGMII_CONTROL, &config); > + config |= MV88E6390_SGMII_CONTROL_RESET; > + mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, > + MV88E6390_SGMII_CONTROL, config); > + } > + > + return 0; > +} > + > +int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, > + bool on) > +{ > + lane = mv88e6393x_serdes_get_lane(chip, port); > + > + if (port == 0 || port == 9 || port == 10) { > + u8 cmode = chip->ports[port].cmode; > + > + mv88e6393x_serdes_port_config(chip, lane, on); > + > + switch (cmode) { > + case MV88E6XXX_PORT_STS_CMODE_1000BASEX: > + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: > + return mv88e6390_serdes_power_sgmii(chip, lane, on); > + case MV88E6XXX_PORT_STS_CMODE_10GBASER: > + return mv88e6390_serdes_power_10g(chip, lane, on); > + } > + } > + > + return 0; > +} > diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h > b/drivers/net/dsa/mv88e6xxx/serdes.h > index 14315f26228a..c3eaaaac8619 100644 > --- a/drivers/net/dsa/mv88e6xxx/serdes.h > +++ b/drivers/net/dsa/mv88e6xxx/serdes.h > @@ -68,15 +68,47 @@ > #define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10) > #define MV88E6390_SGMII_PHY_STATUS_TX_PAUSE BIT(3) > #define MV88E6390_SGMII_PHY_STATUS_RX_PAUSE BIT(2) > +#define MV88E6390_SGMII_STATUS_AN_ABLE BIT(3) > +#define MV88E6390_SGMII_ANAR 0x2004 > +#define MV88E6390_SGMII_ANAR_1000BASEX_FD BIT(5) > +#define MV88E6390_SGMII_CONTROL 0x2000 > +#define MV88E6390_SGMII_CONTROL_RESET BIT(15) > +#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14) > +#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11) > +#define MV88E6390_SGMII_STATUS 0x2001 > > /* Packet generator pad packet checker */ > #define MV88E6390_PG_CONTROL 0xf010 > #define MV88E6390_PG_CONTROL_ENABLE_PC BIT(0) > > +#define MV88E6393X_PORT0_LANE 0x00 > +#define MV88E6393X_PORT9_LANE 0x09 > +#define MV88E6393X_PORT10_LANE 0x0a > + > +/* Port Operational Configuration */ > +#define MV88E6393X_PCS_SELECT_1000BASEX 0x0000 > +#define MV88E6393X_PCS_SELECT_2500BASEX 0x0001 > +#define MV88E6393X_PCS_SELECT_SGMII_PHY 0x0002 > +#define MV88E6393X_PCS_SELECT_SGMII_MAC 0x0003 > +#define MV88E6393X_PCS_SELECT_5GBASER 0x0004 > +#define MV88E6393X_PCS_SELECT_10GBASER 0x0005 > +#define MV88E6393X_PCS_SELECT_USXGMII_PHY 0x0006 > +#define MV88E6393X_PCS_SELECT_USXGMII_MAC 0x0007 > + > +#define MV88E6393X_SERDES_POC 0xf002 > +#define MV88E6393X_SERDES_POC_PCS_MODE_MASK 0x0007 > +#define MV88E6393X_SERDES_POC_RESET BIT(15) > +#define MV88E6393X_SERDES_POC_PDOWN BIT(5) > +#define MV88E6393X_SERDES_POC_ANEG BIT(3) > + > +#define MV88E6393X_ERRATA_1000BASEX_SGMII 0xF074 > +#define MV88E6393X_ERRATA_1000BASEX_SGMII_BIT BIT(14) > + > u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); > u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); > u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); > u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); > +u8 mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); > int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, > u8 lane, unsigned int mode, > phy_interface_t interface, > @@ -105,14 +137,21 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, > int port, u8 lane, > bool on); > int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, > bool on); > +int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, > + bool on); > +int mv88e6393x_setup_errata(struct mv88e6xxx_chip *chip); > int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 > lane, > bool enable); > int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 > lane, > bool enable); > +int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, > + u8 lane, bool enable); > irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int > port, > u8 lane); > irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int > port, > u8 lane); > +irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int > port, > + u8 lane); > int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); > int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, > int port, uint8_t *data);