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 <jan.sondhauss@wago.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#define __KSZ8XXX_ETHTOOL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifndef __KSZ9477_ETHTOOL_H
|
||||
#define __KSZ9477_ETHTOOL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#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
|
||||
@@ -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 = {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifndef __KSZ_ETHTOOL_H
|
||||
#define __KSZ_ETHTOOL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user