[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20250616111909.643441-1-c-vankar@ti.com>
Date: Mon, 16 Jun 2025 16:49:09 +0530
From: Chintan Vankar <c-vankar@...com>
To: <mkubecek@...e.cz>
CC: <s-vadapalli@...com>, <danishanwar@...com>, <c-vankar@...com>,
<netdev@...r.kernel.org>
Subject: [PATCH ethtool-next] pretty: Add support for TI K3 CPSW registers and ALE table dump
Add support to dump CPSW registers and ALE table for the CPSW instances on
K3 SoCs that are configured using the am65-cpsw-nuss.c device-driver in
Linux.
Signed-off-by: Chintan Vankar <c-vankar@...com>
---
Hello All,
This patch is based on commit '7f58b7376cc6' of origin/next branch of
Ethtool repository.
am65-cpsw-nuss.c | 508 +++++++++++++++++++++++++++++++++++++++++++++++
ethtool.c | 1 +
internal.h | 4 +
3 files changed, 513 insertions(+)
create mode 100644 am65-cpsw-nuss.c
diff --git a/am65-cpsw-nuss.c b/am65-cpsw-nuss.c
new file mode 100644
index 0000000..c8952bd
--- /dev/null
+++ b/am65-cpsw-nuss.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Code to dump registers and ALE table for K3 AM65 CPSW Devices.
+ *
+ * Copyright (C) 2025 Texas Instruments
+ * Author: Chintan Vankar <c-vankar@...com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "internal.h"
+
+#define ALE_ENTRY_BITS 74
+#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
+
+#define ALE_ENTRY_FREE 0x0
+#define ALE_ENTRY_ADDR 0x1
+#define ALE_ENTRY_VLAN 0x2
+#define ALE_ENTRY_VLAN_ADDR 0x3
+
+#define BIT(nr) (1 << (nr))
+#define BITMASK(bits) (BIT(bits) - 1)
+
+/* ALE word specifiers */
+#define NUM_ALE_WORDS 2
+#define ALE_WORD_LEN 32
+
+/* MAC address specifiers */
+#define MAC_START_BIT 40
+#define MAC_OCTET_LEN 8
+#define NUM_MAC_OCTET 6
+
+/* RTL version specifiers */
+#define RTL_VERSION_MASK 0xF800
+#define CPSW2G_RTL_VERSION 0x3800
+#define CPSW3G_RTL_VERSION 0x0
+
+/* OUI address uses format xx:xx:xx, use OUI shift as 16 bits and MASK as 0xFF to parse the same*/
+#define OUI_ADDR_SHIFT 16
+#define OUI_ADDR_MASK 0xFF
+
+/* VLAN entry specifiers */
+#define VLAN_INNER_ENTRY 0x0
+#define VLAN_OUTER_ENTRY 0x2
+#define VLAN_ETHERTYPE_ENTRY 0x4
+#define VLAN_IPV4_ENTRY 0x6
+#define VLAN_IPV6_ENTRY_MASK 0x1
+
+/* VLAN Inner/Outer table entry MASKs and SHIFTs*/
+#define NOLEARN_FLAG_SHIFT 2
+#define NOLEARN_FLAG_MASK 0x1FF
+#define INGRESS_CHECK_SHIFT 1
+#define INGRESS_CHECK_MASK 0x1
+#define VLAN_ID_SHIFT 16
+#define VLAN_ID_MASK 0xFFF
+#define NOFRAG_FLAG_SHIFT_2G 12
+#define NOFRAG_FLAG_MASK_2G 0x1
+#define NOFRAG_FLAG_SHIFT 15
+#define NOFRAG_FLAG_MASK 0x1
+#define REG_MASK_SHIFT 4
+#define REG_MASK_MASK 0x1FF
+#define PKT_EGRESS_W1_MASK 0x1
+#define PKT_EGRESS_W1_OFFSET 512
+#define PKT_EGRESS_SHIFT 24
+#define PKT_EGRESS_MASK_2G 0x3
+#define PKT_EGRESS_MASK 0x1FF
+#define UNREG_MASK_SHIFT_2G 20
+#define UNREG_MASK_MASK_2G 0x7
+#define UNREG_MASK_SHIFT 12
+#define UNREG_MASK_MASK 0x1FF
+#define NXT_HDR_CTRL_SHIFT_2G 19
+#define NXT_HDR_CTRL_MASK_2G 0x1
+#define NXT_HDR_CTRL_SHIFT 23
+#define NXT_HDR_CTRL_MASK 0x1
+#define VLAN_MEMBER_LIST_MASK_2G 0x3
+#define VLAN_MEMBER_LIST_MASK 0x1FF
+
+/* VLAN IPv4 entry MASKs and SHIFTs*/
+#define IPV4_ADDR_OCT1_SHIFT 24
+#define IPV4_ADDR_OCT2_SHIFT 16
+#define IPV4_ADDR_OCT3_SHIFT 8
+#define IPV4_ADDR_MASK 0xFF
+
+/* VLAN IPv6 entry MASKs and SHIFTs*/
+#define IPV6_HIGH_ENTRY_FLAG 0x40
+#define IPV6_IGNMCBITS_MASK 0xFF
+#define IPV6_HADDR_W1_SHIFT 12
+#define IPV6_HADDR_W1_MASK_1 0xFFFF
+#define IPV6_HADDR_W1_MASK_2 0xFFF
+#define IPV6_HADDR_W0_SHIFT_1 28
+#define IPV6_HADDR_W0_MASK_1 0xF
+#define IPV6_HADDR_W0_SHIFT_2 12
+#define IPV6_HADDR_W0_MASK_2 0xFFFF
+#define IPV6_LADDR_W2_SHIFT 4
+#define IPV6_LADDR_W2_MAKS 0xF
+#define IPV6_LADDR_W1_SHIFT 16
+#define IPV6_LADDR_W1_MASK_1 0xFFF
+#define IPV6_LADDR_W1_MASK 0xFFFF
+#define IPV6_LADDR_W0_SHIFT 16
+#define IPV6_LADDR_W0_MASK 0xFFFF
+
+/**
+ * Since there are different instances of CPSW (namely cpsw2g, cpsw3g, cpsw5g and cpsw9g)
+ * some register offsets differ to get some parameters for ALE table, parse rtl_version
+ * from ALE_MOD_VER register to determine which instance is being used.
+ */
+u32 rtl_version;
+
+static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
+{
+ int idx;
+
+ idx = start / ALE_WORD_LEN;
+ start -= idx * ALE_WORD_LEN;
+
+ /**
+ * ALE words are stored in order word2, word1 and word0, flip the word to parse in numeric
+ * order
+ */
+ idx = NUM_ALE_WORDS - idx; /* flip */
+ return (ale_entry[idx] >> start) & BITMASK(bits);
+}
+
+#define DEFINE_ALE_FIELD(name, start, bits) \
+static inline int cpsw_ale_get_##name(u32 *ale_entry) \
+{ \
+ return cpsw_ale_get_field(ale_entry, start, bits); \
+}
+
+DEFINE_ALE_FIELD(entry_type, 60, 2)
+DEFINE_ALE_FIELD(vlan_id, 48, 12)
+DEFINE_ALE_FIELD(mcast_state, 62, 2)
+DEFINE_ALE_FIELD(port_mask, 66, 9)
+DEFINE_ALE_FIELD(super, 65, 1)
+DEFINE_ALE_FIELD(agable, 62, 1)
+DEFINE_ALE_FIELD(touched, 63, 1)
+DEFINE_ALE_FIELD(ucast_type, 62, 2)
+DEFINE_ALE_FIELD(port_num, 66, 4)
+DEFINE_ALE_FIELD(port_num_2g, 66, 1)
+DEFINE_ALE_FIELD(port_num_3g, 66, 2)
+DEFINE_ALE_FIELD(blocked, 65, 1)
+DEFINE_ALE_FIELD(secure, 64, 1)
+DEFINE_ALE_FIELD(oui_entry, 62, 2)
+DEFINE_ALE_FIELD(oui_addr, 4, 24)
+DEFINE_ALE_FIELD(mcast, 40, 1)
+DEFINE_ALE_FIELD(vlan_entry_type, 62, 3)
+DEFINE_ALE_FIELD(ethertype, 0, 16)
+DEFINE_ALE_FIELD(ipv4_addr, 0, 32)
+DEFINE_ALE_FIELD(ingress_bits, 65, 5)
+DEFINE_ALE_FIELD(ipv6_addr_low, 0, 60)
+DEFINE_ALE_FIELD(ipv6_addr_mid, 63, 8)
+DEFINE_ALE_FIELD(ipv6_addr_high, 0, 60)
+DEFINE_ALE_FIELD(entry_word0, 0, 32)
+DEFINE_ALE_FIELD(entry_word1, 32, 32)
+DEFINE_ALE_FIELD(entry_word2, 64, 12)
+
+static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < NUM_MAC_OCTET; i++)
+ addr[i] = cpsw_ale_get_field(ale_entry, MAC_START_BIT - MAC_OCTET_LEN * i,
+ MAC_OCTET_LEN);
+}
+
+struct k3_cpsw_regdump_hdr {
+ u32 module_id;
+ u32 len;
+};
+
+enum {
+ K3_CPSW_REGDUMP_MOD_NUSS = 1,
+ K3_CPSW_REGDUMP_MOD_RGMII_STATUS = 2,
+ K3_CPSW_REGDUMP_MOD_MDIO = 3,
+ K3_CPSW_REGDUMP_MOD_CPSW = 4,
+ K3_CPSW_REGDUMP_MOD_CPSW_P0 = 5,
+ K3_CPSW_REGDUMP_MOD_CPSW_PN = 6,
+ K3_CPSW_REGDUMP_MOD_CPSW_CPTS = 7,
+ K3_CPSW_REGDUMP_MOD_CPSW_ALE = 8,
+ K3_CPSW_REGDUMP_MOD_CPSW_ALE_TBL = 9,
+ K3_CPSW_REGDUMP_MOD_LAST,
+};
+
+static const char *mod_names[K3_CPSW_REGDUMP_MOD_LAST] = {
+ [K3_CPSW_REGDUMP_MOD_NUSS] = "cpsw-nuss",
+ [K3_CPSW_REGDUMP_MOD_RGMII_STATUS] = "cpsw-nuss-rgmii-status",
+ [K3_CPSW_REGDUMP_MOD_MDIO] = "cpsw-nuss-mdio",
+ [K3_CPSW_REGDUMP_MOD_CPSW] = "cpsw-nu",
+ [K3_CPSW_REGDUMP_MOD_CPSW_P0] = "cpsw-nu-p0",
+ [K3_CPSW_REGDUMP_MOD_CPSW_PN] = "cpsw-nu-pn",
+ [K3_CPSW_REGDUMP_MOD_CPSW_CPTS] = "cpsw-nu-cpts",
+ [K3_CPSW_REGDUMP_MOD_CPSW_ALE] = "cpsw-nu-ale",
+ [K3_CPSW_REGDUMP_MOD_CPSW_ALE_TBL] = "cpsw-nu-ale-tbl",
+};
+
+static void cpsw_ale_dump_oui_entry(int index, u32 *ale_entry)
+{
+ u32 oui_addr;
+
+ oui_addr = cpsw_ale_get_oui_addr(ale_entry);
+
+ fprintf(stdout, "%d: Type: OUI Unicast\n, \tOUI = %02x:%02x:%02x\n",
+ index, (oui_addr >> OUI_ADDR_SHIFT) & OUI_ADDR_MASK,
+ (oui_addr >> OUI_ADDR_SHIFT) & OUI_ADDR_MASK, oui_addr & OUI_ADDR_MASK);
+}
+
+static void cpsw_ale_dump_addr(int index, u32 *ale_entry)
+{
+ u8 addr[6];
+
+ cpsw_ale_get_addr(ale_entry, addr);
+
+ if (cpsw_ale_get_mcast(ale_entry)) {
+ static const char * const str_mcast_state[] = {"Forwarding",
+ "Blocking/Forwarding/Learning",
+ "Learning/Forwarding",
+ "Forwarding"};
+ u16 port_mask = cpsw_ale_get_port_mask(ale_entry);
+ u8 state = cpsw_ale_get_mcast_state(ale_entry);
+ u8 super = cpsw_ale_get_super(ale_entry);
+
+ fprintf(stdout, "%d: Type: Multicast\n \tAddress = %02x:%02x:%02x:%02x:%02x:%02x, Multicast_State = %s, %sSuper, port_mask = 0x%x\n",
+ index, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+ str_mcast_state[state], super ? "" : "No ", port_mask);
+ } else {
+ static const char * const s_ucast_type[] = {"Persistent", "Untouched", "OUI",
+ "Touched"};
+ u8 ucast_type = cpsw_ale_get_ucast_type(ale_entry);
+ u8 port_num = cpsw_ale_get_port_num(ale_entry);
+ u8 blocked = cpsw_ale_get_blocked(ale_entry);
+ u8 touched = cpsw_ale_get_touched(ale_entry);
+ u8 secure = cpsw_ale_get_secure(ale_entry);
+ u8 agable = cpsw_ale_get_agable(ale_entry);
+
+ fprintf(stdout, "%d: Type: Unicast\n \tUpdated Address = %02x:%02x:%02x:%02x:%02x:%02x, Unicast Type = %s, Port_num = 0x%x, Secure: %d, Blocked: %d, Touch = %d, Agable = %d\n",
+ index, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+ s_ucast_type[ucast_type], port_num, secure, blocked, touched, agable);
+ }
+}
+
+static void cpsw_ale_dump_inner_vlan_entry(int index, u32 *ale_entry)
+{
+ u32 vlan_entry_word0 = cpsw_ale_get_entry_word0(ale_entry);
+ u32 vlan_entry_word1 = cpsw_ale_get_entry_word1(ale_entry);
+ u16 vlan_entry_word2 = cpsw_ale_get_entry_word2(ale_entry);
+
+ fprintf(stdout, "%d: Type: Inner VLAN\n \tNolearn Mask = 0x%x, Ingress Check = %d\n",
+ index, (vlan_entry_word2 >> NOLEARN_FLAG_SHIFT) & NOLEARN_FLAG_MASK,
+ (vlan_entry_word2 >> INGRESS_CHECK_SHIFT) & INGRESS_CHECK_MASK);
+
+ if (rtl_version == CPSW2G_RTL_VERSION) {
+ fprintf(stdout, "\tVLAN ID = %d, No Frag = %d, Registered Mask = 0x%x\n",
+ (vlan_entry_word1 >> VLAN_ID_SHIFT) & VLAN_ID_MASK,
+ (vlan_entry_word1 >> NOFRAG_FLAG_SHIFT_2G) & NOFRAG_FLAG_MASK_2G,
+ (vlan_entry_word1 >> REG_MASK_SHIFT) & REG_MASK_MASK);
+
+ fprintf(stdout, "\tForce Untagged Packet Egress = 0x%x, Unregistered Mask = 0x%x, Limit Next Header Control = %d, Members = 0x%x\n",
+ (vlan_entry_word0 >> PKT_EGRESS_SHIFT) & PKT_EGRESS_MASK_2G,
+ (vlan_entry_word0 >> UNREG_MASK_SHIFT_2G) & UNREG_MASK_MASK_2G,
+ (vlan_entry_word0 >> NXT_HDR_CTRL_SHIFT_2G) & NXT_HDR_CTRL_MASK_2G,
+ (vlan_entry_word0 & VLAN_MEMBER_LIST_MASK_2G));
+ } else {
+ fprintf(stdout, "\tVLAN ID = %d, Registered Mask = 0x%x, No Frag = %d\n",
+ (vlan_entry_word1 >> VLAN_ID_SHIFT) & VLAN_ID_MASK,
+ (vlan_entry_word1 >> REG_MASK_SHIFT) & REG_MASK_MASK,
+ (vlan_entry_word1 >> NOFRAG_FLAG_SHIFT) & NOFRAG_FLAG_MASK);
+
+ fprintf(stdout, "\tForce Untagged Packet Egress = 0x%x, Limit Next Header Control = %d, Unregistered Mask = 0x%x, Members = 0x%x\n",
+ (vlan_entry_word1 & PKT_EGRESS_W1_MASK) * PKT_EGRESS_W1_OFFSET +
+ ((vlan_entry_word0 >> PKT_EGRESS_SHIFT) & PKT_EGRESS_MASK),
+ (vlan_entry_word0 >> NXT_HDR_CTRL_SHIFT) & NXT_HDR_CTRL_MASK,
+ (vlan_entry_word0 >> UNREG_MASK_SHIFT) & UNREG_MASK_MASK,
+ (vlan_entry_word0 & VLAN_MEMBER_LIST_MASK));
+ }
+}
+
+static void cpsw_ale_dump_outer_vlan_entry(int index, u32 *ale_entry)
+{
+ u32 vlan_entry_word0 = cpsw_ale_get_entry_word0(ale_entry);
+ u32 vlan_entry_word1 = cpsw_ale_get_entry_word1(ale_entry);
+ u16 vlan_entry_word2 = cpsw_ale_get_entry_word2(ale_entry);
+
+ fprintf(stdout, "%d: Type: Outer VLAN\n \tNolearn Mask = 0x%x, Ingress Check = %d\n",
+ index, (vlan_entry_word2 >> NOLEARN_FLAG_SHIFT) & NOLEARN_FLAG_MASK,
+ (vlan_entry_word2 >> INGRESS_CHECK_SHIFT) & INGRESS_CHECK_MASK);
+
+ if (rtl_version == CPSW2G_RTL_VERSION) {
+ fprintf(stdout, "\tVLAN ID = %d, No Frag = %d, Registered Mask = 0x%x\n",
+ (vlan_entry_word1 >> VLAN_ID_SHIFT) & VLAN_ID_MASK,
+ (vlan_entry_word1 >> NOFRAG_FLAG_SHIFT_2G) & NOFRAG_FLAG_MASK_2G,
+ (vlan_entry_word1 >> REG_MASK_SHIFT) & REG_MASK_MASK);
+
+ fprintf(stdout, "\tForce Untagged Packet Egress = 0x%x, Unregistered Mask = 0x%x, Limit Next Header Control = %d, Members = 0x%x\n",
+ (vlan_entry_word0 >> PKT_EGRESS_SHIFT) & PKT_EGRESS_MASK_2G,
+ (vlan_entry_word0 >> UNREG_MASK_SHIFT_2G) & UNREG_MASK_MASK_2G,
+ (vlan_entry_word0 >> NXT_HDR_CTRL_SHIFT_2G) & NXT_HDR_CTRL_MASK_2G,
+ (vlan_entry_word0 & VLAN_MEMBER_LIST_MASK_2G));
+ } else {
+ fprintf(stdout, "\tVLAN ID = %d, No Frag = %d, Registered Mask = 0x%x\n",
+ (vlan_entry_word1 >> VLAN_ID_SHIFT) & VLAN_ID_MASK,
+ (vlan_entry_word1 >> NOFRAG_FLAG_SHIFT) & NOFRAG_FLAG_MASK,
+ (vlan_entry_word1 >> REG_MASK_SHIFT) & REG_MASK_MASK);
+
+ fprintf(stdout, "\tForce Untagged Packet Egress = 0x%x, Limit Next Header Control = %d, Unregistered Mask = 0x%x Members = 0x%x\n",
+ (vlan_entry_word1 & 0x1) * 512 + ((vlan_entry_word0 >> PKT_EGRESS_SHIFT) &
+ PKT_EGRESS_MASK),
+ (vlan_entry_word0 >> NXT_HDR_CTRL_SHIFT) & NXT_HDR_CTRL_MASK,
+ (vlan_entry_word0 >> UNREG_MASK_SHIFT) & UNREG_MASK_MASK,
+ (vlan_entry_word0 & VLAN_MEMBER_LIST_MASK));
+ }
+}
+
+static void cpsw_ale_dump_ethertype_entry(int index, u32 *ale_entry)
+{
+ u16 ethertype = cpsw_ale_get_ethertype(ale_entry);
+
+ fprintf(stdout, "%d: Type: VLAN Ethertype\n \tEthertype = 0x%x\n", index, ethertype);
+}
+
+static void cpsw_ale_dump_ipv4_entry(int index, u32 *ale_entry)
+{
+ u8 ingress_bits = cpsw_ale_get_ingress_bits(ale_entry);
+ u32 ipv4_addr = cpsw_ale_get_ipv4_addr(ale_entry);
+
+ fprintf(stdout, "%d: Type: VLAN IPv4\n \tIngress Bits: 0x%x IPv4 Address = %u.%u.%u.%u\n",
+ index, ingress_bits, ipv4_addr >> IPV4_ADDR_OCT1_SHIFT & IPV4_ADDR_MASK,
+ ipv4_addr >> IPV4_ADDR_OCT2_SHIFT & IPV4_ADDR_MASK,
+ ipv4_addr >> IPV4_ADDR_OCT3_SHIFT & IPV4_ADDR_MASK, ipv4_addr & IPV4_ADDR_MASK);
+}
+
+static void cpsw_ale_dump_ipv6_entry(int index, u32 *ale_entry)
+{
+ u32 vlan_entry_word0 = cpsw_ale_get_entry_word0(ale_entry);
+ u32 vlan_entry_word1 = cpsw_ale_get_entry_word1(ale_entry);
+ u16 vlan_entry_word2 = cpsw_ale_get_entry_word2(ale_entry);
+
+ if (index & 0x40) {
+ fprintf(stdout, "%d: Type: VLAN IPv6 Higher Entry (Lower Bit entry at %04u)\n \tIgnored Multicast bits: 0x%x, IPv6 Address (Bits [127:68]) = %04x:%03x%01x:%04x:%03x\n",
+ index, (index & (~IPV6_HIGH_ENTRY_FLAG)),
+ vlan_entry_word2 & IPV6_IGNMCBITS_MASK,
+ (vlan_entry_word1 >> IPV6_HADDR_W1_SHIFT) & IPV6_HADDR_W1_MASK_1,
+ vlan_entry_word1 & IPV6_HADDR_W1_MASK_2,
+ (vlan_entry_word0 >> IPV6_HADDR_W0_SHIFT_1) & IPV6_HADDR_W0_MASK_1,
+ (vlan_entry_word0 >> IPV6_HADDR_W0_SHIFT_2) & IPV6_HADDR_W0_MASK_2,
+ vlan_entry_word0 & IPV6_HADDR_W0_MASK_2);
+ } else {
+ fprintf(stdout, "%d: Type: VLAN IPv6 Lower Entry (Higher Bit entry at %04u)\n \tIPv6 Address (Bits [127:68]) = %01x:%01x%03x:%04x:%04x:%04x\n",
+ index, (index | IPV6_HIGH_ENTRY_FLAG),
+ (vlan_entry_word2 >> IPV6_LADDR_W2_SHIFT) & IPV6_LADDR_W2_MAKS,
+ vlan_entry_word2 & IPV6_LADDR_W2_MAKS,
+ (vlan_entry_word1 >> IPV6_LADDR_W1_SHIFT) & IPV6_LADDR_W1_MASK_1,
+ vlan_entry_word1 & IPV6_LADDR_W1_MASK,
+ (vlan_entry_word0 >> IPV6_LADDR_W0_SHIFT) & IPV6_LADDR_W0_MASK,
+ vlan_entry_word0 & IPV6_LADDR_W0_MASK);
+ }
+}
+
+static void cpsw_ale_dump_vlan_addr(int index, u32 *ale_entry)
+{
+ u8 addr[6];
+ int vlan = cpsw_ale_get_vlan_id(ale_entry);
+
+ cpsw_ale_get_addr(ale_entry, addr);
+ if (cpsw_ale_get_mcast(ale_entry)) {
+ static const char * const str_mcast_state[] = {"Forwarding",
+ "Blocking/Forwarding/Learning",
+ "Learning/Forwarding",
+ "Forwarding"};
+ u16 port_mask = cpsw_ale_get_port_mask(ale_entry);
+ u8 state = cpsw_ale_get_mcast_state(ale_entry);
+ u8 super = cpsw_ale_get_super(ale_entry);
+
+ fprintf(stdout, "%d: Type: Multicast\n \tVID = %d, Address = %02x:%02x:%02x:%02x:%02x:%02x, Multicast_state = %s, %s Super, port_mask = 0x%x\n",
+ index, vlan, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+ str_mcast_state[state], super ? "" : "No ", port_mask);
+ } else {
+ static const char * const s_ucast_type[] = {"Persistent", "Untouched", "OUI",
+ "Touched"};
+ u8 ucast_type = cpsw_ale_get_ucast_type(ale_entry);
+ u8 blocked = cpsw_ale_get_blocked(ale_entry);
+ u8 touched = cpsw_ale_get_touched(ale_entry);
+ u8 secure = cpsw_ale_get_secure(ale_entry);
+ u8 agable = cpsw_ale_get_agable(ale_entry);
+
+ int port_num;
+
+ if (rtl_version == CPSW2G_RTL_VERSION)
+ port_num = cpsw_ale_get_port_num_2g(ale_entry);
+ else if (rtl_version == CPSW3G_RTL_VERSION)
+ port_num = cpsw_ale_get_port_num_3g(ale_entry);
+ else
+ port_num = cpsw_ale_get_port_num(ale_entry);
+
+ fprintf(stdout, "%d: Type: Unicast\n \tVID = %d, Address = %02x:%02x:%02x:%02x:%02x:%02x, Unicast_type = %s, port_num = 0x%x, Secure = %d, Blocked = %d, Touch = %d, Agable = %d\n",
+ index, vlan, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
+ s_ucast_type[ucast_type], port_num, secure, blocked, touched, agable);
+ }
+}
+
+void cpsw_dump_ale(struct k3_cpsw_regdump_hdr *ale_hdr, u32 *ale_pos)
+{
+ int i, ale_entries;
+
+ if (!ale_hdr)
+ return;
+
+ ale_entries = (ale_hdr->len - sizeof(struct k3_cpsw_regdump_hdr)) /
+ ALE_ENTRY_WORDS / sizeof(u32);
+
+ printf("Number of ALE entries: %d\n", ale_entries);
+ ale_pos += 2;
+ for (i = 0; i < ale_entries; i++) {
+ int type;
+
+ type = cpsw_ale_get_entry_type(ale_pos);
+
+ switch (type) {
+ case ALE_ENTRY_FREE:
+ break;
+
+ case ALE_ENTRY_ADDR:
+ u32 oui_entry = cpsw_ale_get_oui_addr(ale_pos);
+
+ if (oui_entry == 0x2)
+ cpsw_ale_dump_oui_entry(i, ale_pos);
+ else
+ cpsw_ale_dump_addr(i, ale_pos);
+ break;
+
+ case ALE_ENTRY_VLAN:
+ u32 vlan_entry_type = cpsw_ale_get_vlan_entry_type(ale_pos);
+
+ if (vlan_entry_type == VLAN_INNER_ENTRY)
+ cpsw_ale_dump_inner_vlan_entry(i, ale_pos);
+ else if (vlan_entry_type == VLAN_OUTER_ENTRY)
+ cpsw_ale_dump_outer_vlan_entry(i, ale_pos);
+ else if (vlan_entry_type == VLAN_ETHERTYPE_ENTRY)
+ cpsw_ale_dump_ethertype_entry(i, ale_pos);
+ else if (vlan_entry_type == VLAN_IPV4_ENTRY)
+ cpsw_ale_dump_ipv4_entry(i, ale_pos);
+ else if (vlan_entry_type & VLAN_IPV6_ENTRY_MASK)
+ cpsw_ale_dump_ipv6_entry(i, ale_pos);
+ break;
+
+ case ALE_ENTRY_VLAN_ADDR:
+ cpsw_ale_dump_vlan_addr(i, ale_pos);
+ break;
+
+ default:
+ break;
+ }
+
+ ale_pos += ALE_ENTRY_WORDS;
+ }
+}
+
+int am65_cpsw_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ struct k3_cpsw_regdump_hdr *dump_hdr, *ale_hdr = NULL;
+ u32 *reg = (u32 *)regs->data, *ale_pos;
+ u32 mod_id;
+ int i, regdump_len = info->regdump_len;
+
+ fprintf(stdout, "K3 CPSW dump version: %d, len: %d\n",
+ regs->version, info->regdump_len);
+ fprintf(stdout, "(Missing registers in memory space can be considered as zero valued)\n");
+
+ /* Line break before register dump */
+ fprintf(stdout, "--------------------------------------------------------------------\n");
+ i = 0;
+ do {
+ u32 *tmp, j;
+ u32 num_items;
+
+ dump_hdr = (struct k3_cpsw_regdump_hdr *)reg;
+ mod_id = dump_hdr->module_id;
+
+ num_items = dump_hdr->len / sizeof(u32);
+
+ if (mod_id == K3_CPSW_REGDUMP_MOD_CPSW_ALE)
+ rtl_version = reg[3] & RTL_VERSION_MASK;
+
+ if (mod_id == K3_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) {
+ ale_hdr = dump_hdr;
+ ale_pos = reg;
+ break;
+ }
+
+ fprintf(stdout, "%s regdump: number of Registers:(%d)\n",
+ mod_names[mod_id], num_items - 2);
+ tmp = reg;
+ for (j = 2; j < num_items; j += 2) {
+ if (tmp[j + 1] != 0x0)
+ fprintf(stdout, "%08x:reg(%08X)\n", tmp[j], tmp[j + 1]);
+ }
+
+ reg += num_items;
+ i += dump_hdr->len;
+ } while (i < regdump_len);
+
+ /* Adding a boundary in between Register dump and ALE table */
+ fprintf(stdout, "--------------------------\n");
+
+ cpsw_dump_ale(ale_hdr, ale_pos);
+
+ return 0;
+};
diff --git a/ethtool.c b/ethtool.c
index b8c20e5..a11c863 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -1208,6 +1208,7 @@ static const struct {
{ "hns3", hns3_dump_regs },
{ "fbnic", fbnic_dump_regs },
{ "hibmcge", hibmcge_dump_regs },
+ { "am65-cpsw-nuss", am65_cpsw_dump_regs }
};
#endif
diff --git a/internal.h b/internal.h
index 03777f0..48ebb4a 100644
--- a/internal.h
+++ b/internal.h
@@ -418,4 +418,8 @@ int lan743x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
/* Meta Ethernet Controller */
int fbnic_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* TI K3 CPSW Ethernet Switch */
+int am65_cpsw_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
#endif /* ETHTOOL_INTERNAL_H__ */
--
2.34.1
Powered by blists - more mailing lists