From patchwork Fri Jul 11 02:29:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aniket Limaye X-Patchwork-Id: 66592 X-Patchwork-Delegate: reatmon@ti.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8EB27C83F17 for ; Fri, 11 Jul 2025 02:29:54 +0000 (UTC) Received: from lelvem-ot01.ext.ti.com (lelvem-ot01.ext.ti.com [198.47.23.234]) by mx.groups.io with SMTP id smtpd.web10.4892.1752200988146220285 for ; Thu, 10 Jul 2025 19:29:48 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@ti.com header.s=ti-com-17Q1 header.b=UGdmFZIo; spf=pass (domain: ti.com, ip: 198.47.23.234, mailfrom: a-limaye@ti.com) Received: from fllvem-sh04.itg.ti.com ([10.64.41.54]) by lelvem-ot01.ext.ti.com (8.15.2/8.15.2) with ESMTP id 56B2Tl6h1314794 for ; Thu, 10 Jul 2025 21:29:47 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1752200987; bh=NdNo23wkthwE5Dgjwjv9ZHoxeuGAbSFkSEfiHZziVkQ=; h=From:To:CC:Subject:Date; b=UGdmFZIoXTUBct8CwSZo5PXP9HubAFrgJGwbTvD/F+x8rrvKNcrmznJuPc6zSXVLH NM5CBMXX3ofQW0EkB7Ie2jw9qDPpF4g4a+C9dG7OIi2WWS6VvjE6O/MeK/vw6WoTlG lL52Pm+SwZLWiQmTj9ZP6h8salz91VgY5btmaQPE= Received: from DFLE115.ent.ti.com (dfle115.ent.ti.com [10.64.6.36]) by fllvem-sh04.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 56B2TlrT4075338 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL) for ; Thu, 10 Jul 2025 21:29:47 -0500 Received: from DFLE103.ent.ti.com (10.64.6.24) by DFLE115.ent.ti.com (10.64.6.36) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Thu, 10 Jul 2025 21:29:46 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DFLE103.ent.ti.com (10.64.6.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55 via Frontend Transport; Thu, 10 Jul 2025 21:29:47 -0500 Received: from aniket-z2-workstation.dhcp.ti.com (aniket-z2-workstation.dhcp.ti.com [10.24.69.69]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 56B2TiVJ2681829; Thu, 10 Jul 2025 21:29:45 -0500 From: Aniket Limaye To: CC: , , Subject: [meta-arago][scarthgap][PATCH v2] meta-arago-distro: ethtool: Add patch to dump CPSW registers for K3 SoCs Date: Fri, 11 Jul 2025 07:59:44 +0530 Message-ID: <20250711022944.2633417-1-a-limaye@ti.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Fri, 11 Jul 2025 02:29:54 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/meta-arago/message/16282 Add patch to support for TI K3 CPSW register and ALE table dump. Signed-off-by: Aniket Limaye --- v2: Fixed encodings in patchfile --- .../ethtool/ethtool-arago.inc | 8 + ...k3-cpsw-registers-and-ale-table-dump.patch | 574 ++++++++++++++++++ .../ethtool/ethtool_%.bbappend | 4 + 3 files changed, 586 insertions(+) create mode 100644 meta-arago-distro/recipes-extended/ethtool/ethtool-arago.inc create mode 100644 meta-arago-distro/recipes-extended/ethtool/ethtool/pretty-add-support-for-ti-k3-cpsw-registers-and-ale-table-dump.patch create mode 100644 meta-arago-distro/recipes-extended/ethtool/ethtool_%.bbappend diff --git a/meta-arago-distro/recipes-extended/ethtool/ethtool-arago.inc b/meta-arago-distro/recipes-extended/ethtool/ethtool-arago.inc new file mode 100644 index 00000000..5fd05a85 --- /dev/null +++ b/meta-arago-distro/recipes-extended/ethtool/ethtool-arago.inc @@ -0,0 +1,8 @@ +PR:append = ".arago0" + +FILESEXTRAPATHS:prepend := "${THISDIR}/ethtool:" + +SRC_URI:append = " \ + file://pretty-add-support-for-ti-k3-cpsw-registers-and-ale-table-dump.patch \ + " + diff --git a/meta-arago-distro/recipes-extended/ethtool/ethtool/pretty-add-support-for-ti-k3-cpsw-registers-and-ale-table-dump.patch b/meta-arago-distro/recipes-extended/ethtool/ethtool/pretty-add-support-for-ti-k3-cpsw-registers-and-ale-table-dump.patch new file mode 100644 index 00000000..a14089b4 --- /dev/null +++ b/meta-arago-distro/recipes-extended/ethtool/ethtool/pretty-add-support-for-ti-k3-cpsw-registers-and-ale-table-dump.patch @@ -0,0 +1,574 @@ +From: Chintan Vankar +Subject: [PATCH] pretty: Add support for TI K3 CPSW registers and ALE table dump +Date: Thu, 3 Jul 2025 12:02:46 +0530 + +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. + +Upstream-Status: Submitted [https://lore.kernel.org/all/20250705134807.3514891-1-c-vankar@ti.com/] +Signed-off-by: Chintan Vankar +--- + Makefile.am | 2 +- + am65-cpsw-nuss.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++ + ethtool.c | 1 + + internal.h | 3 + + 4 files changed, 515 insertions(+), 1 deletion(-) + create mode 100644 am65-cpsw-nuss.c + +diff --git a/Makefile.am b/Makefile.am +index b9e06ad..fe6afcb 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -23,7 +23,7 @@ ethtool_SOURCES += \ + smsc911x.c at76c50x-usb.c sfc.c stmmac.c \ + sff-common.c sff-common.h sfpid.c sfpdiag.c \ + ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c \ +- igc.c cmis.c cmis.h bnxt.c cpsw.c lan743x.c hns3.c ++ igc.c cmis.c cmis.h bnxt.c cpsw.c lan743x.c hns3.c am65-cpsw-nuss.c + endif + + if ENABLE_BASH_COMPLETION +diff --git a/am65-cpsw-nuss.c b/am65-cpsw-nuss.c +new file mode 100644 +index 0000000..de8e3e9 +--- /dev/null ++++ b/am65-cpsw-nuss.c +@@ -0,0 +1,510 @@ ++// SPDX-License-Identifier: GPL-2.0-only OR MIT ++/* Code to dump registers and ALE table for the CPSW instances on K3 SoCs that are configured using ++ * the am65-cpsw-nuss device-driver in Linux. ++ * ++ * Copyright (C) 2025 Texas Instruments ++ * Author: Chintan Vankar ++ */ ++ ++#include ++#include ++ ++#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[NUM_MAC_OCTET]; ++ ++ 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 & 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_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 & IPV6_HIGH_ENTRY_FLAG) { ++ 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[NUM_MAC_OCTET]; ++ 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; ++ /* Values are stored in pair as reg_offset-reg_val, hence parse the same way*/ ++ 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 3ac15a7..a383eb6 100644 +--- a/ethtool.c ++++ b/ethtool.c +@@ -1162,6 +1162,7 @@ static const struct { + { "fsl_enetc", fsl_enetc_dump_regs }, + { "fsl_enetc_vf", fsl_enetc_dump_regs }, + { "hns3", hns3_dump_regs }, ++ { "am65-cpsw-nuss", am65_cpsw_dump_regs }, + }; + #endif + +diff --git a/internal.h b/internal.h +index 4b994f5..81212b4 100644 +--- a/internal.h ++++ b/internal.h +@@ -410,4 +410,7 @@ int cpsw_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + /* Microchip Ethernet Controller */ + int lan743x_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 + diff --git a/meta-arago-distro/recipes-extended/ethtool/ethtool_%.bbappend b/meta-arago-distro/recipes-extended/ethtool/ethtool_%.bbappend new file mode 100644 index 00000000..3b388151 --- /dev/null +++ b/meta-arago-distro/recipes-extended/ethtool/ethtool_%.bbappend @@ -0,0 +1,4 @@ +ETHTOOL_ARAGO = "" +ETHTOOL_ARAGO:arago = "ethtool-arago.inc" + +require ${ETHTOOL_ARAGO}