From f613e4249f5e1ced7cc047b4e96df09e7a322fd7 Mon Sep 17 00:00:00 2001 From: Jan Sondhauss Date: Mon, 15 Sep 2025 09:04:49 +0200 Subject: [PATCH] net: dsa: ksz9477: add ethtool regdump functionality This allows for the switches registers to be dumped via the ethtool interface. Compared to the ksz8 implemention only the registers are exposed. Dumping of the fdb and stat mac table can be added once the need arises. The reg-dump uses the reg_map of the driver to select which registers to dump. Signed-off-by: Jan Sondhauss --- drivers/net/dsa/microchip/Makefile | 1 + drivers/net/dsa/microchip/ksz8_ethtool.c | 152 +++++++++----------- drivers/net/dsa/microchip/ksz8_ethtool.h | 2 + drivers/net/dsa/microchip/ksz9477_ethtool.c | 86 +++++++++++ drivers/net/dsa/microchip/ksz9477_ethtool.h | 14 ++ drivers/net/dsa/microchip/ksz_common.c | 3 + drivers/net/dsa/microchip/ksz_ethtool.h | 60 ++++++++ 7 files changed, 232 insertions(+), 86 deletions(-) create mode 100644 drivers/net/dsa/microchip/ksz9477_ethtool.c create mode 100644 drivers/net/dsa/microchip/ksz9477_ethtool.h create mode 100644 drivers/net/dsa/microchip/ksz_ethtool.h diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index 2f129b30e851..1b0d4ec4c1c8 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -10,6 +10,7 @@ ksz_switch-objs += ksz_ptp.o endif ksz_switch-objs += ksz8_ethtool.o +ksz_switch-objs += ksz9477_ethtool.o ksz_switch-objs += ksz_devlink.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o diff --git a/drivers/net/dsa/microchip/ksz8_ethtool.c b/drivers/net/dsa/microchip/ksz8_ethtool.c index ee1caa4754a5..9ef4095f284f 100644 --- a/drivers/net/dsa/microchip/ksz8_ethtool.c +++ b/drivers/net/dsa/microchip/ksz8_ethtool.c @@ -2,64 +2,17 @@ #include "ksz8_ethtool.h" #include "ksz8.h" - -enum { - KSZ8_REGDUMP_ID_GLOBAL = 0x1100, - KSZ8_REGDUMP_ID_PORT = 0x1200, - KSZ8_REGDUMP_ID_STAT_MAC_TBL = 0x2000, - KSZ8_REGDUMP_ID_FDB = 0x2100, -}; - -struct ksz_regdump_hdr { - u32 module_id; - u32 len; -}; - -struct ksz_regdump_record { - struct ksz_regdump_hdr hdr; - u32 start_off; - u32 end_off; -}; - -// creates a regdump record entry -// note we use u32 to transport register values, despite their size in -// the device -#define KSZ_REGDUMP_ENTRY(id, start, end) { \ - .hdr.module_id = (id), \ - .hdr.len = (((end) - (start)) * sizeof(u32)) * 2 \ - + sizeof(struct ksz_regdump_hdr), \ - .start_off = (start), \ - .end_off = (end), \ -} - -#define SIZE_IS_MULTIPLE_OF(T, T2) (sizeof(T) == (DIV_ROUND_UP(sizeof(T), sizeof(T2))* sizeof(T2))) - -// sizeof(ksz8_dyn_mac_entry) == 12 -struct ksz8_dyn_mac_entry { - /*entry 1*/ - u32 : 24; - u8 timestamp; - /* entry 2 & 3 */ - u8 fid; - u8 src_port; - u8 mac[ETH_ALEN]; -}; - -#define KSZ8_REGDUMP_MAC_ENTRY(id, entry_size, max_entries) { \ - .hdr.module_id = (id), \ - .hdr.len = 2 * sizeof(u32) + (entry_size) * (max_entries) \ - + sizeof(struct ksz_regdump_hdr), \ - .start_off = 0, \ - .end_off = 0, \ -} +#include "ksz_ethtool.h" static const struct ksz_regdump_record ksz8_regdump_records[] = { - KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_GLOBAL, 0x00, 0x10), - KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_PORT, 0x10, 0x20), - KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_PORT + 1, 0x20, 0x30), - KSZ_REGDUMP_ENTRY(KSZ8_REGDUMP_ID_PORT + 2, 0x30, 0x40), - KSZ8_REGDUMP_MAC_ENTRY(KSZ8_REGDUMP_ID_STAT_MAC_TBL, sizeof(struct alu_struct), 8), - KSZ8_REGDUMP_MAC_ENTRY(KSZ8_REGDUMP_ID_FDB, sizeof(struct ksz8_dyn_mac_entry), 1024), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_GLOBAL, 0x00, 0x10), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT, 0x10, 0x20), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 1, 0x20, 0x30), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 2, 0x30, 0x40), + KSZ8_REGDUMP_MAC_ENTRY(KSZ_REGDUMP_ID_STAT_MAC_TBL, + sizeof(struct alu_struct), 8), + KSZ8_REGDUMP_MAC_ENTRY(KSZ_REGDUMP_ID_FDB, + sizeof(struct ksz8_dyn_mac_entry), 1024), }; int ksz8_get_regs_len(struct ksz_device *dev, int port) @@ -72,20 +25,22 @@ int ksz8_get_regs_len(struct ksz_device *dev, int port) return length; } -static void ksz8_get_regs_dump_static_mac_table(struct ksz_device *dev, u32 *reg, u32 *pos) +static void ksz8_get_regs_dump_static_mac_table(struct ksz_device *dev, + u32 *reg, u32 *pos) { BUILD_BUG_ON(!SIZE_IS_MULTIPLE_OF(struct alu_struct, u32)); int index, ret; u32 cnt = *pos; - reg[cnt++] = KSZ8_REGDUMP_ID_STAT_MAC_TBL; + struct ksz_regdump_hdr *hdr = (struct ksz_regdump_hdr *)®[cnt]; - const u32 pos_length = cnt++; - reg[pos_length] = sizeof(struct ksz_regdump_hdr); + hdr->module_id = KSZ_REGDUMP_ID_STAT_MAC_TBL; + hdr->len = sizeof(struct ksz_regdump_hdr); + cnt += 2; for (index = 0; index < dev->info->num_statics; index++) { bool valid; - struct alu_struct* alu = (struct alu_struct*)®[cnt]; + struct alu_struct *alu = (struct alu_struct *)®[cnt]; ret = ksz8_r_sta_mac_table(dev, index, alu, &valid); if (ret) @@ -94,7 +49,7 @@ static void ksz8_get_regs_dump_static_mac_table(struct ksz_device *dev, u32 *reg if (!valid) continue; - reg[pos_length] += sizeof(struct alu_struct); + hdr->len += sizeof(struct alu_struct); cnt += sizeof(struct alu_struct) / sizeof(u32); } *pos = cnt; @@ -107,55 +62,80 @@ static void ksz8_get_regs_dump_fdb(struct ksz_device *dev, u32 *reg, u32 *pos) u16 entries = 0; u32 cnt = *pos; - reg[cnt++] = KSZ8_REGDUMP_ID_FDB; + struct ksz_regdump_hdr *hdr = (struct ksz_regdump_hdr *)®[cnt]; - const u32 pos_length = cnt++; - reg[pos_length] = sizeof(struct ksz_regdump_hdr); + hdr->module_id = KSZ_REGDUMP_ID_FDB; + hdr->len = sizeof(struct ksz_regdump_hdr); + cnt += 2; do { - struct ksz8_dyn_mac_entry *entry = (struct ksz8_dyn_mac_entry*) ®[cnt]; - int ret = ksz8_r_dyn_mac_table(dev, i, entry->mac, &entry->fid, &entry->src_port, - &entry->timestamp, &entries); + struct ksz8_dyn_mac_entry *entry = + (struct ksz8_dyn_mac_entry *)®[cnt]; + int ret = ksz8_r_dyn_mac_table(dev, i, entry->mac, &entry->fid, + &entry->src_port, + &entry->timestamp, &entries); if (!ret) { - reg[pos_length] += sizeof(struct ksz8_dyn_mac_entry); - cnt += DIV_ROUND_UP(sizeof(struct ksz8_dyn_mac_entry), sizeof(u32)); + hdr->len += sizeof(struct ksz8_dyn_mac_entry); + cnt += DIV_ROUND_UP(sizeof(struct ksz8_dyn_mac_entry), + sizeof(u32)); } i++; } while (i < entries); *pos = cnt; } -void ksz8_get_regs(struct ksz_device *dev, int port, - struct ethtool_regs *regs, void *p) +static void ksz_dump_regmap_range(struct ksz_device *dev, u32 *regs, u32 *pos, + u32 start, u32 end) { - int i, j; + int i, length = *pos; + + const struct regmap_range *yes_ranges = dev->info->rd_table->yes_ranges; + for (i = 0; i < dev->info->rd_table->n_yes_ranges; i++) { + const struct regmap_range range = yes_ranges[i]; + u32 reg; + if (range.range_max < start || range.range_min >= end) + continue; + + for (reg = range.range_min; reg <= range.range_max; reg++) { + u8 val; + ksz_read8(dev, reg, &val); + + regs[length++] = reg; + regs[length++] = val; + } + } + + *pos = length; +} + +void ksz8_get_regs(struct ksz_device *dev, int port, struct ethtool_regs *regs, + void *p) +{ + int i; u32 pos = 0; u32 *reg = p; for (i = 0; i < ARRAY_SIZE(ksz8_regdump_records); i++) { struct ksz_regdump_record rec = ksz8_regdump_records[i]; + struct ksz_regdump_hdr *hdr = + (struct ksz_regdump_hdr *)®[pos]; - if (rec.hdr.module_id == KSZ8_REGDUMP_ID_STAT_MAC_TBL) { + if (rec.hdr.module_id == KSZ_REGDUMP_ID_STAT_MAC_TBL) { ksz8_get_regs_dump_static_mac_table(dev, reg, &pos); continue; } - if (rec.hdr.module_id == KSZ8_REGDUMP_ID_FDB) { + if (rec.hdr.module_id == KSZ_REGDUMP_ID_FDB) { ksz8_get_regs_dump_fdb(dev, reg, &pos); continue; } - reg[pos++] = rec.hdr.module_id; - reg[pos++] = rec.hdr.len; - - for (j = rec.start_off; - j < rec.end_off; j++) { - u8 val; - - reg[pos++] = j; - ksz_read8(dev, j, &val); - reg[pos++] = val; - } + pos += 2; + hdr->module_id = rec.hdr.module_id; + hdr->len = pos; + ksz_dump_regmap_range(dev, reg, &pos, rec.start_off, + rec.end_off); + hdr->len = (pos - hdr->len + 2) * sizeof(u32); } } diff --git a/drivers/net/dsa/microchip/ksz8_ethtool.h b/drivers/net/dsa/microchip/ksz8_ethtool.h index 84e28e4d55c2..4296524ccf12 100644 --- a/drivers/net/dsa/microchip/ksz8_ethtool.h +++ b/drivers/net/dsa/microchip/ksz8_ethtool.h @@ -4,8 +4,10 @@ #define __KSZ8XXX_ETHTOOL_H #include +#include "ksz_ethtool.h" #include "ksz_common.h" + int ksz8_get_regs_len(struct ksz_device *dev, int port); void ksz8_get_regs(struct ksz_device *dev, int port, struct ethtool_regs *regs, void *p); diff --git a/drivers/net/dsa/microchip/ksz9477_ethtool.c b/drivers/net/dsa/microchip/ksz9477_ethtool.c new file mode 100644 index 000000000000..2ecf3e7d2fef --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_ethtool.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ksz9477_ethtool.h" +#include "ksz_ethtool.h" + +static const struct ksz_regdump_record ksz8_regdump_records[] = { + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_GLOBAL, 0x00, 0x700), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 0, 0x1000, 0x1fff), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 1, 0x2000, 0x2fff), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 2, 0x3000, 0x3fff), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 3, 0x4000, 0x4fff), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 4, 0x5000, 0x5fff), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 5, 0x6000, 0x6fff), + KSZ_REGDUMP_ENTRY(KSZ_REGDUMP_ID_PORT + 6, 0x7000, 0x7fff), +}; + +int ksz9477_get_regs_len(struct ksz_device *dev, int port) +{ + int i, length = 0; + + const struct regmap_range *yes_ranges = dev->info->rd_table->yes_ranges; + + for (i = 0; i < dev->info->rd_table->n_yes_ranges; i++) { + const struct regmap_range range = yes_ranges[i]; + length += (range.range_max - range.range_min + 1) * + sizeof(u32) * 2; + } + // ports + global + stat + fdb + length += (7 + 1 + 1 + 1) * sizeof(struct ksz_regdump_hdr); + + return length; +} + +static void ksz_dump_regmap_range(struct ksz_device *dev, u32 *regs, u32 *pos, + u32 start, u32 end) +{ + int i, length = *pos; + + const struct regmap_range *yes_ranges = dev->info->rd_table->yes_ranges; + + for (i = 0; i < dev->info->rd_table->n_yes_ranges; i++) { + const struct regmap_range range = yes_ranges[i]; + u32 reg; + if (range.range_max < start || range.range_min >= end) + continue; + + for (reg = range.range_min; reg <= range.range_max; reg++) { + u8 val; + ksz_read8(dev, reg, &val); + + regs[length++] = reg; + regs[length++] = val; + } + } + + *pos = length; +} + +void ksz9477_get_regs(struct ksz_device *dev, int port, + struct ethtool_regs *regs, void *p) +{ + int i; + u32 pos = 0; + u32 *reg = p; + + for (i = 0; i < ARRAY_SIZE(ksz8_regdump_records); i++) { + struct ksz_regdump_record rec = ksz8_regdump_records[i]; + struct ksz_regdump_hdr *hdr = + (struct ksz_regdump_hdr *)®[pos]; + hdr->module_id = rec.hdr.module_id; + pos += 2; + + if (rec.hdr.module_id == KSZ_REGDUMP_ID_STAT_MAC_TBL) { + continue; + } + + if (rec.hdr.module_id == KSZ_REGDUMP_ID_FDB) { + continue; + } + + hdr->len = pos; + ksz_dump_regmap_range(dev, reg, &pos, rec.start_off, + rec.end_off); + hdr->len = (pos - hdr->len + 2) * sizeof(u32); + } +} diff --git a/drivers/net/dsa/microchip/ksz9477_ethtool.h b/drivers/net/dsa/microchip/ksz9477_ethtool.h new file mode 100644 index 000000000000..88106c37343c --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_ethtool.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef __KSZ9477_ETHTOOL_H +#define __KSZ9477_ETHTOOL_H + +#include +#include "ksz_ethtool.h" +#include "ksz_common.h" + +int ksz9477_get_regs_len(struct ksz_device *dev, int port); +void ksz9477_get_regs(struct ksz_device *dev, int port, + struct ethtool_regs *regs, void *p); + +#endif diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index e67b080d1d41..ba7e8fc00504 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -33,6 +33,7 @@ #include "lan937x.h" #include "ksz_devlink.h" #include "ksz8_ethtool.h" +#include "ksz9477_ethtool.h" #define MIB_COUNTER_NUM 0x20 @@ -270,6 +271,8 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .reset = ksz9477_reset_switch, .init = ksz9477_switch_init, .exit = ksz9477_switch_exit, + .get_regs = ksz9477_get_regs, + .get_regs_len = ksz9477_get_regs_len, }; static const struct ksz_dev_ops lan937x_dev_ops = { diff --git a/drivers/net/dsa/microchip/ksz_ethtool.h b/drivers/net/dsa/microchip/ksz_ethtool.h new file mode 100644 index 000000000000..92f26c6794c1 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz_ethtool.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef __KSZ_ETHTOOL_H +#define __KSZ_ETHTOOL_H + +#include +#include + +// sizeof(ksz8_dyn_mac_entry) == 12 +struct ksz8_dyn_mac_entry { + /*entry 1*/ + u32 : 24; + u8 timestamp; + /* entry 2 & 3 */ + u8 fid; + u8 src_port; + u8 mac[ETH_ALEN]; +}; + +struct ksz_regdump_hdr { + u32 module_id; + u32 len; +}; + +struct ksz_regdump_record { + struct ksz_regdump_hdr hdr; + u32 start_off; + u32 end_off; +}; + +enum { + KSZ_REGDUMP_ID_GLOBAL = 0x1100, + KSZ_REGDUMP_ID_PORT = 0x1200, + KSZ_REGDUMP_ID_STAT_MAC_TBL = 0x2000, + KSZ_REGDUMP_ID_FDB = 0x2100, +}; + +// creates a regdump record entry +// note we use u32 to transport register values, despite their size in +// the device +#define KSZ_REGDUMP_ENTRY(id, start, end) { \ + .hdr.module_id = (id), \ + .hdr.len = (((end) - (start)) * sizeof(u32)) * 2 \ + + sizeof(struct ksz_regdump_hdr), \ + .start_off = (start), \ + .end_off = (end), \ +} + +#define SIZE_IS_MULTIPLE_OF(T, T2) (sizeof(T) == (DIV_ROUND_UP(sizeof(T), sizeof(T2))* sizeof(T2))) + +#define KSZ8_REGDUMP_MAC_ENTRY(id, entry_size, max_entries) { \ + .hdr.module_id = (id), \ + .hdr.len = 2 * sizeof(u32) + (entry_size) * (max_entries) \ + + sizeof(struct ksz_regdump_hdr), \ + .start_off = 0, \ + .end_off = 0, \ +} + +#endif +