diff options
Diffstat (limited to 'cpsw.c')
-rw-r--r-- | cpsw.c | 193 |
1 files changed, 193 insertions, 0 deletions
@@ -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; +} |