From nobody Fri Apr 3 17:34:50 2026 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 81C333E9289; Tue, 24 Mar 2026 10:51:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=68.232.154.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774349477; cv=none; b=cPnze3B51n9inMH/yOeRznoPtCN1upZ1n4fg0vWukC2Rue/4sihtRtAAFLappTCOBLsR9IbTHiEeKvN5uER3Wz6q0Nkj72QNjB/bcxm6Nn1AXrNEXeYgJJuvkj4ymDAFR3tOGYCgDrdUBg5fmeR/JHDKXi71P30fophj6OIoYMY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774349477; c=relaxed/simple; bh=chnlYdq6UL7M7aLHV4Wm3SYlwS5FEOy805nPlFj1sco=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=ub/c88v4LjbHLZ4a1+wI0/LY0CMbWROQ7Na8VK5JWTGhzCyIwK1zxySE8+ugz8ir9SzacEeuPBqreBbRh7gY4r1MIA+6Jhf+bf30pUYDiYx3cFHRBjlmsoZ0mD8kdOL1kC0Z/5rG/JQek/e9ucG7vCSEH/4Q+yRUhH39VKNNP1E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=microchip.com; spf=pass smtp.mailfrom=microchip.com; dkim=pass (2048-bit key) header.d=microchip.com header.i=@microchip.com header.b=vf6pn+3Z; arc=none smtp.client-ip=68.232.154.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=microchip.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=microchip.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=microchip.com header.i=@microchip.com header.b="vf6pn+3Z" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1774349468; x=1805885468; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=chnlYdq6UL7M7aLHV4Wm3SYlwS5FEOy805nPlFj1sco=; b=vf6pn+3ZUf1eOw1CtqTXa4WgUa990VxLOPpcYlC1duSQs5kS3Mh+DQ8L UrDa3Un0v7OlE7/WMcL+npxQUIvWRBPiWQsKgaaDbs5WI8Duj+eDxO7i2 +kIh7qOhHuVE9nG9E8gJnYksyS1glTErk8Trno9MHSrNLWYq1BtBuQ0Vx viW9yb73vRfia/SYqvRfBGOKpIttJXGzr1MWCvUaw6VZNnzk3EhVyUKOQ kpOR2kS97AIdn69xOpmZGlf/hAz5QQMFSUpJnjHeiuxvmCRJ1+81lfXGg thuY15VcQGw3Rr2/Rq5Ok5stMNQlkII31WcXTzH93n5Hz82hHhf+eRpbB Q==; X-CSE-ConnectionGUID: av7DD6XARYCJBGtV0zHjGA== X-CSE-MsgGUID: UP4L4f1mSZK/4st1RT2yCw== X-IronPort-AV: E=Sophos;i="6.23,138,1770620400"; d="scan'208";a="222375825" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Mar 2026 03:51:01 -0700 Received: from chn-vm-ex02.mchp-main.com (10.10.87.72) by chn-vm-ex2.mchp-main.com (10.10.87.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.2.2562.35; Tue, 24 Mar 2026 03:50:37 -0700 Received: from [127.0.0.1] (10.10.85.11) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server id 15.1.2507.58 via Frontend Transport; Tue, 24 Mar 2026 03:50:34 -0700 From: =?utf-8?q?Jens_Emil_Schulz_=C3=98stergaard?= Date: Tue, 24 Mar 2026 11:46:52 +0100 Subject: [PATCH net-next v2 9/9] net: dsa: lan9645x: add port statistics Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260324-dsa_lan9645x_switch_driver_base-v2-9-f7504e3b0681@microchip.com> References: <20260324-dsa_lan9645x_switch_driver_base-v2-0-f7504e3b0681@microchip.com> In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-0-f7504e3b0681@microchip.com> To: , Andrew Lunn , "Vladimir Oltean" , "David S. Miller" , "Eric Dumazet" , Jakub Kicinski , Paolo Abeni , Simon Horman , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Woojung Huh , Russell King , Steen Hegelund , Daniel Machon CC: , , , =?utf-8?q?Jens_Emil_Schulz_=C3=98stergaard?= X-Mailer: b4 0.15-dev Add statistics support for the port counters. Chip registers are 32 bit, so this unit is responsible maintaining a 64bit software cache, and updating it frequently to handle overflows in hardware. Reviewed-by: Steen Hegelund Signed-off-by: Jens Emil Schulz =C3=98stergaard --- Changes in v2: - introduce spinlock sw_lock protecting software counters and region buffer. - ran Ioana's selftest for standard counters drivers/net/hw/ethtool_std_stats.sh along with selftests/drivers/net/hw/ethtool_rmon.sh they pass except for software injected pause frames. - remove strings/counters covered by standard counters from get_strings/get_ethtool_stats. - fix proper use of 'src' in standard counters - remove static region table, and use stats_prepare_regions for dynamic region calculation inspired by ocelot. - fix queue leak in error path. --- drivers/net/dsa/microchip/lan9645x/Makefile | 1 + drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 86 ++ drivers/net/dsa/microchip/lan9645x/lan9645x_main.h | 3 + .../net/dsa/microchip/lan9645x/lan9645x_stats.c | 922 +++++++++++++++++= ++++ .../net/dsa/microchip/lan9645x/lan9645x_stats.h | 277 +++++++ 5 files changed, 1289 insertions(+) diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/= microchip/lan9645x/Makefile index 2413d11fe849..cd994943c1c0 100644 --- a/drivers/net/dsa/microchip/lan9645x/Makefile +++ b/drivers/net/dsa/microchip/lan9645x/Makefile @@ -8,4 +8,5 @@ mchp-lan9645x-objs :=3D \ lan9645x_npi.o \ lan9645x_phylink.o \ lan9645x_port.o \ + lan9645x_stats.o \ lan9645x_vlan.o \ diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/n= et/dsa/microchip/lan9645x/lan9645x_main.c index 24a60f40f6b8..9a46c08dad59 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c @@ -5,6 +5,7 @@ #include =20 #include "lan9645x_main.h" +#include "lan9645x_stats.h" =20 static const char *lan9645x_resource_names[NUM_TARGETS + 1] =3D { [TARGET_GCB] =3D "gcb", @@ -73,6 +74,7 @@ static void lan9645x_teardown(struct dsa_switch *ds) lan9645x_npi_port_deinit(lan9645x, lan9645x->npi); lan9645x_mac_deinit(lan9645x); lan9645x_mdb_deinit(lan9645x); + lan9645x_stats_deinit(lan9645x); mutex_destroy(&lan9645x->fwd_domain_lock); } =20 @@ -262,6 +264,12 @@ static int lan9645x_setup(struct dsa_switch *ds) if (!lan9645x->owq) return -ENOMEM; =20 + err =3D lan9645x_stats_init(lan9645x); + if (err) { + dev_err(dev, "Failed to init stats.\n"); + goto owq_destroy; + } + ds->mtu_enforcement_ingress =3D true; ds->assisted_learning_on_cpu_port =3D true; ds->fdb_isolation =3D true; @@ -272,6 +280,10 @@ static int lan9645x_setup(struct dsa_switch *ds) lan9645x->num_phys_ports - lan9645x->num_port_dis); =20 return 0; + +owq_destroy: + destroy_workqueue(lan9645x->owq); + return err; } =20 static void lan9645x_port_phylink_get_caps(struct dsa_switch *ds, int port, @@ -726,6 +738,68 @@ static int lan9645x_mdb_del(struct dsa_switch *ds, int= port, return err; } =20 +static void lan9645x_get_strings(struct dsa_switch *ds, int port, u32 stri= ngset, + uint8_t *data) +{ + lan9645x_stats_get_strings(ds->priv, port, stringset, data); +} + +static void lan9645x_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + lan9645x_stats_get_ethtool_stats(ds->priv, port, data); +} + +static int lan9645x_get_sset_count(struct dsa_switch *ds, int port, int ss= et) +{ + return lan9645x_stats_get_sset_count(ds->priv, port, sset); +} + +static void lan9645x_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + lan9645x_stats_get_eth_mac_stats(ds->priv, port, mac_stats); +} + +static void +lan9645x_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + lan9645x_stats_get_rmon_stats(ds->priv, port, rmon_stats, ranges); +} + +static void lan9645x_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + lan9645x_stats_get_stats64(ds->priv, port, s); +} + +static void lan9645x_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) +{ + lan9645x_stats_get_pause_stats(ds->priv, port, pause_stats); +} + +static void lan9645x_get_mm_stats(struct dsa_switch *ds, int port, + struct ethtool_mm_stats *stats) +{ + lan9645x_stats_get_mm_stats(ds->priv, port, stats); +} + +static void lan9645x_get_eth_phy_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_phy_stats *phy_stats) +{ + lan9645x_stats_get_eth_phy_stats(ds->priv, port, phy_stats); +} + +static void +lan9645x_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + lan9645x_stats_get_eth_ctrl_stats(ds->priv, port, ctrl_stats); +} + static const struct dsa_switch_ops lan9645x_switch_ops =3D { .get_tag_protocol =3D lan9645x_get_tag_protocol, =20 @@ -763,6 +837,18 @@ static const struct dsa_switch_ops lan9645x_switch_ops= =3D { /* Multicast database */ .port_mdb_add =3D lan9645x_mdb_add, .port_mdb_del =3D lan9645x_mdb_del, + + /* Port statistics counters. */ + .get_strings =3D lan9645x_get_strings, + .get_ethtool_stats =3D lan9645x_get_ethtool_stats, + .get_sset_count =3D lan9645x_get_sset_count, + .get_eth_mac_stats =3D lan9645x_get_eth_mac_stats, + .get_rmon_stats =3D lan9645x_get_rmon_stats, + .get_stats64 =3D lan9645x_get_stats64, + .get_pause_stats =3D lan9645x_get_pause_stats, + .get_mm_stats =3D lan9645x_get_mm_stats, + .get_eth_phy_stats =3D lan9645x_get_eth_phy_stats, + .get_eth_ctrl_stats =3D lan9645x_get_eth_ctrl_stats, }; =20 static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x) diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/n= et/dsa/microchip/lan9645x/lan9645x_main.h index c7e4276dd74a..bada40e73e57 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h @@ -226,6 +226,9 @@ struct lan9645x { */ struct mutex mdb_lock; =20 + /* Statistics */ + struct lan9645x_stats *stats; + int num_port_dis; bool dd_dis; bool tsn_dis; diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c b/drivers/= net/dsa/microchip/lan9645x/lan9645x_stats.c new file mode 100644 index 000000000000..3e82a859f173 --- /dev/null +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c @@ -0,0 +1,922 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2026 Microchip Technology Inc. + */ + +#include +#include + +#include "lan9645x_main.h" +#include "lan9645x_stats.h" + +#define LAN9645X_STATS_CHECK_DELAY (3 * HZ) + +static const u32 lan9645x_port_stats_layout[] =3D { + [SCNT_RX_OCT] =3D 0x0, + [SCNT_RX_UC] =3D 0x1, + [SCNT_RX_MC] =3D 0x2, + [SCNT_RX_BC] =3D 0x3, + [SCNT_RX_SHORT] =3D 0x4, + [SCNT_RX_FRAG] =3D 0x5, + [SCNT_RX_JABBER] =3D 0x6, + [SCNT_RX_CRC] =3D 0x7, + [SCNT_RX_SYMBOL_ERR] =3D 0x8, + [SCNT_RX_SZ_64] =3D 0x9, + [SCNT_RX_SZ_65_127] =3D 0xa, + [SCNT_RX_SZ_128_255] =3D 0xb, + [SCNT_RX_SZ_256_511] =3D 0xc, + [SCNT_RX_SZ_512_1023] =3D 0xd, + [SCNT_RX_SZ_1024_1526] =3D 0xe, + [SCNT_RX_SZ_JUMBO] =3D 0xf, + [SCNT_RX_PAUSE] =3D 0x10, + [SCNT_RX_CONTROL] =3D 0x11, + [SCNT_RX_LONG] =3D 0x12, + [SCNT_RX_CAT_DROP] =3D 0x13, + [SCNT_RX_RED_PRIO_0] =3D 0x14, + [SCNT_RX_RED_PRIO_1] =3D 0x15, + [SCNT_RX_RED_PRIO_2] =3D 0x16, + [SCNT_RX_RED_PRIO_3] =3D 0x17, + [SCNT_RX_RED_PRIO_4] =3D 0x18, + [SCNT_RX_RED_PRIO_5] =3D 0x19, + [SCNT_RX_RED_PRIO_6] =3D 0x1a, + [SCNT_RX_RED_PRIO_7] =3D 0x1b, + [SCNT_RX_YELLOW_PRIO_0] =3D 0x1c, + [SCNT_RX_YELLOW_PRIO_1] =3D 0x1d, + [SCNT_RX_YELLOW_PRIO_2] =3D 0x1e, + [SCNT_RX_YELLOW_PRIO_3] =3D 0x1f, + [SCNT_RX_YELLOW_PRIO_4] =3D 0x20, + [SCNT_RX_YELLOW_PRIO_5] =3D 0x21, + [SCNT_RX_YELLOW_PRIO_6] =3D 0x22, + [SCNT_RX_YELLOW_PRIO_7] =3D 0x23, + [SCNT_RX_GREEN_PRIO_0] =3D 0x24, + [SCNT_RX_GREEN_PRIO_1] =3D 0x25, + [SCNT_RX_GREEN_PRIO_2] =3D 0x26, + [SCNT_RX_GREEN_PRIO_3] =3D 0x27, + [SCNT_RX_GREEN_PRIO_4] =3D 0x28, + [SCNT_RX_GREEN_PRIO_5] =3D 0x29, + [SCNT_RX_GREEN_PRIO_6] =3D 0x2a, + [SCNT_RX_GREEN_PRIO_7] =3D 0x2b, + [SCNT_RX_ASSEMBLY_ERR] =3D 0x2c, + [SCNT_RX_SMD_ERR] =3D 0x2d, + [SCNT_RX_ASSEMBLY_OK] =3D 0x2e, + [SCNT_RX_MERGE_FRAG] =3D 0x2f, + [SCNT_RX_PMAC_OCT] =3D 0x30, + [SCNT_RX_PMAC_UC] =3D 0x31, + [SCNT_RX_PMAC_MC] =3D 0x32, + [SCNT_RX_PMAC_BC] =3D 0x33, + [SCNT_RX_PMAC_SHORT] =3D 0x34, + [SCNT_RX_PMAC_FRAG] =3D 0x35, + [SCNT_RX_PMAC_JABBER] =3D 0x36, + [SCNT_RX_PMAC_CRC] =3D 0x37, + [SCNT_RX_PMAC_SYMBOL_ERR] =3D 0x38, + [SCNT_RX_PMAC_SZ_64] =3D 0x39, + [SCNT_RX_PMAC_SZ_65_127] =3D 0x3a, + [SCNT_RX_PMAC_SZ_128_255] =3D 0x3b, + [SCNT_RX_PMAC_SZ_256_511] =3D 0x3c, + [SCNT_RX_PMAC_SZ_512_1023] =3D 0x3d, + [SCNT_RX_PMAC_SZ_1024_1526] =3D 0x3e, + [SCNT_RX_PMAC_SZ_JUMBO] =3D 0x3f, + [SCNT_RX_PMAC_PAUSE] =3D 0x40, + [SCNT_RX_PMAC_CONTROL] =3D 0x41, + [SCNT_RX_PMAC_LONG] =3D 0x42, + [SCNT_TX_OCT] =3D 0x80, + [SCNT_TX_UC] =3D 0x81, + [SCNT_TX_MC] =3D 0x82, + [SCNT_TX_BC] =3D 0x83, + [SCNT_TX_COL] =3D 0x84, + [SCNT_TX_DROP] =3D 0x85, + [SCNT_TX_PAUSE] =3D 0x86, + [SCNT_TX_SZ_64] =3D 0x87, + [SCNT_TX_SZ_65_127] =3D 0x88, + [SCNT_TX_SZ_128_255] =3D 0x89, + [SCNT_TX_SZ_256_511] =3D 0x8a, + [SCNT_TX_SZ_512_1023] =3D 0x8b, + [SCNT_TX_SZ_1024_1526] =3D 0x8c, + [SCNT_TX_SZ_JUMBO] =3D 0x8d, + [SCNT_TX_YELLOW_PRIO_0] =3D 0x8e, + [SCNT_TX_YELLOW_PRIO_1] =3D 0x8f, + [SCNT_TX_YELLOW_PRIO_2] =3D 0x90, + [SCNT_TX_YELLOW_PRIO_3] =3D 0x91, + [SCNT_TX_YELLOW_PRIO_4] =3D 0x92, + [SCNT_TX_YELLOW_PRIO_5] =3D 0x93, + [SCNT_TX_YELLOW_PRIO_6] =3D 0x94, + [SCNT_TX_YELLOW_PRIO_7] =3D 0x95, + [SCNT_TX_GREEN_PRIO_0] =3D 0x96, + [SCNT_TX_GREEN_PRIO_1] =3D 0x97, + [SCNT_TX_GREEN_PRIO_2] =3D 0x98, + [SCNT_TX_GREEN_PRIO_3] =3D 0x99, + [SCNT_TX_GREEN_PRIO_4] =3D 0x9a, + [SCNT_TX_GREEN_PRIO_5] =3D 0x9b, + [SCNT_TX_GREEN_PRIO_6] =3D 0x9c, + [SCNT_TX_GREEN_PRIO_7] =3D 0x9d, + [SCNT_TX_AGED] =3D 0x9e, + [SCNT_TX_LLCT] =3D 0x9f, + [SCNT_TX_CT] =3D 0xa0, + [SCNT_TX_BUFDROP] =3D 0xa1, + [SCNT_TX_MM_HOLD] =3D 0xa2, + [SCNT_TX_MERGE_FRAG] =3D 0xa3, + [SCNT_TX_PMAC_OCT] =3D 0xa4, + [SCNT_TX_PMAC_UC] =3D 0xa5, + [SCNT_TX_PMAC_MC] =3D 0xa6, + [SCNT_TX_PMAC_BC] =3D 0xa7, + [SCNT_TX_PMAC_PAUSE] =3D 0xa8, + [SCNT_TX_PMAC_SZ_64] =3D 0xa9, + [SCNT_TX_PMAC_SZ_65_127] =3D 0xaa, + [SCNT_TX_PMAC_SZ_128_255] =3D 0xab, + [SCNT_TX_PMAC_SZ_256_511] =3D 0xac, + [SCNT_TX_PMAC_SZ_512_1023] =3D 0xad, + [SCNT_TX_PMAC_SZ_1024_1526] =3D 0xae, + [SCNT_TX_PMAC_SZ_JUMBO] =3D 0xaf, + [SCNT_DR_LOCAL] =3D 0x100, + [SCNT_DR_TAIL] =3D 0x101, + [SCNT_DR_YELLOW_PRIO_0] =3D 0x102, + [SCNT_DR_YELLOW_PRIO_1] =3D 0x103, + [SCNT_DR_YELLOW_PRIO_2] =3D 0x104, + [SCNT_DR_YELLOW_PRIO_3] =3D 0x105, + [SCNT_DR_YELLOW_PRIO_4] =3D 0x106, + [SCNT_DR_YELLOW_PRIO_5] =3D 0x107, + [SCNT_DR_YELLOW_PRIO_6] =3D 0x108, + [SCNT_DR_YELLOW_PRIO_7] =3D 0x109, + [SCNT_DR_GREEN_PRIO_0] =3D 0x10a, + [SCNT_DR_GREEN_PRIO_1] =3D 0x10b, + [SCNT_DR_GREEN_PRIO_2] =3D 0x10c, + [SCNT_DR_GREEN_PRIO_3] =3D 0x10d, + [SCNT_DR_GREEN_PRIO_4] =3D 0x10e, + [SCNT_DR_GREEN_PRIO_5] =3D 0x10f, + [SCNT_DR_GREEN_PRIO_6] =3D 0x110, + [SCNT_DR_GREEN_PRIO_7] =3D 0x111, +}; + +struct lan9645x_ethtool_stat { + char name[ETH_GSTRING_LEN]; + u16 idx; +}; + +static const struct lan9645x_ethtool_stat lan9645x_port_ethtool_stats[] = =3D { + { "rx_uc", SCNT_RX_UC }, + { "rx_cat_drop", SCNT_RX_CAT_DROP }, + { "rx_red_prio_0", SCNT_RX_RED_PRIO_0 }, + { "rx_red_prio_1", SCNT_RX_RED_PRIO_1 }, + { "rx_red_prio_2", SCNT_RX_RED_PRIO_2 }, + { "rx_red_prio_3", SCNT_RX_RED_PRIO_3 }, + { "rx_red_prio_4", SCNT_RX_RED_PRIO_4 }, + { "rx_red_prio_5", SCNT_RX_RED_PRIO_5 }, + { "rx_red_prio_6", SCNT_RX_RED_PRIO_6 }, + { "rx_red_prio_7", SCNT_RX_RED_PRIO_7 }, + { "rx_yellow_prio_0", SCNT_RX_YELLOW_PRIO_0 }, + { "rx_yellow_prio_1", SCNT_RX_YELLOW_PRIO_1 }, + { "rx_yellow_prio_2", SCNT_RX_YELLOW_PRIO_2 }, + { "rx_yellow_prio_3", SCNT_RX_YELLOW_PRIO_3 }, + { "rx_yellow_prio_4", SCNT_RX_YELLOW_PRIO_4 }, + { "rx_yellow_prio_5", SCNT_RX_YELLOW_PRIO_5 }, + { "rx_yellow_prio_6", SCNT_RX_YELLOW_PRIO_6 }, + { "rx_yellow_prio_7", SCNT_RX_YELLOW_PRIO_7 }, + { "rx_green_prio_0", SCNT_RX_GREEN_PRIO_0 }, + { "rx_green_prio_1", SCNT_RX_GREEN_PRIO_1 }, + { "rx_green_prio_2", SCNT_RX_GREEN_PRIO_2 }, + { "rx_green_prio_3", SCNT_RX_GREEN_PRIO_3 }, + { "rx_green_prio_4", SCNT_RX_GREEN_PRIO_4 }, + { "rx_green_prio_5", SCNT_RX_GREEN_PRIO_5 }, + { "rx_green_prio_6", SCNT_RX_GREEN_PRIO_6 }, + { "rx_green_prio_7", SCNT_RX_GREEN_PRIO_7 }, + { "tx_uc", SCNT_TX_UC }, + { "tx_drop", SCNT_TX_DROP }, + { "tx_yellow_prio_0", SCNT_TX_YELLOW_PRIO_0 }, + { "tx_yellow_prio_1", SCNT_TX_YELLOW_PRIO_1 }, + { "tx_yellow_prio_2", SCNT_TX_YELLOW_PRIO_2 }, + { "tx_yellow_prio_3", SCNT_TX_YELLOW_PRIO_3 }, + { "tx_yellow_prio_4", SCNT_TX_YELLOW_PRIO_4 }, + { "tx_yellow_prio_5", SCNT_TX_YELLOW_PRIO_5 }, + { "tx_yellow_prio_6", SCNT_TX_YELLOW_PRIO_6 }, + { "tx_yellow_prio_7", SCNT_TX_YELLOW_PRIO_7 }, + { "tx_green_prio_0", SCNT_TX_GREEN_PRIO_0 }, + { "tx_green_prio_1", SCNT_TX_GREEN_PRIO_1 }, + { "tx_green_prio_2", SCNT_TX_GREEN_PRIO_2 }, + { "tx_green_prio_3", SCNT_TX_GREEN_PRIO_3 }, + { "tx_green_prio_4", SCNT_TX_GREEN_PRIO_4 }, + { "tx_green_prio_5", SCNT_TX_GREEN_PRIO_5 }, + { "tx_green_prio_6", SCNT_TX_GREEN_PRIO_6 }, + { "tx_green_prio_7", SCNT_TX_GREEN_PRIO_7 }, + { "tx_aged", SCNT_TX_AGED }, + { "tx_bufdrop", SCNT_TX_BUFDROP }, + { "dr_local", SCNT_DR_LOCAL }, + { "dr_tail", SCNT_DR_TAIL }, + { "dr_yellow_prio_0", SCNT_DR_YELLOW_PRIO_0 }, + { "dr_yellow_prio_1", SCNT_DR_YELLOW_PRIO_1 }, + { "dr_yellow_prio_2", SCNT_DR_YELLOW_PRIO_2 }, + { "dr_yellow_prio_3", SCNT_DR_YELLOW_PRIO_3 }, + { "dr_yellow_prio_4", SCNT_DR_YELLOW_PRIO_4 }, + { "dr_yellow_prio_5", SCNT_DR_YELLOW_PRIO_5 }, + { "dr_yellow_prio_6", SCNT_DR_YELLOW_PRIO_6 }, + { "dr_yellow_prio_7", SCNT_DR_YELLOW_PRIO_7 }, + { "dr_green_prio_0", SCNT_DR_GREEN_PRIO_0 }, + { "dr_green_prio_1", SCNT_DR_GREEN_PRIO_1 }, + { "dr_green_prio_2", SCNT_DR_GREEN_PRIO_2 }, + { "dr_green_prio_3", SCNT_DR_GREEN_PRIO_3 }, + { "dr_green_prio_4", SCNT_DR_GREEN_PRIO_4 }, + { "dr_green_prio_5", SCNT_DR_GREEN_PRIO_5 }, + { "dr_green_prio_6", SCNT_DR_GREEN_PRIO_6 }, + { "dr_green_prio_7", SCNT_DR_GREEN_PRIO_7 }, +}; + +static const struct lan9645x_view_stats lan9645x_view_stat_cfgs[] =3D { + [LAN9645X_STAT_PORTS] =3D { + .name =3D "ports", + .type =3D LAN9645X_STAT_PORTS, + .layout =3D lan9645x_port_stats_layout, + .num_cnts =3D ARRAY_SIZE(lan9645x_port_stats_layout), + .num_indexes =3D NUM_PHYS_PORTS, + }, +}; + +static int __lan9645x_stats_view_idx_hw_read(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, + int idx) +{ + struct lan9645x_stat_region region; + struct lan9645x_view_stats *vstats; + u32 *region_buf; + int err; + + lockdep_assert_held(&lan9645x->stats->hw_lock); + + vstats =3D lan9645x_get_vstats(lan9645x, vtype); + if (!vstats || idx < 0 || idx >=3D vstats->num_indexes) + return -EINVAL; + + lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(idx), lan9645x, SYS_STAT_CFG); + + region_buf =3D &vstats->buf[vstats->num_cnts * idx]; + + /* Each region for this index contains counters which are at sequential + * addresses, so we can use bulk reads to ease lock pressure a bit. + */ + for (int r =3D 0; r < vstats->num_regions; r++) { + region =3D vstats->regions[r]; + err =3D lan_bulk_rd(®ion_buf[region.cnts_base_idx], region.cnt, + lan9645x, SYS_CNT(region.base_offset)); + if (err) { + dev_err(lan9645x->dev, + "stats bulk read err vtype=3D%d idx=3D%d err=3D%d\n", + vtype, idx, err); + return err; + } + } + + return 0; +} + +static void +__lan9645x_stats_view_idx_transfer(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, int idx) +{ + struct lan9645x_view_stats *vstats; + u64 *idx_counters; + u32 *region_buf; + int cntr; + + lockdep_assert_held(&lan9645x->stats->sw_lock); + + vstats =3D lan9645x_get_vstats(lan9645x, vtype); + if (!vstats || idx < 0 || idx >=3D vstats->num_indexes) + return; + + idx_counters =3D STATS_INDEX(vstats, idx); + region_buf =3D &vstats->buf[vstats->num_cnts * idx]; + + for (cntr =3D 0; cntr < vstats->num_cnts; cntr++) + lan9645x_stats_add_cnt(&idx_counters[cntr], region_buf[cntr]); +} + +static void __lan9645x_stats_view_idx_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, + int idx) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + + lockdep_assert_held(&s->hw_lock); + + if (!__lan9645x_stats_view_idx_hw_read(lan9645x, vtype, idx)) { + spin_lock(&s->sw_lock); + __lan9645x_stats_view_idx_transfer(lan9645x, vtype, idx); + spin_unlock(&s->sw_lock); + } +} + +static u64 *lan9645x_stats_view_idx_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, + int idx) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + + mutex_lock(&s->hw_lock); + __lan9645x_stats_view_idx_update(lan9645x, vtype, idx); + mutex_unlock(&s->hw_lock); + + return STAT_COUNTERS(lan9645x, vtype, idx); +} + +static void lan9645x_stats_view_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + struct lan9645x_view_stats *vstats; + int idx; + + vstats =3D lan9645x_get_vstats(lan9645x, vtype); + if (!vstats) + return; + + switch (vtype) { + case LAN9645X_STAT_PORTS: + mutex_lock(&s->hw_lock); + for (idx =3D 0; idx < vstats->num_indexes; idx++) { + if (dsa_is_unused_port(lan9645x->ds, idx)) + continue; + __lan9645x_stats_view_idx_update(lan9645x, vtype, idx); + } + mutex_unlock(&s->hw_lock); + return; + default: + return; + } +} + +static void lan9645x_stats_update(struct lan9645x *lan9645x) +{ + for (int vtype =3D 0; vtype < LAN9645X_STAT_NUM; vtype++) + lan9645x_stats_view_update(lan9645x, vtype); +} + +void lan9645x_stats_get_strings(struct lan9645x *lan9645x, int port, + u32 stringset, u8 *data) +{ + int i; + + if (stringset !=3D ETH_SS_STATS) + return; + + for (i =3D 0; i < ARRAY_SIZE(lan9645x_port_ethtool_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + lan9645x_port_ethtool_stats[i].name, ETH_GSTRING_LEN); +} + +int lan9645x_stats_get_sset_count(struct lan9645x *lan9645x, int port, int= sset) +{ + if (sset !=3D ETH_SS_STATS) + return -EOPNOTSUPP; + + return ARRAY_SIZE(lan9645x_port_ethtool_stats); +} + +void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port, + u64 *data) +{ + struct lan9645x_stats *stats =3D lan9645x->stats; + u64 *c; + int i; + + c =3D lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + spin_lock(&stats->sw_lock); + for (i =3D 0; i < ARRAY_SIZE(lan9645x_port_ethtool_stats); i++) + *data++ =3D c[lan9645x_port_ethtool_stats[i].idx]; + spin_unlock(&stats->sw_lock); +} + +static u64 *lan9645x_stats_port_update(struct lan9645x *lan9645x, int port) +{ + return lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, + port); +} + +void lan9645x_stats_get_eth_mac_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_mac_stats *m) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D lan9645x_stats_port_update(lan9645x, port); + + spin_lock(&s->sw_lock); + + switch (m->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + m->FramesTransmittedOK =3D c[SCNT_TX_UC] + + c[SCNT_TX_MC] + + c[SCNT_TX_BC]; + m->SingleCollisionFrames =3D c[SCNT_TX_COL]; + m->FramesReceivedOK =3D c[SCNT_RX_UC] + + c[SCNT_RX_MC] + + c[SCNT_RX_BC]; + m->FrameCheckSequenceErrors =3D c[SCNT_RX_CRC]; + m->OctetsTransmittedOK =3D c[SCNT_TX_OCT]; + m->OctetsReceivedOK =3D c[SCNT_RX_OCT]; + m->MulticastFramesXmittedOK =3D c[SCNT_TX_MC]; + m->BroadcastFramesXmittedOK =3D c[SCNT_TX_BC]; + m->MulticastFramesReceivedOK =3D c[SCNT_RX_MC]; + m->BroadcastFramesReceivedOK =3D c[SCNT_RX_BC]; + m->InRangeLengthErrors =3D c[SCNT_RX_FRAG] + + c[SCNT_RX_JABBER] + + c[SCNT_RX_CRC]; + m->OutOfRangeLengthField =3D c[SCNT_RX_SHORT] + + c[SCNT_RX_LONG]; + m->FrameTooLongErrors =3D c[SCNT_RX_LONG]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + m->FramesTransmittedOK =3D c[SCNT_TX_PMAC_UC] + + c[SCNT_TX_PMAC_MC] + + c[SCNT_TX_PMAC_BC]; + m->FramesReceivedOK =3D c[SCNT_RX_PMAC_UC] + + c[SCNT_RX_PMAC_MC] + + c[SCNT_RX_PMAC_BC]; + m->FrameCheckSequenceErrors =3D c[SCNT_RX_PMAC_CRC]; + m->OctetsTransmittedOK =3D c[SCNT_TX_PMAC_OCT]; + m->OctetsReceivedOK =3D c[SCNT_RX_PMAC_OCT]; + m->MulticastFramesXmittedOK =3D c[SCNT_TX_PMAC_MC]; + m->BroadcastFramesXmittedOK =3D c[SCNT_TX_PMAC_BC]; + m->MulticastFramesReceivedOK =3D c[SCNT_RX_PMAC_MC]; + m->BroadcastFramesReceivedOK =3D c[SCNT_RX_PMAC_BC]; + m->InRangeLengthErrors =3D c[SCNT_RX_PMAC_FRAG] + + c[SCNT_RX_PMAC_JABBER] + + c[SCNT_RX_PMAC_CRC]; + m->OutOfRangeLengthField =3D c[SCNT_RX_PMAC_SHORT] + + c[SCNT_RX_PMAC_LONG]; + m->FrameTooLongErrors =3D c[SCNT_RX_PMAC_LONG]; + break; + default: + m->FramesTransmittedOK =3D c[SCNT_TX_UC] + + c[SCNT_TX_MC] + + c[SCNT_TX_BC] + + c[SCNT_TX_PMAC_UC] + + c[SCNT_TX_PMAC_MC] + + c[SCNT_TX_PMAC_BC]; + m->SingleCollisionFrames =3D c[SCNT_TX_COL]; + m->FramesReceivedOK =3D c[SCNT_RX_UC] + + c[SCNT_RX_MC] + + c[SCNT_RX_BC] + + c[SCNT_RX_PMAC_UC] + + c[SCNT_RX_PMAC_MC] + + c[SCNT_RX_PMAC_BC]; + m->FrameCheckSequenceErrors =3D c[SCNT_RX_CRC] + + c[SCNT_RX_PMAC_CRC]; + m->OctetsTransmittedOK =3D c[SCNT_TX_OCT] + + c[SCNT_TX_PMAC_OCT]; + m->OctetsReceivedOK =3D c[SCNT_RX_OCT] + + c[SCNT_RX_PMAC_OCT]; + m->MulticastFramesXmittedOK =3D c[SCNT_TX_MC] + + c[SCNT_TX_PMAC_MC]; + m->BroadcastFramesXmittedOK =3D c[SCNT_TX_BC] + + c[SCNT_TX_PMAC_BC]; + m->MulticastFramesReceivedOK =3D c[SCNT_RX_MC] + + c[SCNT_RX_PMAC_MC]; + m->BroadcastFramesReceivedOK =3D c[SCNT_RX_BC] + + c[SCNT_RX_PMAC_BC]; + m->InRangeLengthErrors =3D c[SCNT_RX_FRAG] + + c[SCNT_RX_JABBER] + + c[SCNT_RX_CRC] + + c[SCNT_RX_PMAC_FRAG] + + c[SCNT_RX_PMAC_JABBER] + + c[SCNT_RX_PMAC_CRC]; + m->OutOfRangeLengthField =3D c[SCNT_RX_SHORT] + + c[SCNT_RX_LONG] + + c[SCNT_RX_PMAC_SHORT] + + c[SCNT_RX_PMAC_LONG]; + m->FrameTooLongErrors =3D c[SCNT_RX_LONG] + + c[SCNT_RX_PMAC_LONG]; + break; + } + + spin_unlock(&s->sw_lock); +} + +static const struct ethtool_rmon_hist_range lan9645x_rmon_ranges[] =3D { + { 0, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1526 }, + { 1527, 0xffff }, + {} +}; + +void +lan9645x_stats_get_rmon_stats(struct lan9645x *lan9645x, int port, + struct ethtool_rmon_stats *r, + const struct ethtool_rmon_hist_range **ranges) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D lan9645x_stats_port_update(lan9645x, port); + + spin_lock(&s->sw_lock); + + switch (r->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + r->undersize_pkts =3D c[SCNT_RX_SHORT]; + r->oversize_pkts =3D c[SCNT_RX_LONG]; + r->fragments =3D c[SCNT_RX_FRAG]; + r->jabbers =3D c[SCNT_RX_JABBER]; + r->hist[0] =3D c[SCNT_RX_SZ_64]; + r->hist[1] =3D c[SCNT_RX_SZ_65_127]; + r->hist[2] =3D c[SCNT_RX_SZ_128_255]; + r->hist[3] =3D c[SCNT_RX_SZ_256_511]; + r->hist[4] =3D c[SCNT_RX_SZ_512_1023]; + r->hist[5] =3D c[SCNT_RX_SZ_1024_1526]; + r->hist[6] =3D c[SCNT_RX_SZ_JUMBO]; + r->hist_tx[0] =3D c[SCNT_TX_SZ_64]; + r->hist_tx[1] =3D c[SCNT_TX_SZ_65_127]; + r->hist_tx[2] =3D c[SCNT_TX_SZ_128_255]; + r->hist_tx[3] =3D c[SCNT_TX_SZ_256_511]; + r->hist_tx[4] =3D c[SCNT_TX_SZ_512_1023]; + r->hist_tx[5] =3D c[SCNT_TX_SZ_1024_1526]; + r->hist_tx[6] =3D c[SCNT_TX_SZ_JUMBO]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + r->undersize_pkts =3D c[SCNT_RX_PMAC_SHORT]; + r->oversize_pkts =3D c[SCNT_RX_PMAC_LONG]; + r->fragments =3D c[SCNT_RX_PMAC_FRAG]; + r->jabbers =3D c[SCNT_RX_PMAC_JABBER]; + r->hist[0] =3D c[SCNT_RX_PMAC_SZ_64]; + r->hist[1] =3D c[SCNT_RX_PMAC_SZ_65_127]; + r->hist[2] =3D c[SCNT_RX_PMAC_SZ_128_255]; + r->hist[3] =3D c[SCNT_RX_PMAC_SZ_256_511]; + r->hist[4] =3D c[SCNT_RX_PMAC_SZ_512_1023]; + r->hist[5] =3D c[SCNT_RX_PMAC_SZ_1024_1526]; + r->hist[6] =3D c[SCNT_RX_PMAC_SZ_JUMBO]; + r->hist_tx[0] =3D c[SCNT_TX_PMAC_SZ_64]; + r->hist_tx[1] =3D c[SCNT_TX_PMAC_SZ_65_127]; + r->hist_tx[2] =3D c[SCNT_TX_PMAC_SZ_128_255]; + r->hist_tx[3] =3D c[SCNT_TX_PMAC_SZ_256_511]; + r->hist_tx[4] =3D c[SCNT_TX_PMAC_SZ_512_1023]; + r->hist_tx[5] =3D c[SCNT_TX_PMAC_SZ_1024_1526]; + r->hist_tx[6] =3D c[SCNT_TX_PMAC_SZ_JUMBO]; + break; + default: + r->undersize_pkts =3D c[SCNT_RX_SHORT] + + c[SCNT_RX_PMAC_SHORT]; + r->oversize_pkts =3D c[SCNT_RX_LONG] + + c[SCNT_RX_PMAC_LONG]; + r->fragments =3D c[SCNT_RX_FRAG] + + c[SCNT_RX_PMAC_FRAG]; + r->jabbers =3D c[SCNT_RX_JABBER] + + c[SCNT_RX_PMAC_JABBER]; + r->hist[0] =3D c[SCNT_RX_SZ_64] + + c[SCNT_RX_PMAC_SZ_64]; + r->hist[1] =3D c[SCNT_RX_SZ_65_127] + + c[SCNT_RX_PMAC_SZ_65_127]; + r->hist[2] =3D c[SCNT_RX_SZ_128_255] + + c[SCNT_RX_PMAC_SZ_128_255]; + r->hist[3] =3D c[SCNT_RX_SZ_256_511] + + c[SCNT_RX_PMAC_SZ_256_511]; + r->hist[4] =3D c[SCNT_RX_SZ_512_1023] + + c[SCNT_RX_PMAC_SZ_512_1023]; + r->hist[5] =3D c[SCNT_RX_SZ_1024_1526] + + c[SCNT_RX_PMAC_SZ_1024_1526]; + r->hist[6] =3D c[SCNT_RX_SZ_JUMBO] + + c[SCNT_RX_PMAC_SZ_JUMBO]; + r->hist_tx[0] =3D c[SCNT_TX_SZ_64] + + c[SCNT_TX_PMAC_SZ_64]; + r->hist_tx[1] =3D c[SCNT_TX_SZ_65_127] + + c[SCNT_TX_PMAC_SZ_65_127]; + r->hist_tx[2] =3D c[SCNT_TX_SZ_128_255] + + c[SCNT_TX_PMAC_SZ_128_255]; + r->hist_tx[3] =3D c[SCNT_TX_SZ_256_511] + + c[SCNT_TX_PMAC_SZ_256_511]; + r->hist_tx[4] =3D c[SCNT_TX_SZ_512_1023] + + c[SCNT_TX_PMAC_SZ_512_1023]; + r->hist_tx[5] =3D c[SCNT_TX_SZ_1024_1526] + + c[SCNT_TX_PMAC_SZ_1024_1526]; + r->hist_tx[6] =3D c[SCNT_TX_SZ_JUMBO] + + c[SCNT_TX_PMAC_SZ_JUMBO]; + break; + } + + spin_unlock(&s->sw_lock); + + *ranges =3D lan9645x_rmon_ranges; +} + +/* Called in atomic context */ +void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port, + struct rtnl_link_stats64 *stats) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + spin_lock(&s->sw_lock); + + stats->rx_bytes =3D c[SCNT_RX_OCT] + c[SCNT_RX_PMAC_OCT]; + + stats->rx_packets =3D c[SCNT_RX_SHORT] + + c[SCNT_RX_FRAG] + + c[SCNT_RX_JABBER] + + c[SCNT_RX_CRC] + + c[SCNT_RX_SYMBOL_ERR] + + c[SCNT_RX_SZ_64] + + c[SCNT_RX_SZ_65_127] + + c[SCNT_RX_SZ_128_255] + + c[SCNT_RX_SZ_256_511] + + c[SCNT_RX_SZ_512_1023] + + c[SCNT_RX_SZ_1024_1526] + + c[SCNT_RX_SZ_JUMBO] + + c[SCNT_RX_LONG] + + c[SCNT_RX_PMAC_SHORT] + + c[SCNT_RX_PMAC_FRAG] + + c[SCNT_RX_PMAC_JABBER] + + c[SCNT_RX_PMAC_SZ_64] + + c[SCNT_RX_PMAC_SZ_65_127] + + c[SCNT_RX_PMAC_SZ_128_255] + + c[SCNT_RX_PMAC_SZ_256_511] + + c[SCNT_RX_PMAC_SZ_512_1023] + + c[SCNT_RX_PMAC_SZ_1024_1526] + + c[SCNT_RX_PMAC_SZ_JUMBO]; + + stats->multicast =3D c[SCNT_RX_MC] + c[SCNT_RX_PMAC_MC]; + + stats->rx_errors =3D c[SCNT_RX_SHORT] + + c[SCNT_RX_FRAG] + + c[SCNT_RX_JABBER] + + c[SCNT_RX_CRC] + + c[SCNT_RX_SYMBOL_ERR] + + c[SCNT_RX_LONG] + + c[SCNT_RX_PMAC_SHORT] + + c[SCNT_RX_PMAC_FRAG] + + c[SCNT_RX_PMAC_JABBER] + + c[SCNT_RX_PMAC_CRC] + + c[SCNT_RX_PMAC_SYMBOL_ERR] + + c[SCNT_RX_PMAC_LONG]; + + stats->rx_dropped =3D c[SCNT_RX_LONG] + + c[SCNT_DR_LOCAL] + + c[SCNT_DR_TAIL] + + c[SCNT_RX_CAT_DROP] + + c[SCNT_RX_RED_PRIO_0] + + c[SCNT_RX_RED_PRIO_1] + + c[SCNT_RX_RED_PRIO_2] + + c[SCNT_RX_RED_PRIO_3] + + c[SCNT_RX_RED_PRIO_4] + + c[SCNT_RX_RED_PRIO_5] + + c[SCNT_RX_RED_PRIO_6] + + c[SCNT_RX_RED_PRIO_7]; + + for (int i =3D 0; i < LAN9645X_NUM_TC; i++) { + stats->rx_dropped +=3D c[SCNT_DR_YELLOW_PRIO_0 + i] + + c[SCNT_DR_GREEN_PRIO_0 + i]; + } + + stats->tx_bytes =3D c[SCNT_TX_OCT] + c[SCNT_TX_PMAC_OCT]; + + stats->tx_packets =3D c[SCNT_TX_SZ_64] + + c[SCNT_TX_SZ_65_127] + + c[SCNT_TX_SZ_128_255] + + c[SCNT_TX_SZ_256_511] + + c[SCNT_TX_SZ_512_1023] + + c[SCNT_TX_SZ_1024_1526] + + c[SCNT_TX_SZ_JUMBO] + + c[SCNT_TX_PMAC_SZ_64] + + c[SCNT_TX_PMAC_SZ_65_127] + + c[SCNT_TX_PMAC_SZ_128_255] + + c[SCNT_TX_PMAC_SZ_256_511] + + c[SCNT_TX_PMAC_SZ_512_1023] + + c[SCNT_TX_PMAC_SZ_1024_1526] + + c[SCNT_TX_PMAC_SZ_JUMBO]; + + stats->tx_dropped =3D c[SCNT_TX_DROP] + c[SCNT_TX_AGED]; + + stats->collisions =3D c[SCNT_TX_COL]; + + spin_unlock(&s->sw_lock); +} + +void lan9645x_stats_get_eth_phy_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_phy_stats *p) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D lan9645x_stats_port_update(lan9645x, port); + + spin_lock(&s->sw_lock); + + switch (p->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + p->SymbolErrorDuringCarrier =3D c[SCNT_RX_SYMBOL_ERR]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + p->SymbolErrorDuringCarrier =3D c[SCNT_RX_PMAC_SYMBOL_ERR]; + break; + default: + p->SymbolErrorDuringCarrier =3D c[SCNT_RX_SYMBOL_ERR] + + c[SCNT_RX_PMAC_SYMBOL_ERR]; + break; + } + + spin_unlock(&s->sw_lock); +} + +void +lan9645x_stats_get_eth_ctrl_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_ctrl_stats *ctrl) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D lan9645x_stats_port_update(lan9645x, port); + + spin_lock(&s->sw_lock); + + switch (ctrl->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + ctrl->MACControlFramesReceived =3D c[SCNT_RX_CONTROL]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + ctrl->MACControlFramesReceived =3D c[SCNT_RX_PMAC_CONTROL]; + break; + default: + ctrl->MACControlFramesReceived =3D c[SCNT_RX_CONTROL] + + c[SCNT_RX_PMAC_CONTROL]; + break; + } + + spin_unlock(&s->sw_lock); +} + +void lan9645x_stats_get_pause_stats(struct lan9645x *lan9645x, int port, + struct ethtool_pause_stats *ps) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D lan9645x_stats_port_update(lan9645x, port); + + spin_lock(&s->sw_lock); + + switch (ps->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + ps->tx_pause_frames =3D c[SCNT_TX_PAUSE]; + ps->rx_pause_frames =3D c[SCNT_RX_PAUSE]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + ps->tx_pause_frames =3D c[SCNT_TX_PMAC_PAUSE]; + ps->rx_pause_frames =3D c[SCNT_RX_PMAC_PAUSE]; + break; + default: + ps->tx_pause_frames =3D c[SCNT_TX_PAUSE] + c[SCNT_TX_PMAC_PAUSE]; + ps->rx_pause_frames =3D c[SCNT_RX_PAUSE] + c[SCNT_RX_PMAC_PAUSE]; + break; + } + + spin_unlock(&s->sw_lock); +} + +void lan9645x_stats_get_mm_stats(struct lan9645x *lan9645x, int port, + struct ethtool_mm_stats *stats) +{ + struct lan9645x_stats *s =3D lan9645x->stats; + u64 *c; + + c =3D lan9645x_stats_port_update(lan9645x, port); + + spin_lock(&s->sw_lock); + + stats->MACMergeFrameAssErrorCount =3D c[SCNT_RX_ASSEMBLY_ERR]; + stats->MACMergeFrameSmdErrorCount =3D c[SCNT_RX_SMD_ERR]; + stats->MACMergeFrameAssOkCount =3D c[SCNT_RX_ASSEMBLY_OK]; + stats->MACMergeFragCountRx =3D c[SCNT_RX_MERGE_FRAG]; + stats->MACMergeFragCountTx =3D c[SCNT_TX_MERGE_FRAG]; + stats->MACMergeHoldCount =3D c[SCNT_TX_MM_HOLD]; + + spin_unlock(&s->sw_lock); +} + +static void lan9645x_check_stats_work(struct work_struct *work) +{ + struct delayed_work *del_work =3D to_delayed_work(work); + struct lan9645x_stats *stats; + + stats =3D container_of(del_work, struct lan9645x_stats, work); + + lan9645x_stats_update(stats->lan9645x); + + queue_delayed_work(stats->queue, &stats->work, + LAN9645X_STATS_CHECK_DELAY); +} + +static int lan9645x_stats_prepare_regions(struct lan9645x *lan9645x, + struct lan9645x_view_stats *vstat) +{ + struct lan9645x_stat_region *regions; + const u32 *layout =3D vstat->layout; + size_t num_regions =3D 1; + int i; + + for (i =3D 1; i < vstat->num_cnts; i++) + if (layout[i] !=3D layout[i - 1] + 1) + num_regions++; + + regions =3D devm_kcalloc(lan9645x->dev, num_regions, sizeof(*regions), + GFP_KERNEL); + if (!regions) + return -ENOMEM; + + vstat->num_regions =3D num_regions; + vstat->regions =3D regions; + + regions[0].base_offset =3D layout[0]; + regions[0].cnts_base_idx =3D 0; + regions[0].cnt =3D 1; + + for (i =3D 1, num_regions =3D 0; i < vstat->num_cnts; i++) { + if (layout[i] !=3D layout[i - 1] + 1) { + num_regions++; + regions[num_regions].base_offset =3D layout[i]; + regions[num_regions].cnts_base_idx =3D i; + regions[num_regions].cnt =3D 1; + } else { + regions[num_regions].cnt++; + } + } + + return 0; +} + +static int lan9645x_view_stat_init(struct lan9645x *lan9645x, + struct lan9645x_view_stats *vstat, + const struct lan9645x_view_stats *cfg) +{ + size_t total =3D cfg->num_cnts * cfg->num_indexes; + int err; + + memcpy(vstat, cfg, sizeof(*cfg)); + + vstat->cnts =3D devm_kcalloc(lan9645x->dev, total, sizeof(u64), + GFP_KERNEL); + if (!vstat->cnts) + return -ENOMEM; + + vstat->buf =3D devm_kcalloc(lan9645x->dev, total, sizeof(u32), + GFP_KERNEL); + if (!vstat->buf) + return -ENOMEM; + + err =3D lan9645x_stats_prepare_regions(lan9645x, vstat); + if (err) + return err; + + vstat->stats =3D lan9645x->stats; + + return 0; +} + +int lan9645x_stats_init(struct lan9645x *lan9645x) +{ + const struct lan9645x_view_stats *vs; + struct lan9645x_stats *stats; + int err, i; + + lan9645x->stats =3D devm_kzalloc(lan9645x->dev, sizeof(*stats), + GFP_KERNEL); + if (!lan9645x->stats) + return -ENOMEM; + + stats =3D lan9645x->stats; + stats->lan9645x =3D lan9645x; + + mutex_init(&stats->hw_lock); + spin_lock_init(&stats->sw_lock); + + for (i =3D 0; i < ARRAY_SIZE(lan9645x_view_stat_cfgs); i++) { + vs =3D &lan9645x_view_stat_cfgs[i]; + + if (!vs->num_cnts) + continue; + + err =3D lan9645x_view_stat_init(lan9645x, &stats->view[vs->type], + vs); + if (err) + return err; + } + + stats->queue =3D alloc_ordered_workqueue("%s-stats", 0, + dev_name(lan9645x->dev)); + if (!stats->queue) + return -ENOMEM; + + INIT_DELAYED_WORK(&stats->work, lan9645x_check_stats_work); + queue_delayed_work(stats->queue, &stats->work, + LAN9645X_STATS_CHECK_DELAY); + + return 0; +} + +void lan9645x_stats_deinit(struct lan9645x *lan9645x) +{ + cancel_delayed_work_sync(&lan9645x->stats->work); + destroy_workqueue(lan9645x->stats->queue); + mutex_destroy(&lan9645x->stats->hw_lock); + lan9645x->stats->queue =3D NULL; +} diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h b/drivers/= net/dsa/microchip/lan9645x/lan9645x_stats.h new file mode 100644 index 000000000000..268f6ad18088 --- /dev/null +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2026 Microchip Technology Inc. + */ + +#ifndef _LAN9645X_STATS_H_ +#define _LAN9645X_STATS_H_ + +#include "lan9645x_main.h" + +#define STATS_INDEX(vstats, idx) (&(vstats)->cnts[(vstats)->num_cnts * (id= x)]) + +#define STAT_COUNTERS(lan9645x, type, idx) \ + STATS_INDEX(lan9645x_get_vstats(lan9645x, type), idx) + +/* Counter indices into stat layout structs */ +#define SCNT_FRER_SID_IN_PKT 0 +#define SCNT_ISDX_GREEN_OCT 1 +#define SCNT_ISDX_GREEN_PKT 2 +#define SCNT_ISDX_YELLOW_OCT 3 +#define SCNT_ISDX_YELLOW_PKT 4 +#define SCNT_ISDX_RED_OCT 5 +#define SCNT_ISDX_RED_PKT 6 +#define SCNT_ISDX_DROP_GREEN_OCT 7 +#define SCNT_ISDX_DROP_GREEN_PKT 8 +#define SCNT_ISDX_DROP_YELLOW_OCT 9 +#define SCNT_ISDX_DROP_YELLOW_PKT 10 + +#define SCNT_SF_MATCHING_FRAMES_COUNT 0 +#define SCNT_SF_NOT_PASSING_FRAMES_COUNT 1 +#define SCNT_SF_NOT_PASSING_SDU_COUNT 2 +#define SCNT_SF_RED_FRAMES_COUNT 3 +#define SCNT_SF_STREAM_BLOCK_COUNT 4 + +#define SCNT_ESDX_GREEN_OCT 0 +#define SCNT_ESDX_GREEN_PKT 1 +#define SCNT_ESDX_YELLOW_OCT 2 +#define SCNT_ESDX_YELLOW_PKT 3 + +#define SCNT_RX_OCT 0 +#define SCNT_RX_UC 1 +#define SCNT_RX_MC 2 +#define SCNT_RX_BC 3 +#define SCNT_RX_SHORT 4 +#define SCNT_RX_FRAG 5 +#define SCNT_RX_JABBER 6 +#define SCNT_RX_CRC 7 +#define SCNT_RX_SYMBOL_ERR 8 +#define SCNT_RX_SZ_64 9 +#define SCNT_RX_SZ_65_127 10 +#define SCNT_RX_SZ_128_255 11 +#define SCNT_RX_SZ_256_511 12 +#define SCNT_RX_SZ_512_1023 13 +#define SCNT_RX_SZ_1024_1526 14 +#define SCNT_RX_SZ_JUMBO 15 +#define SCNT_RX_PAUSE 16 +#define SCNT_RX_CONTROL 17 +#define SCNT_RX_LONG 18 +#define SCNT_RX_CAT_DROP 19 +#define SCNT_RX_RED_PRIO_0 20 +#define SCNT_RX_RED_PRIO_1 21 +#define SCNT_RX_RED_PRIO_2 22 +#define SCNT_RX_RED_PRIO_3 23 +#define SCNT_RX_RED_PRIO_4 24 +#define SCNT_RX_RED_PRIO_5 25 +#define SCNT_RX_RED_PRIO_6 26 +#define SCNT_RX_RED_PRIO_7 27 +#define SCNT_RX_YELLOW_PRIO_0 28 +#define SCNT_RX_YELLOW_PRIO_1 29 +#define SCNT_RX_YELLOW_PRIO_2 30 +#define SCNT_RX_YELLOW_PRIO_3 31 +#define SCNT_RX_YELLOW_PRIO_4 32 +#define SCNT_RX_YELLOW_PRIO_5 33 +#define SCNT_RX_YELLOW_PRIO_6 34 +#define SCNT_RX_YELLOW_PRIO_7 35 +#define SCNT_RX_GREEN_PRIO_0 36 +#define SCNT_RX_GREEN_PRIO_1 37 +#define SCNT_RX_GREEN_PRIO_2 38 +#define SCNT_RX_GREEN_PRIO_3 39 +#define SCNT_RX_GREEN_PRIO_4 40 +#define SCNT_RX_GREEN_PRIO_5 41 +#define SCNT_RX_GREEN_PRIO_6 42 +#define SCNT_RX_GREEN_PRIO_7 43 +#define SCNT_RX_ASSEMBLY_ERR 44 +#define SCNT_RX_SMD_ERR 45 +#define SCNT_RX_ASSEMBLY_OK 46 +#define SCNT_RX_MERGE_FRAG 47 +#define SCNT_RX_PMAC_OCT 48 +#define SCNT_RX_PMAC_UC 49 +#define SCNT_RX_PMAC_MC 50 +#define SCNT_RX_PMAC_BC 51 +#define SCNT_RX_PMAC_SHORT 52 +#define SCNT_RX_PMAC_FRAG 53 +#define SCNT_RX_PMAC_JABBER 54 +#define SCNT_RX_PMAC_CRC 55 +#define SCNT_RX_PMAC_SYMBOL_ERR 56 +#define SCNT_RX_PMAC_SZ_64 57 +#define SCNT_RX_PMAC_SZ_65_127 58 +#define SCNT_RX_PMAC_SZ_128_255 59 +#define SCNT_RX_PMAC_SZ_256_511 60 +#define SCNT_RX_PMAC_SZ_512_1023 61 +#define SCNT_RX_PMAC_SZ_1024_1526 62 +#define SCNT_RX_PMAC_SZ_JUMBO 63 +#define SCNT_RX_PMAC_PAUSE 64 +#define SCNT_RX_PMAC_CONTROL 65 +#define SCNT_RX_PMAC_LONG 66 +#define SCNT_TX_OCT 67 +#define SCNT_TX_UC 68 +#define SCNT_TX_MC 69 +#define SCNT_TX_BC 70 +#define SCNT_TX_COL 71 +#define SCNT_TX_DROP 72 +#define SCNT_TX_PAUSE 73 +#define SCNT_TX_SZ_64 74 +#define SCNT_TX_SZ_65_127 75 +#define SCNT_TX_SZ_128_255 76 +#define SCNT_TX_SZ_256_511 77 +#define SCNT_TX_SZ_512_1023 78 +#define SCNT_TX_SZ_1024_1526 79 +#define SCNT_TX_SZ_JUMBO 80 +#define SCNT_TX_YELLOW_PRIO_0 81 +#define SCNT_TX_YELLOW_PRIO_1 82 +#define SCNT_TX_YELLOW_PRIO_2 83 +#define SCNT_TX_YELLOW_PRIO_3 84 +#define SCNT_TX_YELLOW_PRIO_4 85 +#define SCNT_TX_YELLOW_PRIO_5 86 +#define SCNT_TX_YELLOW_PRIO_6 87 +#define SCNT_TX_YELLOW_PRIO_7 88 +#define SCNT_TX_GREEN_PRIO_0 89 +#define SCNT_TX_GREEN_PRIO_1 90 +#define SCNT_TX_GREEN_PRIO_2 91 +#define SCNT_TX_GREEN_PRIO_3 92 +#define SCNT_TX_GREEN_PRIO_4 93 +#define SCNT_TX_GREEN_PRIO_5 94 +#define SCNT_TX_GREEN_PRIO_6 95 +#define SCNT_TX_GREEN_PRIO_7 96 +#define SCNT_TX_AGED 97 +#define SCNT_TX_LLCT 98 +#define SCNT_TX_CT 99 +#define SCNT_TX_BUFDROP 100 +#define SCNT_TX_MM_HOLD 101 +#define SCNT_TX_MERGE_FRAG 102 +#define SCNT_TX_PMAC_OCT 103 +#define SCNT_TX_PMAC_UC 104 +#define SCNT_TX_PMAC_MC 105 +#define SCNT_TX_PMAC_BC 106 +#define SCNT_TX_PMAC_PAUSE 107 +#define SCNT_TX_PMAC_SZ_64 108 +#define SCNT_TX_PMAC_SZ_65_127 109 +#define SCNT_TX_PMAC_SZ_128_255 110 +#define SCNT_TX_PMAC_SZ_256_511 111 +#define SCNT_TX_PMAC_SZ_512_1023 112 +#define SCNT_TX_PMAC_SZ_1024_1526 113 +#define SCNT_TX_PMAC_SZ_JUMBO 114 +#define SCNT_DR_LOCAL 115 +#define SCNT_DR_TAIL 116 +#define SCNT_DR_YELLOW_PRIO_0 117 +#define SCNT_DR_YELLOW_PRIO_1 118 +#define SCNT_DR_YELLOW_PRIO_2 119 +#define SCNT_DR_YELLOW_PRIO_3 120 +#define SCNT_DR_YELLOW_PRIO_4 121 +#define SCNT_DR_YELLOW_PRIO_5 122 +#define SCNT_DR_YELLOW_PRIO_6 123 +#define SCNT_DR_YELLOW_PRIO_7 124 +#define SCNT_DR_GREEN_PRIO_0 125 +#define SCNT_DR_GREEN_PRIO_1 126 +#define SCNT_DR_GREEN_PRIO_2 127 +#define SCNT_DR_GREEN_PRIO_3 128 +#define SCNT_DR_GREEN_PRIO_4 129 +#define SCNT_DR_GREEN_PRIO_5 130 +#define SCNT_DR_GREEN_PRIO_6 131 +#define SCNT_DR_GREEN_PRIO_7 132 + +enum lan9645x_view_stat_type { + LAN9645X_STAT_PORTS =3D 0, + LAN9645X_STAT_ISDX, + LAN9645X_STAT_ESDX, + LAN9645X_STAT_SFID, + + LAN9645X_STAT_NUM, +}; + +struct lan9645x_stat_region { + u32 base_offset; + u32 cnt; + u32 cnts_base_idx; +}; + +/* Counters are organized by indices/views such as + * + * - physical ports + * - isdx + * - esdx + * - frer + * - sfid + * + * Each view contains regions, which is a linear address range of related + * stats. I.e. the ports index has RX, TX and Drop regions. + * + * + * and you have a given counter replicated per index. + */ +struct lan9645x_view_stats { + /* HW register offsets indexed by SCNT_*, used for bulk reading */ + const u32 *layout; + /* Region description for this view, used for bulk reading */ + struct lan9645x_stat_region *regions; + struct lan9645x_stats *stats; + char name[16]; + /* 64bit software counters with the same addr layout hw */ + u64 *cnts; + /* Buffer for bulk reading counter regions from hw */ + u32 *buf; + /* Number of counters per index in view */ + u32 num_cnts; + /* Number of indexes in view */ + u32 num_indexes; + /* Number of counter regions with counters at sequential addresses */ + size_t num_regions; + enum lan9645x_view_stat_type type; +}; + +struct lan9645x_stats { + struct lan9645x *lan9645x; + struct mutex hw_lock; /* lock r/w to stat registers and u32 buf */ + spinlock_t sw_lock; /* lock access to u64 software counters */ + struct delayed_work work; + struct workqueue_struct *queue; + + struct lan9645x_view_stats view[LAN9645X_STAT_NUM]; +}; + +static inline struct lan9645x_view_stats * +lan9645x_get_vstats(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type type) +{ + if (WARN_ON(!(type < LAN9645X_STAT_NUM))) + return NULL; + + return &lan9645x->stats->view[type]; +} + +/* Add a possibly wrapping 32 bit value to a 64 bit counter */ +static inline void lan9645x_stats_add_cnt(u64 *cnt, u32 val) +{ + if (val < (*cnt & U32_MAX)) + *cnt +=3D (u64)1 << 32; /* value has wrapped */ + + *cnt =3D (*cnt & ~(u64)U32_MAX) + val; +} + +int lan9645x_stats_init(struct lan9645x *lan9645x); +void lan9645x_stats_deinit(struct lan9645x *lan9645x); +void lan9645x_stats_get_strings(struct lan9645x *lan9645x, int port, + u32 stringset, u8 *data); +int lan9645x_stats_get_sset_count(struct lan9645x *lan9645x, int port, + int sset); +void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port, + uint64_t *data); +void lan9645x_stats_get_eth_mac_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_mac_stats *mac_stats); +void +lan9645x_stats_get_rmon_stats(struct lan9645x *lan9645x, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges); +void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port, + struct rtnl_link_stats64 *s); +void lan9645x_stats_get_mm_stats(struct lan9645x *lan9645x, int port, + struct ethtool_mm_stats *stats); +void lan9645x_stats_get_pause_stats(struct lan9645x *lan9645x, int port, + struct ethtool_pause_stats *ps); +void +lan9645x_stats_get_eth_ctrl_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats); +void lan9645x_stats_get_eth_phy_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_phy_stats *phy_stats); + +#endif --=20 2.52.0