From 0d30bbd03d8483548bed1fc1e96fc6cfd10248b1 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Feb 2020 18:54:13 +0100 Subject: [PATCH 1/3] net: dsa: mv88e6xxx: Allow PCS registers to be retrieved via ethtool ethtool provides a generic mechanism for a driver to return the registers of an ethernet device. DSA uses this to give the port registers associated with an interfaces. Extend this to allow PCS registers to also be returned, if the port has a PCS associated to it. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 12 +++++++++++- drivers/net/dsa/mv88e6xxx/chip.h | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8c9289549688..316758a42a67 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1018,7 +1018,14 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { - return 32 * sizeof(u16); + struct mv88e6xxx_chip *chip = ds->priv; + int len; + + len = 32 * sizeof(u16); + if (chip->info->ops->serdes_get_regs_len) + len += chip->info->ops->serdes_get_regs_len(chip, port); + + return len; } static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, @@ -1043,6 +1050,9 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, p[i] = reg; } + if (chip->info->ops->serdes_get_regs) + chip->info->ops->serdes_get_regs(chip, port, &p[i]); + mv88e6xxx_reg_unlock(chip); } diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 79cad5e751c6..851686b45414 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -517,6 +517,11 @@ struct mv88e6xxx_ops { int (*serdes_get_stats)(struct mv88e6xxx_chip *chip, int port, uint64_t *data); + /* SERDES registers for ethtool */ + int (*serdes_get_regs_len)(struct mv88e6xxx_chip *chip, int port); + void (*serdes_get_regs)(struct mv88e6xxx_chip *chip, int port, + void *_p); + /* Address Translation Unit operations */ int (*atu_get_hash)(struct mv88e6xxx_chip *chip, u8 *hash); int (*atu_set_hash)(struct mv88e6xxx_chip *chip, u8 hash); From d3f88a24b28dbe6772049693706c23322067e93b Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Feb 2020 18:54:14 +0100 Subject: [PATCH 2/3] net: dsa: mv88e6xxx: Add 6352 family PCS registers to ethtool -d The mv88e6352 has one PCS which can be used for 1000BaseX or SGMII. Add the registers to the dump for the port which the PCS is associated to. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 8 ++++++++ drivers/net/dsa/mv88e6xxx/serdes.c | 23 +++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/serdes.h | 3 +++ 3 files changed, 34 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 316758a42a67..cb284eb505c0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3674,6 +3674,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_power = mv88e6352_serdes_power, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3768,6 +3770,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_enable = mv88e6352_serdes_irq_enable, .serdes_irq_status = mv88e6352_serdes_irq_status, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, }; @@ -4018,6 +4022,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_enable = mv88e6352_serdes_irq_enable, .serdes_irq_status = mv88e6352_serdes_irq_status, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -4398,6 +4404,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_sset_count = mv88e6352_serdes_get_sset_count, .serdes_get_strings = mv88e6352_serdes_get_strings, .serdes_get_stats = mv88e6352_serdes_get_stats, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .phylink_validate = mv88e6352_phylink_validate, }; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 8d8b3b74aee1..94704af224c8 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -237,6 +237,29 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ); } +int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) +{ + if (!mv88e6352_port_has_serdes(chip, port)) + return 0; + + return 32 * sizeof(u16); +} + +void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) +{ + u16 *p = _p; + u16 reg; + int i; + + if (!mv88e6352_port_has_serdes(chip, port)) + return; + + for (i = 0 ; i < 32; i++) { + mv88e6352_serdes_read(chip, i, ®); + p[i] = reg; + } +} + u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index d16ef4da20b0..bb06108ca0bc 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -109,6 +109,9 @@ int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data); +int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); +void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); + /* Return the (first) SERDES lane address a port is using, 0 otherwise. */ static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) From bf3504cea7d7ec834b2d074ac56de41857bbbd07 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Feb 2020 18:54:15 +0100 Subject: [PATCH 3/3] net: dsa: mv88e6xxx: Add 6390 family PCS registers to ethtool -d The mv88e6390 has upto 8 sets of PCS registers, depending on how ports 9 and 10 are configured. The can be spread over 8 ports. If a port has a PCS register set, return it along with the port registers. The register space is sparse, so hard code a list of registers which will be returned. It can later be extended, if needed, by append to the end of the list. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 12 +++++++ drivers/net/dsa/mv88e6xxx/serdes.c | 54 ++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/serdes.h | 2 ++ 3 files changed, 68 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index cb284eb505c0..4ec09cc8dcdc 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3861,6 +3861,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_validate = mv88e6390_phylink_validate, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390_phylink_validate, @@ -3915,6 +3917,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_validate = mv88e6390_phylink_validate, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390x_phylink_validate, @@ -3968,6 +3972,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_validate = mv88e6390_phylink_validate, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -4119,6 +4125,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_validate = mv88e6390_phylink_validate, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, @@ -4464,6 +4472,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_validate = mv88e6390_phylink_validate, }; @@ -4519,6 +4529,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 94704af224c8..238219787233 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -675,3 +675,57 @@ unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) { return irq_find_mapping(chip->g2_irq.domain, port); } + +static const u16 mv88e6390_serdes_regs[] = { + /* SERDES common registers */ + 0xf00a, 0xf00b, 0xf00c, + 0xf010, 0xf011, 0xf012, 0xf013, + 0xf016, 0xf017, 0xf018, + 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, + 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027, + 0xf028, 0xf029, + 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, + 0xf038, 0xf039, + /* SGMII */ + 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, + 0x2008, + 0x200f, + 0xa000, 0xa001, 0xa002, 0xa003, + /* 10Gbase-X */ + 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, + 0x1008, + 0x100e, 0x100f, + 0x1018, 0x1019, + 0x9000, 0x9001, 0x9002, 0x9003, 0x9004, + 0x9006, + 0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016, + /* 10Gbase-R */ + 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, + 0x1028, 0x1029, 0x102a, 0x102b, +}; + +int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) +{ + if (mv88e6xxx_serdes_get_lane(chip, port) == 0) + return 0; + + return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16); +} + +void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) +{ + u16 *p = _p; + int lane; + u16 reg; + int i; + + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane == 0) + return; + + for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) { + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + mv88e6390_serdes_regs[i], ®); + p[i] = reg; + } +} diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index bb06108ca0bc..1906b3ab29c6 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -111,6 +111,8 @@ int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); +int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); +void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); /* Return the (first) SERDES lane address a port is using, 0 otherwise. */ static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,