summaryrefslogtreecommitdiffstats
path: root/cpsw.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpsw.c')
-rw-r--r--cpsw.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/cpsw.c b/cpsw.c
new file mode 100644
index 0000000..68dcfac
--- /dev/null
+++ b/cpsw.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Code to dump registers for TI CPSW switch devices.
+ *
+ * Copyright (c) 2022 Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@linutronix.de>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "internal.h"
+
+#define ALE_ENTRY_BITS 68
+#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
+#define ALE_ENTRY_BYTES (ALE_ENTRY_WORDS * 4)
+
+struct address_table_entry
+{
+ u8 port;
+ u8 reserved1;
+ u8 reserved2;
+ u8 reserved3;
+ u8 addr2;
+ u8 addr1;
+ u16 vlan;
+ u8 addr6;
+ u8 addr5;
+ u8 addr4;
+ u8 addr3;
+} __attribute__((packed));
+
+struct vlan_table_entry
+{
+ u8 reserved1;
+ u8 reserved2;
+ u8 reserved3;
+ u8 reserved4;
+ u8 reserved5;
+ u8 reserved6;
+ u16 vlan;
+ u8 member;
+ u8 mc_unreg;
+ u8 mc_reg;
+ u8 untag;
+} __attribute__((packed));
+
+union ale_entry {
+ struct address_table_entry addr;
+ struct vlan_table_entry vlan;
+ u32 val[3];
+ u8 byte[12];
+};
+
+enum entry_type {
+ FREE_ENTRY = 0,
+ ADDR_ENTRY,
+ VLAN_ENTRY,
+ VLAN_ADDR_ENTRY,
+ LAST_ENTRY
+};
+
+static char *fwd_state_name[] = {
+ "Forwarding",
+ "Blocking/Forwarding/Learning",
+ "Forwarding/Learning",
+ "Forwarding",
+};
+
+static char *type_name[] = {
+ "free entry",
+ "address entry",
+ "VLAN entry",
+ "VLAN address entry",
+ "invalid"
+};
+
+enum entry_type decode_type(union ale_entry *entry)
+{
+ /* Entry Type (61:60) */
+ return (entry->byte[7] >> 4) & 0x3;
+}
+
+static void print_addr(u8 *data)
+{
+ printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ data[5], data[4], data[11], data[10], data[9], data[8]);
+}
+
+static void decode_multi_addr(union ale_entry *entry, int vlan)
+{
+ printf(" MULTI: ");
+ print_addr(entry->byte);
+ printf(" %s", fwd_state_name[entry->addr.vlan >> 14]);
+ printf("%s", (entry->addr.port & 0x02) ? " Super" : "");
+ printf(" Ports: 0x%x", (entry->addr.port >> 2) & 0x3);
+ if (vlan)
+ printf(" VLAN: %04d", entry->addr.vlan & 0x0fff);
+ printf("\n");
+}
+
+static void decode_uni_addr(union ale_entry *entry, int vlan)
+{
+ printf(" UNI : ");
+ print_addr(entry->byte);
+ printf("%s", (entry->addr.port & 0x01) ? " Secure" : "");
+ printf("%s", (entry->addr.port & 0x02) ? " Block" : "");
+ printf("%s", (entry->addr.port & 0x20) ? " DLR" : "");
+ printf(" Ports: 0x%x", (entry->addr.port >> 2) & 0x3);
+ if (vlan)
+ printf(" VLAN: %04d", entry->addr.vlan & 0x0fff);
+ printf("\n");
+}
+
+static void decode_oui_addr(union ale_entry *entry)
+{
+ printf(" OUI : ");
+ print_addr(entry->byte);
+ printf("\n");
+}
+
+static void decode_vlan(union ale_entry *entry)
+{
+ printf(" VLAN ");
+ printf("%04d: ", entry->vlan.vlan & 0x0fff);
+ printf("member: 0x%x ", entry->vlan.member & 0x7);
+ printf("mc flood unreg: 0x%x ", entry->vlan.mc_unreg & 0x7);
+ printf("mc flood reg: 0x%x ", entry->vlan.mc_reg & 0x7);
+ printf("untag: 0x%x\n", entry->vlan.untag & 0x7);
+}
+
+static enum entry_type decode_ale_entry(unsigned int idx, const u8 *data,
+ bool last_was_free)
+{
+ union ale_entry *entry = (union ale_entry *) data;
+ enum entry_type type;
+
+ entry = entry + idx;
+ type = decode_type(entry);
+
+ if (!last_was_free || type != FREE_ENTRY)
+ printf("%04d: %s\n", idx, type_name[type]);
+
+ switch (type)
+ {
+ case FREE_ENTRY:
+ goto out;
+ break;
+
+ case ADDR_ENTRY:
+ /* Multicast: OUI 01:00:5e:xx:xx:xx */
+ if (entry->addr.addr1 == 0x01)
+ decode_multi_addr(entry, 0);
+ else
+ if ((entry->addr.vlan >> 14) == 0x2)
+ decode_oui_addr(entry);
+ else
+ decode_uni_addr(entry, 0);
+ break;
+
+ case VLAN_ENTRY:
+ decode_vlan(entry);
+ break;
+
+ case VLAN_ADDR_ENTRY:
+ /* Check for Individual/Group bit */
+ if (entry->addr.addr1 & 0x01)
+ decode_multi_addr(entry, 1);
+ else
+ decode_uni_addr(entry, 1);
+ break;
+
+ default:
+ printf("internal failure.\n");
+ }
+
+out:
+ return type;
+}
+
+int cpsw_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ unsigned int entries = regs->len/ALE_ENTRY_BYTES;
+ enum entry_type type = LAST_ENTRY;
+ unsigned int i;
+
+ printf("ALE Entries (%d):\n", entries);
+
+ for (i = 0; i < entries; i++)
+ type = decode_ale_entry(i, regs->data, (type == FREE_ENTRY));
+
+ return 0;
+}