net: dsa: microchip: add implementation for ethtool get_regs

The return regs-values follow a simple protocol, with a header identifying
the type of reg-values and their length. Each reg-value consists of the
register number and its value. In order to decode the result in userspace
a small helper tool can be used.

For the stat-mac-table and the dyn-mac table, the corresponding structs
are added to the get-regs data.

TI has the same approach for their am65-cpsw-nuss driver.
Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
This commit is contained in:
Jan Sondhauss
2025-04-09 07:24:14 +02:00
parent fd36c65028
commit 386fba02bb
7 changed files with 209 additions and 4 deletions
+1
View File
@@ -9,6 +9,7 @@ ifdef CONFIG_NET_DSA_MICROCHIP_KSZ_PTP
ksz_switch-objs += ksz_ptp.o
endif
ksz_switch-objs += ksz8_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
+2 -1
View File
@@ -56,5 +56,6 @@ int ksz8_reset_switch(struct ksz_device *dev);
int ksz8_switch_init(struct ksz_device *dev);
void ksz8_switch_exit(struct ksz_device *dev);
int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu);
int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr,
struct alu_struct *alu, bool *valid);
#endif
+3 -2
View File
@@ -26,6 +26,7 @@
#include "ksz8795_reg.h"
#include "ksz8.h"
static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
{
regmap_update_bits(ksz_regmap_8(dev), addr, bits, set ? bits : 0);
@@ -471,8 +472,8 @@ int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr,
return rc;
}
static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr,
struct alu_struct *alu, bool *valid)
int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr,
struct alu_struct *alu, bool *valid)
{
u32 data_hi, data_lo;
const u8 *shifts;
+161
View File
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#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, \
}
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),
};
int ksz8_get_regs_len(struct ksz_device *dev, int port)
{
int i, length = 0;
for (i = 0; i < ARRAY_SIZE(ksz8_regdump_records); i++)
length += ksz8_regdump_records[i].hdr.len;
return length;
}
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;
const u32 pos_length = cnt++;
reg[pos_length] = sizeof(struct ksz_regdump_hdr);
for (index = 0; index < dev->info->num_statics; index++) {
bool valid;
struct alu_struct* alu = (struct alu_struct*)&reg[cnt];
ret = ksz8_r_sta_mac_table(dev, index, alu, &valid);
if (ret)
continue;
if (!valid)
continue;
reg[pos_length] += sizeof(struct alu_struct);
cnt += sizeof(struct alu_struct) / sizeof(u32);
}
*pos = cnt;
}
static void ksz8_get_regs_dump_fdb(struct ksz_device *dev, u32 *reg, u32 *pos)
{
BUILD_BUG_ON(!SIZE_IS_MULTIPLE_OF(struct ksz8_dyn_mac_entry, u32));
u16 i = 0;
u16 entries = 0;
u32 cnt = *pos;
reg[cnt++] = KSZ8_REGDUMP_ID_FDB;
const u32 pos_length = cnt++;
reg[pos_length] = sizeof(struct ksz_regdump_hdr);
do {
struct ksz8_dyn_mac_entry *entry = (struct ksz8_dyn_mac_entry*) &reg[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));
}
i++;
} while (i < entries);
*pos = cnt;
}
void ksz8_get_regs(struct ksz_device *dev, int port,
struct ethtool_regs *regs, void *p)
{
int i, j;
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];
if (rec.hdr.module_id == KSZ8_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) {
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;
}
}
}
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __KSZ8XXX_ETHTOOL_H
#define __KSZ8XXX_ETHTOOL_H
#include <linux/types.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);
#endif
+25 -1
View File
@@ -32,6 +32,7 @@
#include "ksz9477.h"
#include "lan937x.h"
#include "ksz_devlink.h"
#include "ksz8_ethtool.h"
#define MIB_COUNTER_NUM 0x20
@@ -222,6 +223,8 @@ static const struct ksz_dev_ops ksz8_dev_ops = {
.devlink_param_get = ksz8_devlink_param_get,
.devlink_param_set = ksz8_devlink_param_set,
.devlink_info_get= ksz8_devlink_info_get,
.get_regs = ksz8_get_regs,
.get_regs_len = ksz8_get_regs_len,
};
static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port,
@@ -3479,6 +3482,25 @@ static int ksz_devlink_param_set(struct dsa_switch *ds, u32 id,
return -EOPNOTSUPP;
}
static int ksz_get_regs_len(struct dsa_switch *ds, int port)
{
struct ksz_device *dev = ds->priv;
if (dev->info->ops->get_regs_len)
return dev->info->ops->get_regs_len(dev, port);
return -EOPNOTSUPP;
}
static void ksz_get_regs(struct dsa_switch *ds, int port,
struct ethtool_regs *regs, void *p)
{
struct ksz_device *dev = ds->priv;
if (dev->info->ops->get_regs)
dev->info->ops->get_regs(dev, port, regs, p);
}
static const struct dsa_switch_ops ksz_switch_ops = {
.get_tag_protocol = ksz_get_tag_protocol,
.connect_tag_protocol = ksz_connect_tag_protocol,
@@ -3527,6 +3549,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.devlink_param_get = ksz_devlink_param_get,
.devlink_param_set = ksz_devlink_param_set,
.devlink_info_get = ksz_devlink_info_get,
.get_regs = ksz_get_regs,
.get_regs_len = ksz_get_regs_len,
};
struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
@@ -3724,6 +3748,7 @@ int ksz_switch_register(struct ksz_device *dev)
schedule_delayed_work(&dev->mib_read, 0);
ret = ksz_devlink_param_setup(dev->ds);
return ret;
}
EXPORT_SYMBOL(ksz_switch_register);
@@ -3741,7 +3766,6 @@ void ksz_switch_remove(struct ksz_device *dev)
if (dev->reset_gpio)
gpiod_set_value_cansleep(dev->reset_gpio, 1);
}
EXPORT_SYMBOL(ksz_switch_remove);
+3
View File
@@ -377,6 +377,9 @@ struct ksz_dev_ops {
int (*devlink_info_get)(struct ksz_device *dev,
struct devlink_info_req *req,
struct netlink_ext_ack *extack);
int (*get_regs_len)(struct ksz_device *dev, int port);
void (*get_regs)(struct ksz_device *dev, int port,
struct ethtool_regs *regs, void *p);
};
struct ksz_device *ksz_switch_alloc(struct device *base, void *priv);